Name
Password

国際化

  • 2010-02-09 11:30:07

Ymirではアプリケーションの国際化のための仕組みとして、ロケール情報の管理機能とメッセージリソース機能を持っています。

ロケール情報の管理機能

Ymirではアプリケーション利用者毎にロケールを持たせるための仕組みをフレームワークとして提供しています。

ロケールの操作にはorg.seasar.ymir.locale.LocaleManagerを用います。LocaleManagerは特に設定をしなくてもPageクラス等から利用できますが、app.diconに登録されているアプリケーション用コンポーネントからLocaleManagerを利用したい場合は、app.diconからymir-component.diconをインクルードしておいて下さい。

LocaleManagerを利用したいコンポーネントには、LocaleManagerのためのSetterを用意しておけばLocaleManagerコンポーネントが自動的にインジェクトされます。

利用者に関連付けられているロケールを取得するにはLocaleManager#getLocale()を使います。

現在のセッションにロケールを関連付けるためには、LocaleManager#setLocale(Locale)を使います。

現在のセッションに関連付けられているロケールを削除するには、LocaleManager#removeLocale()を使います。

なお、LocaleManagerはメッセージリソースから適切なメッセージを取り出す処理などでYmirの内部からも利用されますので、アプリケーションでは基本的にはLocaleManagerを使ってロケールの管理をするようにして下さい。

メッセージリソース機能

メッセージリソースとは、Strutsのメッセージリソースと同様のもので、ロケールごとのプロパティを扱うための仕組みです。

メッセージリソースは、次のようにして利用されます。

  1. バリデーションエラーなどのエラーメッセージを格納するNotesオブジェクトから実際のメッセージ文字列を構築する
  2. ZPTテンプレート中にメッセージリソースのキーを指定したTALES式を埋め込むことで、メッセージ文字列を表示する
  3. Pageクラスなどからメッセージリソースを明示的に取得してメッセージ文字列を構築する

メッセージリソースを管理するためには、org.seasar.ymir.message.Messagesを使用します。

標準のYmirの設定では、クラスパス上のmessages_XXX.xpropertiesXXXはロケール名。このファイルのソースは通常src/main/resources/ディレクトリ直下に置かれます)というロケール毎のファイルに格納されたリソースがメッセージリソースとして使われるようになっています。

xpropertiesファイルの書式は基本的に通常のJavaのプロパティファイルと同じですが、UTF-8で直接非ASCII文字を記述することができます。

Notesオブジェクトを使ったメッセージ文字列の構築

例えば、PageクラスのgetNotes()メソッドが返すNotesオブジェクトから現在のロケールに従ったメッセージ文字列を表示するには、ZPTテンプレート中で次のように書きます。

<ul>
  <li tal:repeat="note self/notes/notes" tal:content="note/%value">メッセージ</li>
</ul>

Noteオブジェクトを格納する変数「note」について「note/%value」と書くと現在のロケールに従ったメッセージ文字列がMessagesコンポーネントから取り出され、Noteがパラメータを持つ場合はパラメータが埋め込まれてメッセージ文字列が構築されて表示されます。

パラメータが埋め込まれる際、パラメータの値の先頭に「label.」をつけたキーに対応する値がMessagesコンポーネントにあればそれが実際の値として埋め込まれます。なければパラメータの値がそのまま埋め込まれます。

