ローカライゼーション

ローカライゼーションとは、正しい言語で正しいテキストをユーザーに提示するということに関するものです。

ローカライゼーションのサポートはTapestryにうまく統合されています。 Tapestryではユーザーに示すテキストをアプリケーションの他の部分から簡単に切り離すことができます ... そして、そのテキストをJavaコード内やコンポーネントテンプレート内から取り出すことができます。 他の言語に翻訳したメッセージをTapestry内ですべて一緒に扱うことができます。

コンポーネントメッセージカタログ

コンポーネント毎にコンポーネントメッセージカタログがあります。コンポーネントメッセージカタログは拡張子 ".properties" を持つファイルのセットです。 これらのプロパティファイルは java.util.ResourceBundle と同じフォーマットで、key=value 形式の行から成ります。 これらのファイルを置く場所は、ページまたはコンポーネントのコンパイル済みJavaクラスと同じ、クラスパス内のパッケージフォルダです。

したがって、org.example.myapp.pages.MyPage というクラスのメインプロパティファイルは org/example/myapp/pages/MyPage.properties となります。

これらの値の翻訳がある場合、拡張子の前に ISO言語コード を付けた追加のプロパティファイルを用意します。例えば、フランス語訳がある場合は MyPage_fr.properties というファイルを作成します。

言語固有のファイル内の値はメインプロパティファイルの値よりも 優先 します。フランスで使用されているフランス語という、より限定したローカライゼーションが必要な場合、 MyPage_fr_FR.properties というファイルを用意することができます(これは言語コード + 国コードで、これに加えてさらにバリエーションを追加していくことも出来ます ... しかし、言語コードよりも細かなバリエーションが必要になることは実際にはあまり無いでしょう)。

カタログ内のメッセージにはキーでアクセスします。カタログ内のメッセージにアクセスする際に、Tapestryではキーの大文字小文字を区別しません。

プロパティファイルの文字セット

Tapestry は UTF-8 文字セットでメッセージカタログのプロパティファイルを読み込みます。 そのため、Javaの native2ascii ツールを使う必要はありません。

メッセージカタログの継承

コンポーネントクラスが別のコンポーネントクラスのサブクラスの場合、ベースクラスのメッセージカタログを継承します。 自身のメッセージカタログはベースクラスのカタログを拡張し、またベースクラスから継承した値より優先します。

これにより、共通のメッセージを含んだベースコンポーネントクラスを用意し、それをサブクラスで拡張したりオーバーライドすることができます(ベースコンポーネントクラスのメソッドを拡張したりオーバーライドするのと同じです)。 もちろんこれは多階層の継承においても働きます。

アプリケーションメッセージカタログ

コンテキスト内に WEB-INF/AppName.properties というファイルがある場合、このファイルはアプリケーション全体用のメッセージカタログとして使用されます。 web.xml内のフィルタ名が AppName として使用されますが、それは通常 "app" となっているので、アプリケーションメッセージカタログは WEB-INF/app.properties となります。 このファイル名の大文字小文字は区別しません。このプロパティファイルをローカライズすることもできます。

ページやコンポーネントは各々にアプリケーションメッセージカタログをオーバーライドできます。

コンポーネントテンプレートのローカライズ

メッセージカタログと同じルックアップメカニズムがコンポーネントテンプレートにも適用されます。 Tapestryは各コンポーネントテンプレートのローカライズされたバージョンを探し、最も適切なものを使用します。 例えば、フランス語のユーザー用に MyPage_fr.html を用意し、MyPage.html を残りのユーザー用とすることができます。

ローカライズされたメッセージへのアクセス

ここまでに述べたことは、どんなファイルを作成しどこに置けば良いかということで、その情報をどのように利用するかということについては述べていません。

メッセージには次のふたつの方法でアクセスすることができます:

ひとつ目の方法では、コンポーネントパラメータ内かテンプレート式展開として message: バインディングプリフィックスを使用することが出来ます。

<t:layout title="message:page-title">

  ${message:greeting}, ${user.name}!
  
  . . .
</t:layout>

ここでは、page-title メッセージがカタログから取り出されてBorderコンポーネントのtitleパラメータに渡されます。

そして greeting メッセージがカタログから取り出され、テンプレートの一部としてレスポンスに書き出されます。

