ホーム > i18n / struts2 | English



../../images/kobu-big.gif

../../images/phone.gif



Struts2アプリケーション
ローカライズサンプル



Struts2アプリケーションの国際化(I18N - Internationalization)と現地化(L10N - Localization)の実例を公開します。

ダウンロード: Struts2アプリケーションのEclipseプロジェクト → struts2-i18n.zip

デモ: このサンプルはこちらで動作しています → http://www.ssjava.net/struts2-i18n/(期間限定)

このStruts2サンプルのみどころです。

以下では、サンプルの概要に続き、上記の各項目についての解説、最後にファイル構成について述べます。

はじめに

Apache Strutsは2000年にApache Foundationに寄付されたWebアプリフレームワークです。 2007年に登場したバージョン2(Struts2)は旧版(Struts1)とは大きく変わりました。 オフィシャルサイトに「Apache Struts 2 was originally known as WebWork 2.」と明記してあるとおり、名前は同じでも、コア部分が別の製品になりました。

このサンプルアプリ作成の当初の目的は:

この過程で複数の方法による入力値の検証機構(validation)のテストも行えました。

結論は、大半のことがむずかしい設定もなしに期待通り動作しました。 多少の違和感を感じていたStruts1に対して、Struts2には好感を持ちました。

このサンプルは下記のチュートリアルサンプルを元に作成しました。

Basic Struts 2 Application That Uses Ant and Struts 2 Version 2.3.1.2
(これの2012/01時点のもの)

主に下記のサイトの情報を参考にしました。

サンプルの紹介

このサンプルでは大きく二つのアクションを作ってみました。 ひとつは単純な例で、メッセージ(単一の文字列)を受け取り、それに別の文字列を追加して表示します。 sample.action.simpleというパッケージに含まれるアクションです。

images/simple-entry-ja.png

images/simple-view-ja.png

もうひとつはやや複雑な例で、同じくメッセージを受け取り表示するものですが、合わせて投稿者など複数の入力フィールドがあります。 入力情報を一個のモデルクラスとして作成したものです。 sample.action.complexというパッケージに含まれるアクションです。

images/complex-entry-ja.png

アクションマッピング

アクションマッピングとは次の三つのものを関連付けることです。

Strutsではアクションマッピングの指定方法が三つあり、これらを組み合わせて使えます。

アノテーションやコンベンションを使うためには追加のプラグインJARが必要です(struts2-convention-plugin-x.x.x.jar)。

コンベンション

このサンプルでは、アクションマッピングの方法として、三番目のコンベンションのみを使ってみました。 アクションマッピングを行うためにstruts.xmlやアノテーションは一切つかっていません。

おおざっぱに言うと、コンベンション式のアクションマッピングでは次のようにしてアクションのURLが決まります。

コンベンションマッピングの決まりごとについての詳細は次をごらんください。

Config Browser Pluginを使うと、現在のマッピングの様子がわかります → このサンプルのマッピングを見る

コンベンションマッピングの例

このサンプルでは次の三つのパターンのコンベンションマッピングを試してみました。

直接表示ページが参照される場合(アクションなし)

  http://www.example.com/struts-i18n/index -> /index.jsp
                                    /hello -> /hello.jsp
                                    /hello-in-free-marker -> /hello-in-free-marker.ftl
                                    /hello-in-velocity -> /hello-in-velocity.vm

アクションを経由して結果ページが表示される場合

  /simple/simple -> sample.action.simple.Simple.class -> /simple/simple.jsp

  /simple/simple -> sample.action.simple.Simple.class -> /simple/simple.jsp

  /simple/simple-in-free-marker -> sample.action.simple.SimpleInFreeMarker.class
    -> /simple/simple-in-free-marker.jsp

  /simple/simple-in-free-marker -> sample.action.simple.SimpleInVelocity.class
    -> /simple/simple-in-velocity.jsp

検証により入力フォームに戻る場合があるケース

  /complex/object-backed-input.jsp
    --> sample.action.complex.ObjectBacked.class
         <--
  /complex/object-backed-input.jsp
    --> sample.action.complex.ObjectBacked.class
         --> /complex/object-backed-sucess.jsp

  /complex/object-backed-input.jsp
    --> sample.action.complex.ModelBased.class
         <--
  /complex/object-backed-input.jsp
    --> sample.action.complex.ModelBased.class
         --> /complex/object-backed-sucess.jsp

いくつか注意点とヒントです。

