サイトのトップへ戻る

libGDX ドキュメント 日本語訳

国際化と地域化



概要

専門的に言うならば、国際化というのは、コードを変更することなく様々な言語や地域に対応できるようにするためにソフトウェアを設計することです。 地域化というのは 、地域固有のコンポーネントと翻訳文を追加し、国際化対応ソフトウェアを特定の言語や地域向けに調節することです。 文字数が長くなってしまうので、国際化(internationalization)はi18n 、地域化(localization)はl10n としばしば略されます。 internationalizationという単語は最初にi、最後にnの文字があり、その間に18個の文字があるためこのように略されています。 localizationという単語は最初にl、最後にnの文字があり、その間に10個の文字があるためこのように略されています。

LibGDXでは、 I18NBundle クラス を使って 局所性鋭敏化された文字の保存と取得を行います。 このBundleを使うことでアプリケーションに様々な翻訳を簡単に適用できます。



Properties ファイルを作成する

各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ファイル内を検索します。



Bundleを作成する

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を使用するのがベストです。



Message Format

既に見てきたように、properties ファイル内の文字にはパラメータを含めることができます。 これらの文字は一般的にはパターンと呼ばれ、 java.text.MessageFormatAPIで指定された構文に従います。 要するに、パターンには何も含めないか、もしくは {index, type, style}形式の複数のフォーマットを含めることができます。 type とstyleについては設定するかどうか任意です。 全ての機能について学ぶにはMessageFormat クラスの公式JavaDocを参照してください。

重要: 既定のエスケープ処理のルールだと翻訳する人が混乱してしまうので、LibGDX では MessageFormatの構文について一点だけ変更を行っています。 文字の中で{リテラルを使用したい場合、通常はシングルクォーテーション(')を使ってエスケープ処理をする必要があります。 しかし LibGDX では、同じものを二回記述するだけです。;例えば、 {{0} はフォーマット要素がなくてもリテラル文字 {0}として解釈されます。 結果としてシングルクォーテーションのエスケープ処理をする必要がなくなります!

フォーマットは地域化されているので注意してください。つまり、数字や日付や時間と行ったタイプのデータは自動的に指定した地域のフォーマットで表示されます。 例えば、イタリア地域の場合だと浮動小数3.143,14になります; 小数点がカンマに置き換わっているのが分かるでしょう。 ( GWTを使用している場合、これについていくつか制限 があります。)

java.util.Propertiesと違い、既定のエンコードは UTF-8となっています。もし何らかの理由で UTF-8 エンコードを使用したくない場合は、 希望のエンコードが指定できるよう多重定義されたcreateBundleメソッドのうち、どれかを実行してください。



Plural Forms

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} のように入れ子になった部分も適切に処理できるということです。



GWT での制限と互換性

前にも言ったように、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 を使うことをお勧めします。 bundleを複数使うといくつかのメリットがあります。 :

  • コードの可読性が高くなり、管理が簡単になります。
  • Bundleが大きくなるのを避けられます。Bundleが大きくなるとメモリへの読み込みに時間がかかってしまいます。
  • 必要な時に必要なBundleのみを読み込むことで、メモリ使用量を減らすことができます。