1. 序章

このチュートリアルでは、Localeに基づいてメッセージのローカライズとフォーマットを行う方法を検討します。

JavaのMessageFormatとサードパーティライブラリICUの両方を使用します。

2. ローカリゼーションのユースケース

私たちのアプリケーションが世界中から幅広いユーザーを獲得する場合、当然、ユーザーの好みに基づいてさまざまなメッセージを表示したい場合があります

最初のそして最も重要な側面は、ユーザーが話す言語です。 その他には、通貨、数値、日付の形式が含まれる場合があります。 最後に重要なのは、文化的な好みです。ある国のユーザーに受け入れられるものは、他の国のユーザーには耐えられない場合があります。

電子メールクライアントがあり、新しいメッセージが到着したときに通知を表示したいとします。

このようなメッセージの簡単な例は次のとおりです。

Alice has sent you a message.

英語を話すユーザーにとっては問題ありませんが、英語を話さないユーザーはそれほど幸せではないかもしれません。 たとえば、フランス語を話すユーザーは、次のメッセージを表示したいと考えています。

Alice vous a envoyé un message.

ポーランドの人々はこれを見て喜ぶでしょうが:

Alice wysłała ci wiadomość.

アリスが1つのメッセージだけでなく、少数のメッセージを送信する場合でも、適切な形式の通知が必要な場合はどうなりますか?

次のように、さまざまな部分を1つの文字列に連結することで、この問題に対処したくなるかもしれません。

String message = "Alice has sent " + quantity + " messages";

アリスだけでなくボブもメッセージを送信する可能性がある場合に通知が必要になると、状況は簡単に制御不能になる可能性があります。

Bob has sent two messages.
Bob a envoyé deux messages.
Bob wysłał dwie wiadomości.

ポーランド語(wysłaławysłał)の場合、動詞がどのように変化するかに注意してください。 これは、banal文字列の連結がメッセージのローカライズに受け入れられることはめったにないという事実を示しています

ご覧のとおり、2つのタイプの問題が発生します。 1つは翻訳に関連し、もう1つはフォーマットに関連しています。 次のセクションでそれらに対処しましょう。

3. メッセージのローカリゼーション

アプリケーションのローカリゼーション(l10n)は、アプリケーションをユーザーの快適さに適合させるプロセスとして定義できます。 内部化、またはi18nという用語も使用されることがあります。

アプリケーションをローカライズするために、まず、ハードコードされたすべてのメッセージをresourcesフォルダーに移動して削除しましょう。

各ファイルには、対応する言語のメッセージとキーと値のペアが含まれている必要があります。 たとえば、ファイル messages_en.properties には、次のペアが含まれている必要があります。

label=Alice has sent you a message.

messages_pl.properties には、次のペアが含まれている必要があります。

label=Alice wysłała ci wiadomość.

同様に、他のファイルはキーlabelに適切な値を割り当てます。 これで、英語版の通知を受け取るために、ResourceBundleを使用できます。

ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.UK);
String message = bundle.getString("label");

変数messageの値は、「アリスからメッセージが送信されました。」になります。

JavaのLocaleクラスには、頻繁に使用される言語と国へのショートカットが含まれています。

ポーランド語の場合、次のように書くことができます。

ResourceBundle bundle
  = ResourceBundle.getBundle("messages", Locale.forLanguageTag("pl-PL"));
String message = bundle.getString("label");

ロケールを指定しない場合、システムはデフォルトのロケールを使用することに注意してください。 この問題の詳細については、記事「Java8での国際化とローカリゼーション」を参照してください。 次に、利用可能な翻訳の中から、システムは現在アクティブなロケールに最も類似しているものを選択します。

リソースファイルにメッセージを配置することは、アプリケーションをよりユーザーフレンドリーにするための良いステップです。 次の理由により、アプリケーション全体の翻訳が容易になります。

  1. 翻訳者は、メッセージを検索するためにアプリケーションを調べる必要はありません。
  2. 翻訳者はフレーズ全体を見ることができ、文脈を把握するのに役立ち、したがってより良い翻訳を促進します
  3. 新しい言語への翻訳の準備ができたら、アプリケーション全体を再コンパイルする必要はありません。

4. メッセージ形式

メッセージをコードから別の場所に移動しましたが、ハードコードされた情報が含まれています。 メッセージの名前と番号を、文法的に正しいままになるようにカスタマイズできると便利です。

プレースホルダーを値で置き換えることにより、文字列テンプレートをレンダリングするプロセスとしてフォーマットを定義できます。

次のセクションでは、メッセージのフォーマットを可能にする2つのソリューションを検討します。

4.1. JavaのMessageFormat

文字列をフォーマットするために、Javaはjava.lang.String多数のフォーマットメソッドを定義します。 ただし、java.text.format.MessageFormatを介してさらに多くのサポートを得ることができます。

説明のために、パターンを作成してMessageFormatインスタンスにフィードしてみましょう。

String pattern = "On {0, date}, {1} sent you "
  + "{2, choice, 0#no messages|1#a message|2#two messages|2<{2, number, integer} messages}.";
MessageFormat formatter = new MessageFormat(pattern, Locale.UK);

パターン文字列には、3つのプレースホルダー用のスロットがあります。

それぞれの値を指定すると、次のようになります。

String message = formatter.format(new Object[] {date, "Alice", 2});

次に、 MessageFormat がテンプレートに入力し、メッセージをレンダリングします。

On 27-Apr-2019, Alice sent you two messages.

4.2. MessageFormat構文

上記の例から、メッセージパターンは次のようになります。

pattern = "On {...}, {..} sent you {...}.";

中括弧{…}であるプレースホルダーが含まれ、必須の引数indexと2つのオプションの引数typeおよびstyleが含まれます。

