|
|
「Java SE 6完全攻略」第53回 ResourceBundleの新機能 その1国際化プログラミングで避けて通れないのが、ユーザインターフェースなどで使用される文字列です。 たとえば、メニューバーの文字列は英語であれば「File」と表記し、日本語であれば「ファイル」と表記します。言語に応じて変化させるということは、ロケールに応じて変化させるということと同じです。 つまり、これらの文字列はそれぞれのロケールごとに用意しておき、実行時に差し替えます。このようにロケールごとに変化させる文字列などをまとめて地域化リソースと呼びます。 そして、地域化リソースを扱うためのクラスがjava.util.ResourceBundleクラスです。 Java SE 6では、このResourceBundleクラスの機能が大幅に強化されました。 そこで、今週はResourceBundleクラスの基本的な使用方法を紹介し、来週から新しい機能について紹介していくことにしましょう。 ResourceBundleクラスの使い方ResourceBundleクラスの使用方法はとても簡単です。しかし、その動作原理を理解しておかないと、望んだリソースが取得できないこともあります。
このサンプルでは、System.out.printlnメソッドで出力する文字列をResourceBundleオブジェクトから取得します。 public ResourceBundleSample1() {
ResourceBundle resources
= ResourceBundle.getBundle("net.javainthebox.resources");
System.out.println(resources.getString("hello.world"));
}
まず、ResourceBundleオブジェクトを取得します。これには、赤字で示したように、ResourceBundleクラスのstaticメソッドであるgetBundleメソッドを使用します。 getBundleメソッドの引数がリソースバンドルのファミリ名となります。パッケージを使用する場合は、パッケージ名を記述した後、続けてファミリ名を記述します。 上記のコードではnet.javaintheboxがパッケージで、resourcesがファミリ名になります。 また、getBundleメソッドにはロケールを引数に取るものがオーバロードされています。ロケールを指定しない場合、デフォルトロケールが使用されます。 リソースは文字列のキーとペアで保持されており、リソースを取得するには、getObjectメソッド、getStringメソッド、getStringArrayメソッドのいずれかを使用します。 リソースが文字列以外の場合に使用するのがgetObjectメソッド、文字列の場合はgetStringメソッドになります。文字列の配列の場合、getStringArrayメソッドを使用します。 このいずれのメソッドも引数はキーとなる文字列です。 上記のコードでは文字列のリソースを扱っているので、getStringメソッドを使用しています。また、引数のキーはhello.worldです。 リソースの記述リソースを記述するためには、次の2種類の方法があります。
クラスとして記述する方法は、java.util.ListResourceBundleクラスの派生クラスとしてリソースを記述します。 この時、リソースバンドルのファミリ名がクラス名となります。したがって、上記のコードでは、パッケージがnet.javainthebox、クラス名がresourcesとなります。 package net.javainthebox;
import java.util.ListResourceBundle;
public class resources extends ListResourceBundle {
private final static String resources[][] = {
{"hello.world", "Hello, World! - resources class"}
};
protected Object[][] getContents() {
return resources;
}
}
ListResourceBundleクラスの派生クラスでオーバライドしなくてはいけないのが、赤字で示したgetContentsメソッドです。 getContentsメソッドの戻り値はObjectクラスの二重配列です。キーとリソースのペアを配列にし、それを複数保持しています。 できあがったresourcesクラスが、デフォルトのリソースバンドルとなります。 さて、ここに日本語のリソースバンドルも加えましょう。日本語を加えるにはファミリ名に_jaを付け加えたクラスを作成します。 public class resources_ja extends ListResourceBundle {
private final static String resources[][] = {
{"hello.world", "こんにちは、世界! - resources_ja クラス"}
};
protected Object[][] getContents() {
return resources;
}
}
同じようにロケールja_JPに対応したリソースはファミリ名に_ja_JPを付け加えたクラスになります。 public class resources_ja_JP extends ListResourceBundle {
private final static String resources[][] = {
{"hello.world", "こんにちは、世界! - resources_ja_JP クラス"}
};
protected Object[][] getContents() {
return resources;
}
}
ロケールのバリアントに対応させたければ、バリアントまで含めたロケールを付加したクラスを作成します。 もちろん、クラスでリソースを記述したら、javacでコンパイルしておかないとダメです。 次にプロパティファイルで記述する方法です。 ファイルで記述する場合、ファミリ名に拡張子propertiesを加えたファイル名にします。この場合はresources.propertiesがファイル名となります。また、パッケージと同一のディレクトリに配置する必要があります。 hello.world=Hello, World! - resources.properties. プロパティファイルはキーが左辺、リソースが右辺となるように記述します。 このファイルがデフォルトリソースバンドルに相当します。 クラスでリソースを記述したのと同様に、日本語ロケールの場合ファイル名はresources_ja.properties、ロケールja_JPの場合ファイル名はresources_ja_JP.preopertiesとなります。 プロパティファイルに日本語などを使う場合、Unicodeエスケープで記述する必要があります。たとえば、resources_ja.propertiesの内容を次のようにしたとしましょう。 hello.world=こんにちは、世界! - resources_ja.properties これをnative2asciiツールを使用してUnicodeエスケープに変換します。変換すると次のようになります。 hello.world=\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c! - resources_ja.properties プロパティファイルで記述されたリソースを扱うのはjava.util.PropertyResourceBundleクラスですが、プログラムの中で直接使用することはありません。 さて、リソースを記述できたので、サンプルを実行してみましょう。 C:\temp>java ResourceBundleSample1 こんにちは、世界! - resources_ja_JP クラス デフォルトロケールであるja_JPに対応したリソースが選択されたことが分かります。 リソースバンドルの検索リソースの記述にはクラスを使用する方法とプロパティファイルを用いる方法を紹介しました。この両者を混在させて使用することもできます。 重要なのは、複数のロケールに対応したリソースを記述した場合、クラスもしくはプロパティファイルの選択順序を理解しておくということです。 ResourceBundleクラスは条件に合致するリソースバンドルを検索し、はじめに見つかったリソースバンドルを使用します。 ここで、指定されたロケールがlan_NA_VAであり、デフォルトロケールがdeflan_DEFNA_DEFVA、またリソースバンドルのファミリ名がresourceであったとしましょう。この場合、ロケールの検索順序は次のようになります。
もし、リソースバンドルが発見できなかった場合、java.util.MissingResourceException例外が発生します。 前節の実行結果はデフォルトロケールで検索を開始し、デフォルトロケールに対応するクラスファイルresources_ja_JP.classが発見できたため、それを利用しています。 この結果はクラスファイルの方がプロパティファイルよりも優先して検索されるため、resources_ja_JP.propertiesが存在していたとしても変化しません。 ここまで、ResourceBundleクラスの使い方を紹介してきましたが、面倒くさいと思えるところがいくつかありませんか? たとえば、日本語のプロパティファイルはnative2asciiツールでUnicodeエスケープ表示に変更しなくてはいけないことや、プロパティファイルしか作成していないのにわざわざクラスファイルを検索しにいくところなどです。 と、このように書いたら、Java SE 6ではそれが解決していることが分かってしまいますね。その新機能の紹介は来週からのお楽しみということにしておきましょう。
|