専門的に言うならば、国際化というのは、コードを変更することなく様々な言語や地域に対応できるようにするためにソフトウェアを設計することです。 地域化というのは 、地域固有のコンポーネントと翻訳文を追加し、国際化対応ソフトウェアを特定の言語や地域向けに調節することです。 文字数が長くなってしまうので、国際化(internationalization)はi18n 、地域化(localization)はl10n としばしば略されます。 internationalizationという単語は最初にi、最後にnの文字があり、その間に18個の文字があるためこのように略されています。 localizationという単語は最初にl、最後にnの文字があり、その間に10個の文字があるためこのように略されています。
LibGDXでは、 I18NBundle
クラス を使って
局所性鋭敏化された文字の保存と取得を行います。
このBundleを使うことでアプリケーションに様々な翻訳を簡単に適用できます。
各Bundleは、同じ基本名を共有するproperties ファイルの集まりです。基本名とは以下の例における MyBundle
のことです。
基本名の後ろにある文字は、地域ごとの言語コード, 国コード, バリエント を示します。
例えば:
MyBundle.properties
MyBundle_de.properties
MyBundle_en_GB.properties
MyBundle_fr_CA_VAR1.properties
MyBundle_en_GB
とは、言語コードは英語 (en
)、国コードは英国 (GB
)で指定された地域に該当します。
言語コードも国コードもバリエントも指定しない既定のproperties ファイルを作成することができます。
今回の例では、MyBundle.properties
の中身は以下のようになっています:
game=My Super Cool Game
newMission={0}, you have a new mission. Reach level {1}.
coveredPath=You covered {0,number}% of the path
highScoreTime=High score achieved on {0,date} at {0,time}
サポート地域を追加するには、翻訳された値が保存された追加の properties ファイルを翻訳担当者に作成してもらいます。 プログラムでは値ではなくキーを参照するので、ソースコードを変更する必要はありません。
例えば、追加でイタリア語をサポートするには、翻訳担当者がMyBundle.properties
ファイル内の値を翻訳し、ファイル名をMyBundle_it.properties
にして配置します。
MyBundle_it.properties
の中身は以下のようになります:
newMission={0}, hai una nuova missione. Raggiungi il livello {1}.
coveredPath=Hai coperto il {0,number}% del percorso
highScoreTime=High score ottenuto il {0,date} alle ore {0,time}
地域に関わらずゲーム名は変更したくないので、イタリア語のproperties ファイルにはgame
という名前のキーがありません。注意してください。
キーが見つからない場合は、見つかるまで漸減的な検索が行われます;
例えば、MyBundle_en_GB.properties
ファイル内でキーが見つからなかった場合は、 MyBundle_en.properties
ファイル内を検索します。
それでも見つからなかった場合は、既定のMyBundle.properties
ファイル内を検索します。
I18NBundle
クラスのインスタンスは、適切なproperties ファイルをメモリに読み込んだ後、地域用に名前が付けられた文字を管理します。
ファクトリーメソッドの createBundle
を実行して、必要なbundleのインスタンスを新規に作成して取得します:
FileHandle baseFileHandle = Gdx.files.internal("i18n/MyBundle"); Locale locale = new Locale("fr", "CA", "VAR1"); I18NBundle myBundle = I18NBundle.createBundle(baseFileHandle, locale);
もしくは、あなたがAssetManager
を使用している場合は以下のようにします:
assetManager.load("i18n/MyBundle", I18NBundle.class);
// ... after loading ...
I18NBundle myBundle = assetManager.get("i18n/MyBundle", I18NBundle.class);
createBundle
を実行する際に任意のlocaleを設定しない場合は、既定の locale が使用されます。この動きは望み通りのものでしょう。
どちらの場合でもファイル拡張子.properties
は省いて指定する必要があるので注意してください。
指定したLocale のプロパティファイルが存在しない場合は、createBundle
は最も近いファイルを見つけようと試みます。
例えば、ロケール fr_CA_VAR1
が必要な状況で既定Locale がen_US
となっている場合、
createBundle
以下の順序でファイルを検索します:
MyBundle_fr_CA_VAR1.properties
MyBundle_fr_CA.properties
MyBundle_fr.properties
MyBundle_en_US.properties
MyBundle_en.properties
MyBundle.properties
createBundle
では、基底ファイルのMyBundle.properties
を選択する前に、既定Locale を基にしてファイルを検索するので注意してください。
createBundle
が該当するファイルの検索に失敗した場合は、MissingResourceException
の例外が投げられます。
この例外が発生するのを避けるためには、ファイル名の語尾に何も付いていない基底ファイルを常に用意しておくようにしましょう。
Bundleから翻訳された値を取得するには、以下のように get
メソッドを実行します:
String value = myBundle.get(key);
get
メソッドで取得された文字は、指定したキーに対応しています。
指定したLocalのproperties ファイルが存在するのであれば、この文字は適切な言語となっているはずです。
get
メソッド(もしくはformat
メソッド)を使った際に指定したキーに対応する文字が見つからない場合は、
MissingResourceException
の例外が発生します。
翻訳文字にはフォーマットされたパラメーラーを含めることができます。これらのパラメータに値を代入するには、以下のように format
メソッドを実行します:
String value = myBundle.format(key, arg1, arg2, ...);
例えば、前述のproperties ファイルから文字を取得するには、以下のようなコードになります:
String game = myBundle.format("game"); String mission = myBundle.format("newMission", player.getName(), nextLevel.getName()); String coveredPath = myBundle.format("coveredPath", path.getPerc()); String highScoreTime = myBundle.format("highScoreTime", highScore.getDate());
引数を指定しなかった場合、 get
メソッドと format
メソッドは同じ動きをすると思うかもしれません。
その考え方は全面的に正しいとは言えません。
get
メソッドはproperties ファイル内の指定された文字を厳格に取得しますが、一方でformat
メソッドでは引数が指定されなかったとしてもエスケープ処理のために置換を行います。結果として get
の方が少し速くなりますが、翻訳にエスケープ処理が含まれていない場合を除けば常にformat
を使用するのがベストです。
既に見てきたように、properties ファイル内の文字にはパラメータを含めることができます。
これらの文字は一般的にはパターンと呼ばれ、 java.text.MessageFormat
APIで指定された構文に従います。
要するに、パターンには何も含めないか、もしくは {index, type, style}
形式の複数のフォーマットを含めることができます。
type とstyleについては設定するかどうか任意です。
全ての機能について学ぶにはMessageFormat
クラスの公式JavaDocを参照してください。
重要: 既定のエスケープ処理のルールだと翻訳する人が混乱してしまうので、LibGDX では MessageFormat
の構文について一点だけ変更を行っています。
文字の中で{
リテラルを使用したい場合、通常はシングルクォーテーション('
)を使ってエスケープ処理をする必要があります。
しかし LibGDX では、同じものを二回記述するだけです。;例えば、 {{0}
はフォーマット要素がなくてもリテラル文字 {0}
として解釈されます。
結果としてシングルクォーテーションのエスケープ処理をする必要がなくなります!
フォーマットは地域化されているので注意してください。つまり、数字や日付や時間と行ったタイプのデータは自動的に指定した地域のフォーマットで表示されます。
例えば、イタリア地域の場合だと浮動小数3.14
は 3,14
になります; 小数点がカンマに置き換わっているのが分かるでしょう。 ( GWTを使用している場合、これについていくつか制限 があります。)
java.util.Properties
と違い、既定のエンコードは UTF-8
となっています。もし何らかの理由で UTF-8 エンコードを使用したくない場合は、
希望のエンコードが指定できるよう多重定義されたcreateBundle
メソッドのうち、どれかを実行してください。
Plural forms は、MessageFormat
が提供する標準の choice
形式によってサポートされています。
詳細については java.text.ChoiceFormat
クラスの公式ドキュメントを参照してください。
ここでは例を示すだけにします。
それでは、以下のプロパティの場合について考えてみましょう:
collectedCoins=You collected {0,choice,0#no coins|1#one coin|1<{0,number,integer} coins|100<hundreds of coins} along the path.
これまでと同じ様に地域化された文字を取得できます:
System.out.println(myBundle.format("collectedCoins", 0)); System.out.println(myBundle.format("collectedCoins", 1)); System.out.println(myBundle.format("collectedCoins", 32)); System.out.println(myBundle.format("collectedCoins", 117));
すると出力結果は以下のようになります:
You collected no coins along the path.
You collected one coin along the path.
You collected 32 coins along the path.
You collected hundreds of coins along the path.
注目すべき点は、このchoice 形式では{0,choice,...}
内の{0,number,integer}
のように入れ子になった部分も適切に処理できるということです。
前にも言ったように、LibGdx によって提供されるI18N システムはクロスプラットフォームです。 ですが、 GWT 環境に関して言うといくつか制限があります。 具体的には:
java.text.MessageFormat
のフォーマット構文が完全にサポートされている訳ではありません。
単純な構文のみを使用するよう厳守する必要があります。
単純な構文とは、例えば{index}
といったような、index情報のみで構成されたフォーマットのことです。
Formatのtype とstyleはサポートされておらず、使用できません; 使用した場合はIllegalArgumentException
の例外が発生します。format
メソッドに指定した引数は toString
メソッドによって文字列に変換され、BundleのLocal情報は無視されます。
あなたのアプリケーションがGWT 環境と非GWT 環境の両方で動作する場合は、アプリケーション開始時に I18NBundle.setSimpleFormat(true)を実行する必要があります。
これを行うことで、以降で呼び出すファクトリメソッド createBundle
で、全ての環境で同じ動作をするBundleを作成するようになります。
これを行わないと、非GWT環境のイタリアLocalでは翻訳された文字3,14
を取得し(コンマと丸め込みがされています)、GWT 環境の同じlocale では翻訳されていない文字3.1415927
を取得することになります。
反対に simpleFormat
メソッドにtrue
を設定した場合は、どのLocalのどの環境でも翻訳されていない文字 3.1415927
を取得することになります。
もちろん、アプリケーションで複数の bundleを使用することも可能です。例えば、ゲームのステージごとに違う bundle を使うことをお勧めします。 bundleを複数使うといくつかのメリットがあります。 :