{index}
{index, type}
{index, type, style}

プレースホルダーのインデックスは、挿入するオブジェクトの配列からの要素の位置に対応します。

存在する場合、typeおよびstyleは次の値をとることがあります。

タイプ スタイル
番号 整数、通貨、パーセント、カスタム形式
日にち ショート、ミディアム、ロング、フル、カスタムフォーマット
時間 ショート、ミディアム、ロング、フル、カスタムフォーマット
選択 カスタムフォーマット

タイプとスタイルの名前は主にそれ自体を物語っていますが、詳細については公式ドキュメントを参照してください。

ただし、詳しく見てみましょう。 カスタムフォーマット 。 

上記の例では、次の形式式を使用しました。

{2, choice, 0#no messages|1#a message|2#two messages|2<{2, number, integer} messages}

一般に、選択スタイルには、垂直バー(またはパイプ)で区切られたオプションの形式があります。

オプション内では、最後のオプションを除いて、一致値kiと文字列viが#で区切られています。 最後のオプションで行ったように、他のパターンを文字列viにネストする場合があることに注意してください。

{2, choice, ...|2<{2, number, integer} messages}

選択タイプは数値ベースのものであるため、数値行を間隔に分割する一致値kiの自然な順序があります。

区間[ki、ki + 1)に属する値 k を指定すると(左端が含まれ、右端が除外されます)、値 vi が選択されています。

選択したスタイルの範囲をさらに詳しく考えてみましょう。 この目的のために、次のパターンを採用します。

pattern = "You''ve got "
  + "{0, choice, 0#no messages|1#a message|2#two messages|2<{0, number, integer} messages}.";

一意のプレースホルダーにさまざまな値を渡します。

n メッセージ
-1、0、0.5 メッセージはありません。
1, 1.5 メッセージが届いています。
2 2つのメッセージがあります。
2.5 2つのメッセージがあります。
5 5つのメッセージがあります。

4.3. 物事をより良くする

そのため、メッセージをフォーマットしています。 ただし、メッセージ自体はハードコーディングされたままです。

前のセクションから、文字列パターンをリソースに抽出する必要があることがわかりました。 懸念を分離するために、formatと呼ばれる別のリソースファイルの束を作成しましょう。

その中で、言語固有のコンテンツを含むlabelというキーを作成します。

たとえば、英語版では、次の文字列を入力します。

label=On {0, date, full} {1} has sent you 
  + {2, choice, 0#nothing|1#a message|2#two messages|2<{2,number,integer} messages}.

メッセージがゼロの場合のため、フランス語バージョンを少し変更する必要があります。

label={0, date, short}, {1}{2, choice, 0# ne|0<} vous a envoyé 
  + {2, choice, 0#aucun message|1#un message|2#deux messages|2<{2,number,integer} messages}.

また、ポーランド語版とイタリア語版でも同様の変更を行う必要があります。

実際、ポーランド語版にはさらに別の問題があります。 ポーランド語(および他の多くの言語)の文法によれば、動詞は主語と性別が一致している必要があります。 選択タイプを使用することでこの問題を解決できますが、別の解決策を検討しましょう。

4.4. ICUのMessageFormat

International Components for Unicode (ICU)ライブラリを使用してみましょう。 文字列をタイトルケースに変換するチュートリアルですでに説明しました。 これは成熟した広く使用されているソリューションであり、さまざまな言語に合わせてアプリケーションをカスタマイズできます。

ここでは、これについて詳しく説明することはしません。 おもちゃのアプリケーションに必要なものに限定します。 最も包括的で更新された情報については、ICUの公式サイトを確認する必要があります。

執筆時点では、 ICU for Java(ICU4J)の最新バージョンは64.2です。 いつものように、それを使い始めるために、私たちはそれを私たちのプロジェクトへの依存関係として追加する必要があります:

<dependency>
    <groupId>com.ibm.icu</groupId>
    <artifactId>icu4j</artifactId>
    <version>64.2</version>
</dependency>

さまざまな言語でさまざまな数のメッセージに対して適切に形成された通知を受け取りたいとします。

N 英語 研磨
0 アリスはあなたにメッセージを送りませんでした。 ボブはあなたにメッセージを送信していません。 Aliceniewisłałaciżadnejwiadomości。 Bobniewisłałciżadnejwiadomości。
1 アリスからメッセージが届きました。 ボブからメッセージが届きました。 Alicewisłałaciwiadomość。 Bobwisłałciwiadomość。
> 1 アリスからN通のメッセージが届きました。 ボブはあなたにN個のメッセージを送りました。 AlicewisłałaciNwiadomości。 BobwisłałciNwiadomości。

まず、ロケール固有のリソースファイルにパターンを作成する必要があります。

ファイルformats.propertiesを再利用して、次の内容のキーlabel-icuを追加しましょう。

label-icu={0} has sent you
  + {2, plural, =0 {no messages} =1 {a message}
  + other {{2, number, integer} messages}}.

これには、3つの要素の配列を渡すことでフィードする3つのプレースホルダーが含まれています。

Object[] data = new Object[] { "Alice", "female", 0 }

英語版では、性別値のプレースホルダーは役に立たないのに対し、ポーランド語版では、次のことがわかります。

label-icu={0} {2, plural, =0 {nie} other {}}
+  {1, select, male {wysłał} female {wysłała} other {wysłało}} 
+  ci {2, plural, =0 {żadnych wiadomości} =1 {wiadomość}
+  other {{2, number, integer} wiadomości}}.

wysłał/wysłała/wysłałoを区別するために使用します。

5. 結論

このチュートリアルでは、アプリケーションのユーザーに示すメッセージをローカライズしてフォーマットする方法を検討しました。

いつものように、このチュートリアルのコードスニペットはGitHubリポジトリにあります。