例えばNoteが持つキー(Note#getValue()の値)が「error.required」、Noteが持つパラメータ(Note#getParameters()の値)の1つ目の値が「name」、messages.xpropertiesの内容が以下のような場合、

error.required={0}を指定して下さい
label.name=なまえ

実際に表示されるメッセージは「なまえを指定して下さい」となります。キーが「error.required」、パラメータの1つ目の値が「password」である場合は、label.passwordのエントリが登録されていないので「passwordを指定して下さい」となります。

messages.xpropertiesからメッセージ文字列やパラメータ文字列を取り出す際の処理についてはページ固有のメッセージ文字列の探索も参照して下さい。

[Ymir-1.0.3以降] Noteがパラメータを1つ以上持つ場合、キーと最初のパラメータ値を「+」で結合したキーに対応するメッセージ文字列があれば、それが使用されます。例えば上の例で、

error.required+name={0}を教えて下さい

というエントリがmessages.xpropertiesにある場合、キーが「error.required」、パラメータが「name」であるNoteからメッセージ文字列を構築すると「なまえを教えて下さい」になります。

複合パラメータに関するメッセージ文字列の検索

名前が「.」でつながっていたり、「[10]」などの添え字がついている名前が組み合わさったようなパラメータを「複合パラメータ」と言います。例えば次のようなものが複合パラメータです:

user.first.name
names[10]
fruits.fruit[10].name

複合パラメータについては、通常のパラメータとはメッセージ文字列の検索方法が異なります。

messages.xpropertiesに「_.note.parameter.segment.template」というエントリがある場合、複合パラメータはセグメント(「.」で区切られている単位)毎に処理されます。例えばmessages.xpropertiesに以下のエントリがある場合、

_.note.parameter.segment.direction=r
_.note.parameter.head=
_.note.parameter.delimiter=の
_.note.parameter.tail=
_.note.parameter.segment.template={1}番目の{0}

複合パラメータ「fruits.fruit[10].name」に対応するメッセージ文字列は「fruitsの10番目のfruitのname」になります。

基本的には「_note.parameter.head」の文字列を先頭に付与し、セグメント毎に「_note.parameter.delimiter」を挟んで、「_note.parameter.segment.direction」で指定された方向に(rは右向き、lは左向き)結合し、最後に「_note.parameter.tail」の文字列を付与します。セグメントが添え字を持っている場合は「_note.parameter.segment.template」を使って添え字つきのセグメントのメッセージ文字列を構築して結合します。「_note.parameter.segment.template」に指定できるプレースフォルダは、{0}がセグメントから添え字を取り除いたものから構築したメッセージ文字列、{1}が添え字、{2}が添え字を1オリジンに直したものになります。

[Ymir-1.0.3以降] セグメントに対応するメッセージ文字列が空文字列である場合は、そのセグメントは無視されます。例えばmessages.xpropertiesに以下のエントリがある場合、

label.first=

「user.first.name」に対応するメッセージ文字列は「userのname」になります。

messages.xpropertiesに「_.note.parameter.segment.template」というエントリがない場合、複合パラメータは通常のパラメータと同様に処理されますが、その時添え字は全て除去されます。また、対応するエントリがmessages.xpropertiesnにない場合は、最後のセグメントに関してメッセージ文字列が検索されます。例えば、複合パラメータ「fruits.fruit[10].name」については、次の順でメッセージ文字列が検索されます:

fruits.fruit.name → name

メッセージキーを直接指定したメッセージ文字列の構築

例えば、メッセージキー「message.ok」に対応する現在のロケールに応じたメッセージ文字列を表示するには、ZPTテンプレート中で次のように書きます。

<span tal:replace="messages/%message.ok">メッセージ</span>

なお、この方法ではパラメータの埋め込みはできません。

ロケールによらずデフォルトのメッセージを表示したい場合は次のように「%」をつけずに書いて下さい。

<span tal:replace="messages/message.ok">メッセージ</span>

詳しくは「ZPTテンプレート」の「セグメントの解釈」の表のMessagesに関する項目を参照して下さい

プログラムコードにおけるメッセージ文字列の構築

プログラム中でメッセージ文字列を構築するには、メッセージリソースを管理するMessagesコンポーネントを取得して利用します。

Pageクラス等から利用する場合は特に設定は不要ですが、app.diconに登録されているアプリケーション用コンポーネントからMessagesコンポーネントを利用したい場合は、app.diconからymir-component.diconをインクルードしておいて下さい。

このコンポーネントを利用したいクラスでMessagesクラスを引数に持つSetterメソッドを用意するだけで、自動的にMessagesコンポーネントがインジェクトされます。後はMessagesインタフェースの提供メソッドを呼び出してメッセージ文字列を構築することができます。

メッセージ文字列の取得には、Messages#getMessage(String key)を使って下さい。指定したキーに対応するメッセージ文字列のうち、現在のロケールに合うものが返されます。

ページ固有のメッセージ文字列の探索

Ymirでは、あるキーに対応するメッセージをページ毎に変えることができるようになっています。具体的には、指定されたキーからページ固有のキーを生成し、そのキーに対応するメッセージ文字列を使用するようになっています。見つからない場合は、指定されたキーそのものに対応するメッセージ文字列が使用されます。

ページ固有のキーは、次のルールに従って生成されます。

  • キーがドットを含まない場合、ページのコンポーネント名(またはページのコンポーネント名の末尾から「Page」を取り除いたもの)とキーをドット区切りで連結したもの
  • キーがドットを含む場合、キーのドットより前の部分とページのコンポーネント名(またはページのコンポーネント名の末尾から「Page」を取り除いたもの)とキーのドットより後ろの部分をドット区切りで連結したもの

例えば「/index.html」ページ(コンポーネント名はindexPage)内でメッセージ文字列を検索する場合は、ページ固有のキーは以下のようになります:

例1: 元のキー:pageTitle → ページ固有のキー:indexPage.pageTitle, index.pageTitle
例2: 元のキー:label.userName → ページ固有のキー:label.indexPage.userName, label.index.userName
例3: 元のキー:message.action.succeed → ページ固有のキー:message.indexPage.action.succeed, message.index.action.succeed

キーの探索順序は以下のようになります:

indexPage.pageTitle → index → pageTitle

上記のようなメッセージ文字列探索が行なわれるのは、以下の場合です。

  • バリデーションエラーなどのエラーメッセージを格納するNoteオブジェクトから実際のメッセージ文字列を構築する場合(パラメータ文字列についても上記のような探索が行なわれます)
  • ZPTテンプレート中にメッセージリソースのキーを「%」つきで指定したTALES式を埋め込むことで、メッセージ文字列を表示する場合
  • Messages#getMessage(String)を使用してメッセージ文字列を取得する場合

[Ymir-1.0.7以降] またページのコンポーネント名がパッケージ名を含む場合は、パッケージ名についてもキーが生成され探索対象となります。

例えば「/member/edit/index.html」ページ(コンポーネント名はmember_edit_indexPage)内でメッセージ文字列を検索する場合は、以下のようにキーが生成され探索されます:

member_edit_indexPage.pageTitle → member_edit_index.pageTitle → member_edit.pageTitle → member.pageTitle → pageTitle

画像の国際化

画像をロケールに従って切り替えたい場合は、TALESのi18npage式を使います。

例えば/image/title.jpgという画像をロケール毎に切り替えたい場合は以下のように記述します:

<img tal:attributes="src i18npage:/image/title.jpg" src="/image/title.jpg" />