ローカライズされたテキストを絶えず変更する必要がある場合、たとえば、展開ごとに異なる傾向がある場合は、データベースが最適です。ええと、データベースだけでなく、なんらかの方法で文字列をキャッシュする必要があります。そしてもちろん、リソースアクセスレイヤーを完全に再構築したくないと思います。
そのためにResourceBundle
、データベースから文字列を自動的にロードしてに格納するようにクラスを拡張することを提案できますWeakHashMap
。私はその機能のために選択WeakHashMap
します-それはメモリフットプリントを減らす必要がなくなったときにマップからキーを削除します。とにかく、アクセサクラスを作成する必要があります。かなり古いテクノロジーであるJ2EEについて言及したので、Java SE 1.4互換の例を示します(新しいJava用に簡単に作り直すことができ、必要に応じて@Overrideを配置し、列挙に文字列の一般化を追加するだけです)。
public class WeakResourceBundle extends ResourceBundle {
private Map cache = new WeakHashMap();
protected Locale locale = Locale.US; // default fall-back locale
// required - Base is abstract
// @Override
protected Object handleGetObject(String key) {
if (cache.containsKey(key))
return cache.get(key);
String value = loadFromDatabase(key, locale);
cache.put(key, value);
return value;
}
// required - Base is abstract
// @Override
public Enumeration getKeys() {
return loadKeysFromDatabase();
}
// optional but I believe needed
// @Override
public Locale getLocale() {
return locale;
}
// dummy testing method, you need to provide your own
// should throw MissingResourceException if key does not exist
private String loadFromDatabase(String key, Locale aLocale) {
System.out.println("Loading key: " + key
+ " from database for locale:"
+ aLocale );
return "dummy_" + aLocale.getDisplayLanguage(aLocale);
}
// dummy testing method, you need to provide your own
private Enumeration loadKeysFromDatabase() {
return Collections.enumeration(new ArrayList());
}
}
ResourceBundleの読み込みルールがおかしいため、実際にはクラスを拡張WeakResourceBundle
して、サポートされている言語ごとに1つのクラスを作成する必要があります。
// Empty Base class for Invariant Language (usually English-US) resources
// Do not need to modify anything here since I already set fall-back language
package com.example.i18n;
public class MyBundle extends WeakResourceBundle {
}
それぞれ1つのサポートされている言語(私はそれが悪いことを知っています):
// Example class for Polish ResourceBundles
package com.example.i18n;
import java.util.Locale;
public class MyBundle_pl extends WeakResourceBundle {
public MyBundle_pl() {
super();
locale = new Locale("pl");
}
}
ここで、をインスタンス化する必要がある場合は、次のResourceBundle
ように呼び出すだけです。
// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
"com.example.i18n.MyBundle", polishLocale);
そして、キーにアクセスするには:
String someValue = myBundle.getString("some.key");
考えられる落とし穴:
ResourceBundle
完全修飾クラス名(したがってパッケージ名)が必要です。
Locale
パラメータを省略すると、デフォルト(サーバーを意味する)Locale
が使用されます。Locale
インスタンス化中は必ず合格してくださいResourceBundle
。
myBundle.getString()
MissingResourceException
あなたが私の提案に従えば投げることができます。問題を回避するには、try-catchブロックを使用する必要があります。代わりに、キーが欠落している場合(など)にデータベースアクセス層からダミー文字列を返すことを決定できますreturn "!" + key + "!"
が、どちらの方法でも、おそらくエラーとしてログに記録されるはずです。
- 言語と国コードの両方を渡すロケールオブジェクトの作成を常に試みる必要があります。これは、たとえば簡体字中国語(zh_CN)と繁体字中国語(zh_TW)のような言語は、(少なくとも書き込みに関しては)まったく異なる言語であり、2つのフレーバーをサポートする必要があるためです。他の国では、ResourceBundleは実際に正しい言語リソースを自動的にロードします(私が作成したので、まだ機能していることに注意してください
MyBundle_pl.java
。また、特定の言語のリソースクラスがない場合MyBundle_pl_PL.java
、ResourceBundleは自動的にEnlish-US()にフォールバックします(MyBundle.java
そのため、このような奇妙なクラス階層を使用しました)。
編集
それをもっと恐ろしいものにする方法についてのいくつかのランダムな考え。
静的ファクトリ(ResourceBundleを直接使用することは避けてください)
ResourceBundleを使用してバンドルを直接インスタンス化する代わりに、静的ファクトリメソッドを追加できます。
public static ResourceBundle getInstance(Locale aLocale) {
return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}
WeakResourceBundle
クラスの名前をより適切な名前に変更することにした場合(私は使用することにしましたLocalizationProvider
)、コードの消費からバンドルを簡単にインスタンス化できるようになりました。
ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);
自動生成されたリソースクラス
ローカライズされたMyBundle
クラスは、スクリプトを作成することで簡単に生成できます。スクリプトは、構成ファイルまたはデータベース駆動のいずれかである可能性があります。システム内で使用されているロケールを何らかの方法で知る必要があります。いずれにせよ、クラスは非常によく似たコードを共有しているので、それらを自動的に生成することは本当に理にかなっています。
ロケールの自動検出
クラスを実装するのはあなたなので、その動作を完全に制御できます。したがって、(アプリケーションアーキテクチャを知っている)ここにロケール検出を含め、getInstance()
実際に適切な言語リソースを自動的にロードするように変更できます。
追加のローカリゼーション関連のメソッドを実装する
ローカライズされたアプリケーションで実行する必要のある一般的なタスクがあります。日付、数値、通貨などのフォーマットと解析が通常の例です。エンドユーザーのロケールを設定したら、そのようなメソッドをLocalizationProviderでラップするだけです。
ねえ、私は本当に私の仕事が大好きです:)