今回はアプリケーションを作成する上で見過ごされがちではあるのですが、とても重要な要素について紹介します。
もう読者の皆さんにはおわかりのはずですね。そう、国際化です。
アプリケーションを動作させることを、特定の言語や地域だけに限定できるのであれば、国際化は必要ありません。しかし、ネットでつながれた世界では、アプリケーションをどの言語、どの地域で動作させるかを特定することがとても難しくなります。
そのため、できればはじめから国際化を考慮して、アプリケーションを作る必要があります。とはいうものの、アプリケーションを作りはじめてすぐの時には、なかなか国際化まで頭が回らないというのも本音だと思います。
Javaで国際化を行う場合、メニューなどの文字列はリソースバンドルによって切り替えます。最近では、EclipseやNetBeansなどのIDEの機能として、国際化が簡単にできるようになりました。しかし、IDEの力を借りないで既存のアプリケーションの国際化を行うのは、それなりに大変です。
これに対し、JavaFXではとても簡単に国際化を行うことができます。しかも、国際化は言語レベルで扱われるため、とてもスマートに国際化することができるのです。
そこで、今回は次の2つのトピックに関して解説を行っていきます。
- ロケールによる文字列の切り替え
- 埋め込み文字列のフォーマット
今回使用したサンプルのソースを含めたNetBeansのプロジェクトは下記のリンクよりダウンロードすることができます。
なお、2月12日にJavaFX 1.1がリリースされました。それと同時にNetBeansのJavaFXプラグインもJavaFX 1.1に対応したバージョンがリリースされています。本連載も今回からJavaFX 1.1を使用していきます。
ロケールによる文字列の切り替え
国際化を行うに当たって、その対象となるアプリケーションを用意しましょう。今回、作成したのは世界時計です。といっても、3つの都市の時間しか表示できませんが。
コンボボックスで都市を選択すると、その都市における時間を表示するというものです。ちょっと長いのですが、以下に世界時計のスクリプトを示します。
コンボボックスで都市名を選択すると、変数selectedIndexの値が変化します。変数selectedIndexには置換トリガが設定しておき、値が変更するとそれに応じたタイムゾーンをデフォルトタイムゾーンに設定します。
タイムゾーンにはJavaのjava.util.TimeZoneクラスを使用しています。デフォルトタイムゾーンを変更すると、Dateクラスの変数timeの表示が変化します。
図1に動作例を示します。Tokyoを選択しているので、時間がJSTで表されていることがわかります。
では、まず世界時計の文字列をロケールに応じて変更するようにしてみましょう。
JavaFXではロケールに応じた文字列の変更はとても簡単に行うことができます。たとえば、ラベルで表示しているCity:という文字列をロケールに応じて切り替えるようにするには、次のように表記します。
文字列の前にシャープを2つ付け足すだけです。
Javaのリソースバンドルではキーに対応した文字列をロケールによって変更させますが、JavaFXでは##がついた文字列自体がデフォルトの文字列になり、しかもキーにもなります。
次に行うのが、リソースファイルの作成です。
リソースファイルはデフォルトではスクリプトのファイル名に言語を付け加え、拡張子をfxpropertiesとします。たとえば、世界時計のスクリプトファイルはworldClock.fxとしたので、日本語のリソースファイルはworldClock_ja.fxpropertiesになります。同じように、フランス語であればworldClock_fr.fxpropertiesとなります。
作成したリソースファイルはスクリプトファイルと同じディレクトリに配置します。したがって、NetBeansでは図2のように表示されます。
worldClock_ja.fxpropertiesの内容は次のようにします。
先ほど##を付けた文字列をキーとし、イコールで結んで言語に応じた表記を記述します。ここでは、"City:"に対応する日本語の文字列として"都市:"としました。
また、リソースファイルの文字セットを指定することもできます。文字セットを指定する場合、リソースファイルの先頭に@charsetに続けて文字セット名を記述します。最後のセミコロンを忘れずに。ここでは、Shift_JISにしてみました。なお、リソースファイルのデフォルトの文字セットはUTF-8です。
では、実行してみましょう。実行結果を図3に示します。
図1では「City:」と表示されていた箇所が「都市:」と表示されるようになりました。
他の文字列に関しても日本語のリソースを記述してみましょう。スクリプトで##を付加する文字列として、都市の名前とStageのtitleアトリビュートがあります。これらの部分も次のようにスクリプトを変更します。
これに対応して、リソースファイルworldClock_ja.fxpropertiesは次のようにしました。
これだけで日本語に対応することができました。さっそく実行してみましょう。
ここでは通常の文字列しか扱いませんでしたが、\n、\t、\r、\f、\"、\\などの文字もリソースファイルで扱うことができます。
このように簡単に各国語に対応できることはわかりました。しかし、ここで気になるのがデフォルトの文字列が同じだった場合です。たとえば、メニューでよく使用されるFileなどは、名詞と動詞の両方で使用されます。同じ訳語を当てるのであれば問題ありませんが、異なる訳語を当てる場合、何らかの方法で区別することが必要です。
また、デフォルトの文字列が長い場合はどうでしょう。何行にも渡るような文字列をリソースファイルに書くのはちょっと面倒です。
このような場合、デフォルトの文字列とは別にキーとなる文字列だけ別に記述することも可能です。キーを別に記述する場合、##の後に[ ]を記述し、その中にキーとなる文字列を記述します。
ここでは、都市名をキーを別にして書き直してみます。
"New York"の文字列に対応するキーは"NY"とし、同じように"Paris"は"PR"、"Tokyo"は"TK"としました。
リソースファイルはリスト7のようになります。
実行結果は図4と同じなので示しませんが、このようにキーとデフォルト文字列を別々に扱うことも可能なのです。
スクリプトとリソースファイルの結びつけ
デフォルトでは1つのスクリプトファイルにつき、1つのリソースファイルが必要ですが、スクリプトファイルが多くなってくるとリソースの扱いが煩雑になりがちです。また、スクリプトファイルと同じディレクトリに配置しなくてはならないことも、ちょっと気になります。
そこで、javafx.util.StringLocalizerクラスを使用して、スクリプトとリソースファイルの結びつきを変更してみましょう。
StringLocalizerクラスにはassociate関数とdissociate関数が定義されています。associate関数がスクリプトとリソースファイルの結びつけ、dissociate関数が切り離しを行います。
associate関数はオーバロードされており、パッケージ単位ごとにリソースファイルを割り当てることと、1つのスクリプトファイルごとにリソースファイルを割り当てることの両方ができます。前者が引数が2つ、後者が引数が3つとなります。
ここでは、後者の引数が3つのものを使用して、図5のようにリソースファイルをデフォルトの位置とは違うresourcesディレクトリに置いてみましょう。
この変更に伴うスクリプトの変更は、リスト8にある1行をスクリプトの先頭に付け加えるだけです。
associate関数の第1引数がリソースファイルの場所を示しています。実際のリソースファイル名はresources/worldClock_xx.fxpropertiesとなります(xxの部分は言語を示します)。第2引数がパッケージ、第3引数がスクリプトファイルを示します。ここでは、パッケージを使用していないので、空文字にしてあります。
また、StringLocalizerクラスでも文字列の置き換えを行うことができます。たとえば、都市名をStringLocalizerクラスを使用して言語ごとに置き換えする場合、リスト9のように記述します。
StringLocalizerオブジェクトのkeyアトリビュートにキーとなる文字列を指定すると、localizedStringアトリビュートに言語ごとに置き換えられた文字列を取得することができます。
埋め込み文字列のフォーマット
JavaFX Scriptでは文字列中に{ }を記述することで、オブジェクトを埋め込むことができます。これは読者のみなさんはすでにご存じのはずです。
今までは単にオブジェクトを記述していましたが、フォーマットを指定することもできるのです。たとえば、次のような表記を行うことができます。
%で始まる部分がフォーマット文字列です。ここで使用できるフォーマット文字列は、java.util.Formatterクラスで使用できるフォーマット文字列と同じです。
このスクリプトを実行した結果を図6に示します。
フォーマット文字列を指定しなかった場合は、文字列表記である%sを指定した場合と同じ結果になります。
%dや%fなどの数値フォーマットや%sはロケールに応じて変更することはありませんが、日付/時間はロケールによって変化することがあります。
日付/時間に関するフォーマット文字列はjava.util.Formatterクラスで使えるものだけでなく、POSIXのstrftimeに対応するように拡充されています。表1に拡張されたフォーマット文字列を示します。出力例は日本時間の2009年2月9日(月) 2時56分35秒をフォーマットした場合を示します。
表1 日付/時間に関する拡張フォーマット
フォーマット文字列 |
説明 |
出力例 |
%tx |
ローカライズされた日付表現 |
2009年2月9日 |
%tX |
ローカライズされた時間表現 |
2時56分35秒 JST |
%tG |
ISO8601形式の4桁の西暦年。ISO8601で表した週数に対応している |
2009 |
%tg |
ISO8601形式の2桁の西暦年。ISO8601で表した週数に対応している |
09 |
%tu |
曜日の数値表現。月曜が1、日曜が7 |
1 |
%tU |
週数。1月の第1日曜を第1週とする |
06 |
%tV |
ISO8601で表した週数。その年に少なくとも4日以上含まれる最初の週を第1週とする |
07 |
%tw |
曜日の数値表現。日曜日が0、土曜日が6 |
1 |
%tW |
週数。1月の第1月曜日を第1週とする |
06 |
%tEc |
代替カレンダの日付と時間表現 |
平成21年2月9日 2時56分35秒 JST |
%tEC |
代替カレンダの基準年の名前 |
平成 |
%tEx |
代替カレンダの日付表現 |
平成21年2月9日 |
%tEX |
代替カレンダの時間表現 |
2時56分35秒 JST |
%Ey |
代替カレンダの年 |
21 |
代替カレンダというのは、西暦以外の地域に特有のカレンダのことを指します。日本では和暦が代替カレンダとなります。
Javaでは和暦を扱うためにja_JP_JPという特殊なロケールを使う必要がありました。それに比べるとJavaFXでは和暦でもシンプルに表すことができます。
たとえば、先ほどの世界時計で代替カレンダを使用してみましょう。%tEcで時間をフォーマットするとデフォルトでは図7になり、日本語ロケールでは図8となります。
英語ロケールでは代替カレンダがないため西暦で表され、日本語ロケールでは和暦で表されました。
しかし、ちょっと違和感があります。たとえば、英語ロケールでは12時間表記で表されているのが、日本語表記では24時間表記で表されている点や、タイムゾーンが日本語ロケールだけ表示されている点などです。そこで、フォーマットをカスタマイズしてみました(リスト11)。
英語ロケールで表示すると、図9のようになります。
英語ではこの順番ですが、日本語では曜日は日付の後に表記するなど順番がことなります。そこで、この文字列もロケールによって切り替えてしまいましょう。
つまり、文字列の前に##を付加して記述してしまいます。
そして、リソースファイルにリスト13にある行を追加します。
キーの部分はスクリプトでの文字列から{ }と変数を除いた文字列とします。値には見慣れない数字と$が入っています。これはフォーマット文字列の順番を表しています。最初の%2$txはキーの2番目のフォーマット文字列である%txに対応しています。
これを図示したのが図10です。このように、$の前の数字で表している要素を指定できます。これはFormatterクラスと同じです。
では、これで実行してみましょう。図11に日本語ロケールで実行した結果を示します。
今回示したようにJavaFXの国際化は、とても簡単に扱うことができます。
残念ながら、現状のJavaFXではデフォルトロケールしか扱うことができません。デフォルトロケールを切り替えることは可能ですが、アプリケーション中で複数のロケールを使用することはできないようです。今後の拡充に期待したいところですね。
また、通貨のフォーマッティングや、アラビア語のように右から左に記述する文字列のサポートなどが、今後行われる予定だそうです。
なお、JavaFX 1.0および1.1では、AppletやJava Web Startでリソースファイルの読み込みを行う場合、JARファイルに署名が必要です。しかし、次のバージョンから署名が不必要になるようです。