2011年2月21日月曜日

Struts 2のバリデーションエラー・メッセージ&国際化への対応

simpleテーマのバリデーションエラー・メッセージ

 テーマをsimpleにした場合は、バリデーションエラーのメッセージは自動では表示されませんので、JSPファイルに<s:fielderror>タグを記述する必要があります。単に<s:fielderror />と記述すると、すべてのメッセージがリスト表示されます。個別にメッセージを取得したい場合は、
<s:fielderror><s:param value="%{'userName'}" /></s:fielderror>

 という具合に、paramタグで個別にメッセージを指定します。

自動生成されるエラーメッセージ

 バリデーションは、すべてエラーメッセージは任意に設定できました。ところが、Struts 2のエラーメッセージの中には、デフォルトで自動的にその内容を生成されてしまうものがあります。例えば、メンバ変数の変換エラーの場合がそれにあたります。

 フォームに入力された値は、対応するActionクラスのメンバ変数に、自動的に型変換されて設定されます。正しく型変換できない場合、Struts 2でエラーを検出し、それを示すメッセージが自動で表示されます。変換エラーがStruts 2側で対応できるようになったこと自体は良いのですが、メッセージがデフォルトでは「Invalid field value for field 〜」という具合に英語表示されてしまうのです。

独自の表示用メッセージ定義

 アプリケーション独自のメッセージを表示するためには、Javaのプロパティファイルを用います。プロパティファイルとは、「キー = 値」というフォーマットで記述されたファイルのことで、Javaのアプリケーションから利用されます。通常ファイル名に「.properties」という拡張子を付与します。

 Struts 2で利用する場合は、Actionクラスと同じディレクトリ階層に「Actionクラス名.properties」という名前で配置します。

 フィールドの変換エラーのメッセージは、「invalid.fieldvalue.プロパティ名=メッセージ」というキーでメッセージを定義します。

下記はその具体例です。

ResearchWithValidation.properties

invalid.fieldvalue.age=年齢は数字で入力してください

 なお、プロパティファイルの値に日本語を使用する場合は、Unicodeエスケープ形式で記述する必要があります。文字コードWindows-31J(シフトJIS)でテキストファイルを作成し、JDK付属のnative2asciiコマンドなどを利用して、Unicodeエスケープに変換しましょう。Eclipse環境なら、PropertiesEditorプラグインが便利です。Unicodeエスケープの変換を意識することなく、通常のテキストファイルの感覚で扱うことができます。

プロパティファイルによるメッセージ管理

 実は、プロパティファイルで定義できるメッセージは、変換エラーのみに限ったことではありません。他のエラーメッセージや、通常のメッセージ、文字列をプロパティファイルで定義したものに置換することができます。プログラム中ではメッセージをキーで扱い、実際に表示するための文字列は外部ファイルに持つことができるわけです。

バリデーションのエラーメッセージ

 バリデーションのエラーメッセージを、プロパティファイルによる定義に変更するには、次のようにします。キーの名前は任意です。

Research-ResearchConfirm-validation.xmlを変更

<!-- 1.名前フィールドの必須入力チェック -->

<field name="name">

    <field-validator type="requiredstring">

        <message key="errors.required.name"/>

    </field-validator>

</field>

Research.properties

errors.required.name=名前を入力してください

 バリデーションをアノテーションで記述した場合でも、ActionSupportクラスで提供されているgetTextというメソッドを利用すれば、プロパティファイルからメッセージを取得できます。

Research.javaのアノテーションの一部

@Validations(

        requiredStrings = {

            @RequiredStringValidator(

                fieldName="name",

                message="${getText('errors.required.name')}" )

        }

)

プログラムでプロパティファイルを扱う

 また、getTextメソッドは、当然ながらアノテーションだけでなくプログラム中に記述できます。

getTextの例

    public void validate() {

        if ( name == null || name.length() == 0 ) {

            addActionError( getText("errors.required.name") );

        }

    }

JSPファイルでプロパティファイルを扱う

 JSPでのカスタムタグでも、プロパティファイルを参照できるものがあります。この場合のプロパティファイルは、JSPファイルを呼び出すActionクラスが管理するものを指します。

国際化文字列の表示タグ

<s:text name="some.key" />

 上記は、キーを指定してプロパティファイルの値をそのまま表示するタグです。

テキスト入力のラベル表示

<s:textfield key="some.key" name="textfieldName"/>

 こちらは、テキスト入力のラベルにプロパティファイルの値を使用します。

 以上のタグ以外でも、getTextメソッドを使えばラベル部分などにプロパティファイルの文字列が使えます。