URLの末尾の「.action」は省略できる
/hello.actionでも/helloでもよい。
アクションクラスはなくてもよい
/hello.actionのURLに対しては、sample.action.Helloまたはsample.action.HelloActionがあればそのアクションが実行される。これがなくても、/WEB-INF/contentにhello.jspがあればそれが表示される。hello-success.jspを置いてみましたが、これは見つかりませんでした。
アクションクラス名の末尾の「Action」は省略できる
/hello.actionに対しては「HelloAction」という名前のクラスが選択される。しかし、Actionインターフェースを実装(ActionSupportを継承すればそうなる)すれば「Hello」だけでもよい。
表示ページ名の末尾の「success」は省略できる
アクションクラス(たとえばHelloAction.java)から「success」を返した場合、hello-success.jspがあればそのページが、なくても、hello.jspがあればそれが表示されます。

テンプレート

Struts2では表示ページとして、JSPのほか、FreeMarkerとVelocityテンプレートが使えます。 同じアプリケーションの中でこれらを混在させるともできます。 FreeMarkerは、Struts2自身が内部でStrutsタグをHTMLコードへ変換するのに使用しているため、特に設定なしで使えます。 Velocityは必要なJARを追加すれば使えます(後述)。

コンベンション使用のアクションマッピングの場合、決定した結果ページの名前を持つ、一定の拡張子を持つ表示ページあるいはテンプレートファイルが選択されます。

デフォルトのままの設定で、同じ結果ページ名を持つ複数種類の表示ページを置いてみたところ、Velocity、JSP、FreeMarkerの順に見つかりました。

  /hello.jsp + /hello.ftl + /hello.vm
  /hello.jsp +              /hello.vm
               /hello.ftl + /hello.vm
    /hello.vm takes precedence in all three cases

  /hello.jsp + /hello.ftl
    /hello.jsp selected

  if there is only a sigle page having /hello.???,
    that type is selected

文字列リソースの参照レベル

続いて、本題の国際化(Internationalization = I18N)および現地化(Localzation = L10N)についてです。

Javaには、言語ごとに文字列リソースをプロパティファイルに記述して集めたリソースバンドルと、実行時にロケール設定に従い使用言語に応じた文字列リソースを自動選択するしくみが、元から備わっています。 Struts2アプリケーションからも通常のJavaアプリと同じ方法でこれが利用できます。 表示ページからはStrutsタグを用いて文字列リソースが参照できます。 StrutsタグはJSPだけでなく、FreeMarkerやVelocityのテンプレートからも利用できます。

Struts2では複数の階層で文字列リソース(Javaのプロパティファイル)を用意することができます。 参照の優先順に主要なレベルを並べると、

リソース置き場所参照可能範囲
action-class.propertiesやaction-class_xx.propertiesクラスが置かれたパッケージ内特定のクラスからのみ
package.propertiesやpackage_xx.propertiesパッケージ内該当パッケージの全クラス
global.propertiesおよびglobal_xx.propertiesWEB-INF/classesアプリ全体

このうちglobal(_xx).propertiesを定義する場合にはstruts.xmlに下記の指定が必要です。

  <constant name="struts.custom.i18n.resources" value="global" />

文字列リソースの参照レベルの詳細は下記をごらんください。

Apache Struts 2 Documentation - Localization

実際に試してみると、確かに適切なレベルのリソースファイルから文字列が取られていることが確認できました。 action.sample.Simpleアクションの場合であれば、アクションクラスのコードの中や、結果表示ページで、文字列を参照すると、まずSimple.properties(またはSimple_xx.properties)、続いてaction.sample.package.properties(あるいはpackage_xx.properties)、そして最後にglobal.properties(あるいはglobal_xx.properties)の順に指定のキーを持つ文字列が検索されていることがわかりました。

しかし問題は入力ページです。 いったんアクションクラスが呼ばれて、入力項目にエラーがあって検証動作の結果、入力ページが呼ばれた場合は上記の参照レベルに従った文字列抽出が行われます。 しかし、一番最初に入力ページを呼び出す場合には、まだそのページはアクションクラスとの対応関係がないので、global(_xx).propertiesにあるものしか参照できませんでした。 結果、大半のメッセージはglobal(_xx).propertiesに置くしかない、という状況に陥りました。 うまい方法があればよいと思いますがよくわかりません。

文字列リソースの参照例

アプリケーションの各所から現在使用中の言語に応じたリソースファイル内の文字列を取り出す方法をまとめます。