通常 "prop:" がデフォルトのバインディングプリフィックスなので、user.name はメッセージのキーではなくプロパティパスです。

プロパティファイル一式を用意し、次のメッセージを拡張することができます:

page-title=Your Account
greeting=Welcome back

フランス語バージョンはおそらく次のようになるでしょう:

page-title=Votre Compte
greeting=Bienvenue en arriere

プログラム上では Messages インターフェースのインスタンスとして、コンポーネントメッセージカタログをコンポーネントクラスにインジェクトすることができます:

  @Inject
  private Messages messages;

get() メソッドでメッセージを取得するか format() メソッドでフォーマットすることができます:

  public String getCartSummary()     
  {
    if (items.isEmpty())
      return messages.get("no-items");
      
    return messages.format("item-summary", items.size());
  }

format() メソッドは java.text.Formatter を使用しているので、printfスタイルのすべてを利用できます。

no-items=Your shopping cart is empty.     
item-summary=You have %d items in your cart.

Tapestryテンプレート内で条件によるメッセージの切り替えを行うのは簡単ですが、Javaコード内で行う方がさらに簡単なことも時々あります。

存在しないキー

メッセージカタログ内に無いキーを参照してもTapestryは例外を投げません(例外を投げると、アプリケーションの開発初期にはとてもイライラすることになります)。 キーが存在しない場合、"[[missing key: key-not-found]]" といった「プレースホルダ」メッセージが生成されます。

リロード

コンポーネントクラスやコンポーネントテンプレートと同様に、メッセージカタログのプロパティファイルを変更するとすぐに変更内容が反映されます。

アセットのローカライゼーション

アセットをインジェクトする場合、そのアセットも同様にローカライズされます。 使用中のロケールに最も近いものを探し、それに対応するAssetオブジェクトがインジェクトされます。

ロケールの選択

HTTPリクエストヘッダから各リクエストのロケールを決定します。リクエストのロケールはWebブラウザの環境を反映しています。ユーザーが使用しているキーボードレイアウトまで表しているかもしれません。 それは、例えばイギリス英語(en_GB)と米語(en)を区別するというように、とても詳細になることがあります。

Tapestryはリクエストで指定されている生のロケールを既知のロケールに「限定」します。 設定シンボル tapestry.supported-locales を用いて、各リクエストに対して有効なロケールを選択します。 このシンボルの値はロケール名のカンマ区切りリストです。Tapestryはリクエストロケールに対して最も適合するロケールをこのリストから選びます; 例えば、リクエストロケールが "fr_FR" の場合、"de" ではなく "fr" が選択されるでしょう。 適合するロケールが見つからない場合、シンボルに設定したリストの最初のロケール名が有効なロケールとして使用されます(リストの最初のロケールは、適合するロケールがない場合のデフォルトです)。 したがって、主にフランス語を話す人を対象としたサイトではリスト内の最初のロケールとして "fr" を指定することになるでしょう。

ロケールの変更

Tapestryはロケールの変更にはまだ対応していません。しかしもうすぐ利用できるようになるでしょう。 Tapestry 4 の振る舞いを真似る予定です: 次回の閲覧でデフォルトとなるロケールをクッキーでクライアントに保存しておき、(セッションが存在する場合)セッションにロケール名を格納します。 TODO: 確かこれはKentによって実装されたんだと思う。

出力のContent-Typeと文字セット

Tapestryがページをレンダリングする際の、いちばん最初の段階で出力のContent-Typeと文字セットを決定します。

この情報はページのメタデータから得られます。メタデータは Meta アノテーションを用いて指定します。

"tapestry.response-content-type" というキーで得られるメタデータがContent-Typeとなります。デフォルトは "text/html" です。

ContentType アノテーションでレスポンスのContent-Typeを指定することができます。このアノテーションのvalue属性がContent-Typeとなります。

全ての出力マークアップと全てのリクエストの文字セットは "UTF-8" です。UTF-8はUnicodeの一形態であり、各文字は1バイトかそれ以上の数のバイトにエンコードされます。 西ヨーロッパ言語の多くの文字(すなわち、典型的なASCII文字)は1バイトにエンコードされます。 アクセント付きの文字や(日本語、アラビア語のような)非西ヨーロッパ言語の文字は2バイト以上にエンコードされます。