getTextの例

<s:property value="getText('some.key')" />

国際化への対応

 上記のプロパティファイル参照方法は国際化(i18n)にも対応しています。JSPと同様に、接続ユーザーのロケール情報を利用して自動的にロケールに対応したプロパティファイルを読み込むことができます。

 Struts 2でも、プロパティファイルの命名規則は、「プロパティファイルのベース名_言語コード_国コード_地域コード.properties」となります。例えば前述のプロパティファイルで、英語であれば「ResearchWithValidation_en.properties」という名前にします。

プロパティファイルの参照順序

 先ほど、プロパティファイルを「Actionクラス名.properties」とすると書きましたが、他の名前にすることも可能です。また、別のディレクトリに配置することもできますので、必要に応じてプロパティファイルの適応範囲を広げることができます。

 また探索順が決まっているので、複数ファイルに重複したキーが存在していても問題ありません。参照の優先順位に従って値の取得が行われます。

 プロパティファイルの設定の仕方は、Struts 2のドキュメントに細かく記載されています。とはいえ、以下のケースだけ頭に入れておけば十分ではないかと思います。優先度を高い順に並べると次のようになります。

§           1. Actionクラス名.properties

そのActionクラス限定のメッセージを定義することになります。

§           2. Actionクラスの基底クラス名.properties

継承したActionクラスに共通のメッセージを定義することができます。

§           3. パッケージ内のpackage.properties

同一のパッケージにあるActionクラスで共通にできます。

§           4. グローバル(struts.xmlで指定)

アプリケーション全体で共通して1つのプロパティファイルを使用することになります。

 struts.xmlでの指定の場合、<struts>タグ配下に、<constant>を追加します。

struts.xmlでプロパティファイルを指定

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

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

 

    <package name="HelloProject" extends="struts-default">

 

    以下省略

 valueで指定するファイル名には、拡張子.propertiesの指定は不要です。また、カンマ区切りで複数記述することもできます。キーが重複した場合は、後から記述した方が有効となります。

Windows-31J(シフトJIS)の文字コードを扱うには

 Struts 2のデフォルトは、UTF-8の文字コードですので、表示する文字コードも同じようにUTF-8にしておけば問題はありません。
 ただ、Windows-31Jの文字コードで表示・入力したい場合もあるかと思います。そのようなケースでは、前述のプロパティファイルの対応に加えて、Struts 2の設定プロパティのstruts.i18n.encodingの値を変更します。「struts.xml」で変更する場合は、<constant>タグを用いて、struts.i18n.encodingの値を変更します。

struts.xmlでエンコード変更

<struts>

  <constant name="struts.i18n.encoding" value="Windows-31J"/>

プロパティファイルへの動的なメッセージ設定

 ここまでは、固定メッセージをプロパティファイルに設定してきました。しかし、Struts 2ではプレースホルダを利用して、動的にメッセージを設定することもできます。

 具体的には、前述のgetTextメソッドの引数に置換する文字列を指定し、動的にメッセージを参照します。例えば、プロパティファイルに次のような記述を行います。

プレースホルダを使用した例

fieldLabel.name=名前

fieldLabel.age=年齢

errors.required={0}を入力してください

 次のようにgetTextメソッドを記述すると、動的に置換されたメッセージを得ることができます。ちなみに、このサンプルは共通メッセージを統一して定義するという使い方の例にもなっています。

getTextの例

getText("errors.required", new String[]{ getText("fieldLabel.name") });

getText("errors.required", new String[]{ getText("fieldLabel.age") });

 また、getTextメソッドは次のようにプロパティファイル内にも記述できます。用途としては、グローバルなプロパティファイルを用いて共通の文字列を定義し、その参照をActionクラスごとのプロパティファイルで行うようなケースが考えられます。

プロパティファイル内のgetTextメソッドの例

errors.sample=${getText("AppName")}では、この機能は使用できません

XML定義ファイルでのgetText

 Struts 2では、設定ファイルにOGNL式が書けるようになっているので、getTextメソッドはバリデーション定義のXMLファイルにも適用できます。下記の例では、動的なパラメータを扱えるようになります。なおOGNL式でのリストは、プログラムでの表記と異なり、要素を{}で囲むように記述しますので注意してください。

XML定義ファイルでのgetText使用例

<validator type="requiredstring">

    <param name="fieldName">name</param>

    <message>${getText("errors.required",{getText("fieldLavel")})}</message>

</validator>

0 件のコメント:

コメントを投稿