global_xx.properties

global.propertiesを例に取ります。
ここに指定する文字列はデフォルトで、ユーザのリクエストで指定された使用言語に該当するglobal_xx.propertiesが用意されていない場合に使われます。 このサンプルでは英語の文字列をここに記述しています。

  title=I18N Sample with Struts2
  message=Message
  submit=Submit
  is_what_you_typed=is what you typed.
  back=Go back

日本語の文字列リソースの名前はglobal_ja.propertiesです。

images/global-ja-properties.png

実際にはこれをnative2asciiで「\u306E」のようなUnicodeエスケープを使う形式に変換しなければなりません。 あまり使いやすくはありませんが、Eclipseのプロパティエディタでは直接日本語が入力できます。 かな漢字変換を確定するとコード表記に変わります。 テキストエディタで書いたものをEclipseプロパティエディタにペーストする方法もあります。

getText

Javaのソースコード内からはgetText()でキーを指定して現在使用中の言語の文字列が取り出せます。

  public String execute()
  {
    message = "'" + message + "' " + getText("is_what_you_typed");
  }

表示結果は、

英語の場合

images/simple-jsp-en.png

日本語の場合

images/simple-jsp-ja.png

Strutsタグ

JSPページからはStrutsタグでこれら文字列を参照します。

  <h2><s:text name="title"/></h2>
  <s:form action="simple" method="post">
    <s:textfield key="message" /> 
    <s:submit key="submit" />
  </s:form>

FreeMarkerとVelocityの場合はStrutsタグの文法が異なります。

FreeMarker

  <h2><@s.text name="title"/></h2>
  <@s.form action="simple" method="post">
    <@s.textfield key="message" /> 
    <@s.submit key="submit" />
  </@s.form>

Velociy

  <h2>#stext ("name=title")♂</h2>
  #sform ("action=simple method=post")
    #stextfield ("key=message")
    #ssubmit ("key=submit")
  #end

ブラウザの言語設定が英語の場合と日本語の場合の表示例です。

英語の場合

images/index-jsp-en.png

日本語の場合

images/index-jsp-ja.png

この例にあるように、ブラウザの設定によらず、使用言語を強制することもできます。

        <a href="<s:url action='/index.action?request_locale=en'/>">English</a><br/>
        <a href="<s:url action='/index.action?request_locale=ja'/>">Japanese</a>

入力値の検証

入力値の検査とローカライズには深い関係があります。 エラーがあったとき、入力値のラベルやエラーメッセージを表示する必要があるからです。

Struts2のフォームへの入力値の検証(validation)は三つの方法で行え、これらを同時に使えます。

Struts2では、フォームの入力値を受け取るsetメソッドや、結果の値を表示ページに渡すgetメソッドを、アクションクラスそのものに置くことができます。 また、別にモデルクラスを定義してそこにget/setメソッドを置くこともできます。

私は上記三つの検証方法をフォーム値の置き方の異なる次の三つのケースでそれぞれ試してみました。

このうち、簡略化のため、Simple.java用の検証コードは省きましたが、残りの二つはそのまま残してあります。

次の表示例は、何もタイプしないで送信ボタンを押した状態の、object-backed-input.jspです。 最初の三つのフィールドに対して検証エラーが起こった状態です。

英語の場合

images/complex-input-en.png

日本語の場合

images/complex-input-ja.png

テスト結果は、ひとつの例外を除いてすべて期待どおり動作しました。 その例外とは、最後のgetModelを用いたケースで、モデルクラス側にアノテーションを記述した場合です。

検証指定の例

典型的な検証指定の例を示します。

Actionクラスのvalidateメソッド

アクションクラスにvalidateメソッドを書いておくと、Struts2がexecuteメソッドの前に呼び出してくれます。 ここで、データをチェックし、エラーがあった場合、addFieldError()を呼び出すと、あたかもアクションクラスから結果「input」を返したのと同じ扱いをしてくれます。 すると、該当の入力ページが表示され、そこにエラーメッセージも追加されます。

  public void validate()
  {
    String submitter = message.getSubmitter();
    if (submitter == null ||  submitter.length() == 0)
    {
      addFieldError("message.submitter", getText("message_submitter_missing"));
    }
  }

ここでは、message.submitterがフィールドラベル、message_submitter_missingがエラーメッセージで、それぞれ該当の文字列リソースから取り出して表示されます。

検証用XMLファイル

