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というパッケージに含まれるアクションです。
もうひとつはやや複雑な例で、同じくメッセージを受け取り表示するものですが、合わせて投稿者など複数の入力フィールドがあります。 入力情報を一個のモデルクラスとして作成したものです。 sample.action.complexというパッケージに含まれるアクションです。
アクションマッピングとは次の三つのものを関連付けることです。
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
いくつか注意点とヒントです。
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.properties | WEB-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.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です。
実際にはこれをnative2asciiで「\u306E」のようなUnicodeエスケープを使う形式に変換しなければなりません。 あまり使いやすくはありませんが、Eclipseのプロパティエディタでは直接日本語が入力できます。 かな漢字変換を確定するとコード表記に変わります。 テキストエディタで書いたものをEclipseプロパティエディタにペーストする方法もあります。
Javaのソースコード内からはgetText()でキーを指定して現在使用中の言語の文字列が取り出せます。
public String execute()
{
message = "'" + message + "' " + getText("is_what_you_typed");
}
表示結果は、
英語の場合
日本語の場合
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
ブラウザの言語設定が英語の場合と日本語の場合の表示例です。
英語の場合
日本語の場合
この例にあるように、ブラウザの設定によらず、使用言語を強制することもできます。
<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メソッドを置くこともできます。
私は上記三つの検証方法をフォーム値の置き方の異なる次の三つのケースでそれぞれ試してみました。
ModelDriven.getModel
でStrutsに渡してしまう(sample.action.complex.ModelBased.javaとsample.model.Message.java)
このうち、簡略化のため、Simple.java用の検証コードは省きましたが、残りの二つはそのまま残してあります。
次の表示例は、何もタイプしないで送信ボタンを押した状態の、object-backed-input.jspです。 最初の三つのフィールドに対して検証エラーが起こった状態です。
英語の場合
日本語の場合
テスト結果は、ひとつの例外を除いてすべて期待どおり動作しました。 その例外とは、最後のgetModelを用いたケースで、モデルクラス側にアノテーションを記述した場合です。
典型的な検証指定の例を示します。
アクションクラスに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(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
このサンプルのプロジェクトファイルにはStruts2など必要なJARは入れておりません。
サンプルのソースファイルをごらんになる場合には不要ですが、ご自分でコンパイルや実行をお試しになる場合には、プロジェクトに必要なJARを追加してください。
私の場合は、
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>
Struts2の設定ファイルのstruts.xml
をWEB-INF/classes
に置きました。
アクションマッピングを書く必要がないため次のように非常に簡単です。
<struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="global" /> </struts>
デバッグモードの指定と、先に述べたglobal(_xx).propertiesの指定です。
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を使用しました。
このサイトで提供しているほかのローカライズサンプルです。
横浜工文社では、世界で使えるソフトウェアやドキュメントの制作で実績があります。
何なりとご相談ください。
Copyright © 2012 Kobu.Com. All rights reserved.
提供: 横浜工文社
制作: 荒井文吉
執筆: 2012/05/29
公開するサンプルは試作品で、完全なものではありません。
この解説書とサンプルの転載はご遠慮ください。
リンク張りやお問合せやコメントを歓迎いたします。