アクションクラスに対応するXML(ActionClassName-validation.xml)を用意し、検証内容を記述します。

: ObjectBacked-validation.xml(ObjectBackedアクションクラスの検証用XML)

  <validators>
     <validator type="requiredstring">
       <param name="fieldname">message.text</param><!-- validates 'text' -->
       <message key="message_text_missing"/>
     </validator>
  </validators>

message.textがフィールドラベルのリソースキー、message_text_missingがエラー文字列のキーです。

アノテーション

フィールドのsetメソッドの直前に検証内容を記述できます。

  private String message;

  @RequiredStringValidator(key = "message_missing")
  public void setMessage(String message)
  {
    this.message = message;
  }

以上が検証についての説明でした。

ファイル構成

サンプルアプリケーションを構成するファイルの一覧です。

+ src
  + sample
    + action
      + simple
        package.properties
        package_ja.properties
        Simple.java
        SimpleInFreeMarker.java
        SimpleInVelocity.java
      + complex
        package.properties
        package_ja.properties
        ObjectBacked.java
        ObjectBacked-validation.xml
        ObjectBacked.properties
        ObjectBacked_ja.properties
        ModelBased.class
        ModelBased-validation.xml
      + model
        Message.java
        Message.properties
        Message_ja.properties

+ WebContent
  + WEB-INF
    web.xml
    + content
      index.jsp
      hello.jsp
      hello-in-free-marker.ftl
      hello-in-velocity.vm
      + simple
        simple.jsp
        simple-in-free-marker.ftl
        simple-in-velocity.vm
      + complex
        object-backed-input.jsp
        object-backed-success.jsp
        model-based-input.jsp
        model-based-success.jsp
    + classes
      global.properties
      global_ja.properties
      struts.xml
    + lib
      asm-3.3.jar
      asm-commons-3.3.jar
      asm-tree-3.3.jar
      commons-collections-3.2.jar
      commons-fileupload-1.2.2.jar
      commons-io-2.0.1.jar
      commons-lang-2.6.jar
      commons-lang3-3.1.jar
      freemarker-2.3.19.jar
      javassist-3.11.0.GA.jar
      ognl-3.0.5.jar
      struts2-config-browser-plugin-2.3.3.jar
      struts2-convention-plugin-2.3.3.jar
      struts2-core-2.3.3.jar
      velocity-1.7.jar
      velocity-tools-view-2.0.jar
      xwork-core-2.3.3.jar

使用JAR

このサンプルのプロジェクトファイルにはStruts2など必要なJARは入れておりません。
サンプルのソースファイルをごらんになる場合には不要ですが、ご自分でコンパイルや実行をお試しになる場合には、プロジェクトに必要なJARを追加してください。

私の場合は、

web.xml

Struts2を有効にするにはweb.xmlに下記を追加するだけです。 これにより、すべてのリクエストがStruts2のフィルタへ渡っていきます。

  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

struts.xml

Struts2の設定ファイルのstruts.xmlWEB-INF/classesに置きました。 アクションマッピングを書く必要がないため次のように非常に簡単です。

  <struts>
    <constant name="struts.devMode" value="true" />
    <constant name="struts.custom.i18n.resources" value="global" />
  </struts>

デバッグモードの指定と、先に述べたglobal(_xx).propertiesの指定です。

Tomcatの使用バージョン

Struts2のドキュメントによると、Struts2はServlet API 2.4以上、JSP 2.0以上、Java 5以上が必要です。 このため、Tomcatは5.5.x以上であれば使えます。 私は事情があってTomcat 5.5.23を使用しました。 6.xや7.xでは試しておりません。 Javaは6.0を使用しました。

お知らせ

このサイトで提供しているほかのローカライズサンプルです。

CakePHP 2.xローカライズサンプル
最新のCakePHP 2.xで作るWebアプリで、ローカライズ、命名規則(コンベンション)、Bake自動生成ツール、認証コンポーネントをテストを試しました。
Androidローカライズサンプル
日本語・英語を自動切換え可能なAndroidアプリケーションサンプル。

横浜工文社では、世界で使えるソフトウェアやドキュメントの制作で実績があります。

何なりとご相談ください。


Copyright © 2012 Kobu.Com. All rights reserved.

提供: 横浜工文社
制作: 荒井文吉
執筆: 2012/05/29

公開するサンプルは試作品で、完全なものではありません。
この解説書とサンプルの転載はご遠慮ください。
リンク張りやお問合せやコメントを歓迎いたします。