How to Translate Messages using the ICU MessageFormat

Messages (i.e. strings) in applications are almost never completely static. They contain variables or other complex logic like pluralization. To handle this, the Translator component supports the ICU MessageFormat syntax.

アプリケーション内のメッセージ (文字列) が完全に静的であることはほとんどありません。変数や複数形などの複雑なロジックが含まれています。これを処理するために、Translator コンポーネントは ICU MessageFormat 構文をサポートしています。

Tip

ヒント

You can test out examples of the ICU MessageFormatter in this online editor.

このオンライン エディターで ICU MessageFormatter の例を試すことができます。

Using the ICU Message Format

In order to use the ICU Message Format, the message domain has to be suffixed with +intl-icu:

ICU メッセージ フォーマットを使用するには、メッセージ ドメインの末尾に +intl-icu を付ける必要があります。
Normal file name ICU Message Format filename
messages.en.yaml messages+intl-icu.en.yaml
messages.fr_FR.xlf messages+intl-icu.fr_FR.xlf
admin.en.yaml admin+intl-icu.en.yaml

All messages in this file will now be processed by the MessageFormatter during translation.

このファイル内のすべてのメッセージは、翻訳中に MessageFormatter によって処理されます。

Message Placeholders

The basic usage of the MessageFormat allows you to use placeholders (called arguments in ICU MessageFormat) in your messages:

MessageFormat の基本的な使用法では、メッセージでプレースホルダー (ICU MessageFormat では引数と呼ばれる) を使用できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
# translations/messages+intl-icu.en.yaml
say_hello: 'Hello {name}!'

Caution

注意

In the previous translation format, placeholders were often wrapped in % (e.g. %name%). This % character is no longer valid with the ICU MessageFormat syntax, so you must rename your parameters if you are upgrading from the previous format.

以前の翻訳形式では、プレースホルダーはしばしば % で囲まれていました (例: %name%)。この % 文字は ICUMessageFormat 構文では無効になっているため、以前の形式からアップグレードする場合は、パラメーターの名前を変更する必要があります。

Everything within the curly braces ({...}) is processed by the formatter and replaced by its placeholder:

中括弧 ({...}) 内のすべてがフォーマッタによって処理され、そのプレースホルダーに置き換えられます。
1
2
3
4
5
// prints "Hello Fabien!"
echo $translator->trans('say_hello', ['name' => 'Fabien']);

// prints "Hello Symfony!"
echo $translator->trans('say_hello', ['name' => 'Symfony']);

Selecting Different Messages Based on a Condition

The curly brace syntax allows to "modify" the output of the variable. One of these functions is the select function. It acts like PHP's switch statement and allows you to use different strings based on the value of the variable. A typical usage of this is gender:

中括弧構文により、変数の出力を「変更」できます。これらの関数の 1 つが select 関数です。 PHP の switch ステートメントのように機能し、変数の値に基づいて異なる文字列を使用できます。これの非典型的な使用法は性別です。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# translations/messages+intl-icu.en.yaml

# the 'other' key is required, and is selected if no other case matches
invitation_title: >-
    {organizer_gender, select,
        female   {{organizer_name} has invited you to her party!}
        male     {{organizer_name} has invited you to his party!}
        multiple {{organizer_name} have invited you to their party!}
        other    {{organizer_name} has invited you to their party!}
    }

This might look very complex. The basic syntax for all functions is {variable_name, function_name, function_statement} (where, as you see later, function_statement is optional for some functions). In this case, the function name is select and its statement contains the "cases" of this select. This function is applied over the organizer_gender variable:

これは非常に複雑に見えるかもしれません。すべての関数の基本的な構文は {variable_name, function_name, function_statement} です (後でわかるように、function_statement は一部の関数ではオプションです)。この場合、関数名はselectで、そのステートメントにはこのselectの「ケース」が含まれています。この関数は、organizer_gender 変数に適用されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// prints "Ryan has invited you to his party!"
echo $translator->trans('invitation_title', [
    'organizer_name' => 'Ryan',
    'organizer_gender' => 'male',
]);

// prints "John & Jane have invited you to their party!"
echo $translator->trans('invitation_title', [
    'organizer_name' => 'John & Jane',
    'organizer_gender' => 'multiple',
]);

// prints "ACME Company has invited you to their party!"
echo $translator->trans('invitation_title', [
    'organizer_name' => 'ACME Company',
    'organizer_gender' => 'not_applicable',
]);

The {...} syntax alternates between "literal" and "code" mode. This allows you to use literal text in the select statements:

{...} 構文は、「リテラル」モードと「コード」モードを交互に切り替えます。これにより、select ステートメントでリテラル テキストを使用できます。
  1. The first {organizer_gender, select, ...} block starts the "code" mode, which means organizer_gender is processed as a variable.
    最初の {organizer_gender, select, ...} ブロックは「コード」モードを開始します。これは、organizer_gender が変数として処理されることを意味します。
  2. The inner {... has invited you to her party!} block brings you back in "literal" mode, meaning the text is not processed.
    内側の {... はあなたを彼女のパーティーに招待しました!} ブロックは、テキストが処理されないことを意味する「リテラル」モードに戻ります。
  3. Inside this block, {organizer_name} starts "code" mode again, allowing organizer_name to be processed as a variable.
    このブロック内で、{organizer_name} は再び「コード」モードを開始し、organizer_name を変数として処理できるようにします。

Tip

ヒント

While it might seem more logical to only put her, his or their in the switch statement, it is better to use "complex arguments" at the outermost structure of the message. The strings are in this way better readable for translators and, as you can see in the multiple case, other parts of the sentence might be influenced by the variables.

彼女、彼、または彼らだけを switch ステートメントに入れる方がより論理的に思えるかもしれませんが、メッセージの最も外側の構造で「複雑な引数」を使用する方が適切です。このようにして、文字列は翻訳者にとってより読みやすくなり、複数のケースでわかるように、文の他の部分が変数の影響を受ける可能性があります。

Tip

ヒント

It's possible to translate ICU MessageFormat messages directly in code, without having to define them in any file:

ICU MessageFormat メッセージは、ファイルで定義しなくてもコードで直接変換できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$invitation = '{organizer_gender, select,
    female   {{organizer_name} has invited you to her party!}
    male     {{organizer_name} has invited you to his party!}
    multiple {{organizer_name} have invited you to their party!}
    other    {{organizer_name} has invited you to their party!}
}';

// prints "Ryan has invited you to his party!"
echo $translator->trans(
    $invitation,
    [
        'organizer_name' => 'Ryan',
        'organizer_gender' => 'male',
    ],
    // if you prefer, the required "+intl-icu" suffix is also defined as a constant:
    // Symfony\Component\Translation\MessageCatalogueInterface::INTL_DOMAIN_SUFFIX
    'messages+intl-icu'
);

Pluralization

Another interesting function is plural. It allows you to handle pluralization in your messages (e.g. There are 3 apples vs There is one apple). The function looks very similar to the select function:

もう 1 つの興味深い機能は複数形です。メッセージで複数形を処理できます (例: There are 3 apples vsThere is one apple)。この関数は、select 関数と非常によく似ています。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
# translations/messages+intl-icu.en.yaml
num_of_apples: >-
    {apples, plural,
        =0    {There are no apples}
        one   {There is one apple...}
        other {There are # apples!}
    }

Pluralization rules are actually quite complex and differ for each language. For instance, Russian uses different plural forms for numbers ending with 1; numbers ending with 2, 3 or 4; numbers ending with 5, 6, 7, 8 or 9; and even some exceptions to this!

複数形化のルールは実際には非常に複雑で、言語ごとに異なります。 5、6、7、8、または 9 で終わる数字。これには例外もあります。

In order to properly translate this, the possible cases in the plural function are also different for each language. For instance, Russian has one, few, many and other, while English has only one and other. The full list of possible cases can be found in Unicode's Language Plural Rules document. By prefixing with =, you can match exact values (like 0 in the above example).

これを適切に翻訳するために、複数機能の可能なケースも言語ごとに異なります。たとえば、ロシア語には「1」、「少数」、「多数」、「その他」がありますが、英語には「1」と「その他」しかありません。考えられるケースの完全なリストは、Unicode の言語複数規則ドキュメントにあります。 = を前に付けることで、正確な値 (上記の例の 0 など) と一致させることができます。

Usage of this string is the same as with variables and select:

この文字列の使用法は、変数と選択の場合と同じです。
1
2
3
4
5
// prints "There is one apple..."
echo $translator->trans('num_of_apples', ['apples' => 1]);

// prints "There are 23 apples!"
echo $translator->trans('num_of_apples', ['apples' => 23]);

Note

ノート

You can also set an offset variable to determine whether the pluralization should be offset (e.g. in sentences like You and # other people / You and # other person).

オフセット変数を設定して、複数形化をオフセットするかどうかを決定することもできます (たとえば、You and # other people/ You and # other person のような文で)。

Tip

ヒント

When combining the select and plural functions, try to still have select as outermost function:

select 関数と複数関数を組み合わせるときは、select を最も外側の関数として使用するようにしてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{gender_of_host, select,
    female {{num_guests, plural, offset:1
        =0    {{host} does not give a party.}
        =1    {{host} invites {guest} to her party.}
        =2    {{host} invites {guest} and one other person to her party.}
        other {{host} invites {guest} and # other people to her party.}
    }}
    male {{num_guests, plural, offset:1
        =0    {{host} does not give a party.}
        =1    {{host} invites {guest} to his party.}
        =2    {{host} invites {guest} and one other person to his party.}
        other {{host} invites {guest} and # other people to his party.}
    }}
    other {{num_guests, plural, offset:1
        =0    {{host} does not give a party.}
        =1    {{host} invites {guest} to their party.}
        =2    {{host} invites {guest} and one other person to their party.}
        other {{host} invites {guest} and # other people to their party.}
    }}
}
メッセージでの範囲の使用

The pluralization in the legacy Symfony syntax could be used with custom ranges (e.g. have different messages for 0-12, 12-40 and 40+). The ICU message format does not have this feature. Instead, this logic should be moved to PHP code:

従来の Symfony 構文の複数形は、カスタム範囲で使用できます (たとえば、0-12、12-40、および 40+ に対して異なるメッセージを持ちます)。 ICU メッセージ形式にはこの機能がありません。代わりに、このロジックを PHP コードに移動する必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
// Instead of
$message = $translator->trans('balance_message', $balance);
// with a message like:
// ]-Inf,0]Oops! I'm down|]0,1000]I still have money|]1000,Inf]I have lots of money

// use three different messages for each range:
if ($balance < 0) {
    $message = $translator->trans('no_money_message');
} elseif ($balance < 1000) {
    $message = $translator->trans('some_money_message');
} else {
    $message = $translator->trans('lots_of_money_message');
}

Additional Placeholder Functions

Besides these, the ICU MessageFormat comes with a couple other interesting functions.

これらに加えて、ICU MessageFormat には他にも興味深い機能がいくつかあります。

Ordinal

Similar to plural, selectordinal allows you to use numbers as ordinal scale:

複数形と同様に、selectordinal を使用すると、数値を序数として使用できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
# translations/messages+intl-icu.en.yaml
finish_place: >-
    You finished {place, selectordinal,
        one   {#st}
        two   {#nd}
        few   {#rd}
        other {#th}
    }!

# when only formatting the number as ordinal (like above), you can also
# use the `ordinal` function:
finish_place: You finished {place, ordinal}!
1
2
3
4
5
6
7
8
// prints "You finished 1st!"
echo $translator->trans('finish_place', ['place' => 1]);

// prints "You finished 9th!"
echo $translator->trans('finish_place', ['place' => 9]);

// prints "You finished 23rd!"
echo $translator->trans('finish_place', ['place' => 23]);

The possible cases for this are also shown in Unicode's Language Plural Rules document.

この可能性のあるケースは、Unicode の Language Plural Rules ドキュメントにも示されています。

Date and Time

The date and time function allows you to format dates in the target locale using the IntlDateFormatter:

日付と時刻関数を使用すると、IntlDateFormatter を使用してターゲット ロケールで日付をフォーマットできます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
# translations/messages+intl-icu.en.yaml
published_at: 'Published at {publication_date, date} - {publication_date, time, short}'

The "function statement" for the time and date functions can be one of short, medium, long or full, which correspond to the constants defined by the IntlDateFormatter class:

時刻関数と日付関数の「関数ステートメント」は、IntlDateFormatter クラスで定義された定数に対応する short、medium、long、または full のいずれかです。
1
2
// prints "Published at Jan 25, 2019 - 11:30 AM"
echo $translator->trans('published_at', ['publication_date' => new \DateTime('2019-01-25 11:30:00')]);

Numbers

The number formatter allows you to format numbers using Intl's NumberFormatter:

数値フォーマッタを使用すると、Intl の NumberFormatter を使用して数値をフォーマットできます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
# translations/messages+intl-icu.en.yaml
progress: '{progress, number, percent} of the work is done'
value_of_object: 'This artifact is worth {value, number, currency}'
1
2
3
4
5
6
7
8
9
// prints "82% of the work is done"
echo $translator->trans('progress', ['progress' => 0.82]);
// prints "100% of the work is done"
echo $translator->trans('progress', ['progress' => 1]);

// prints "This artifact is worth $9,988,776.65"
// if we would translate this to i.e. French, the value would be shown as
// "9 988 776,65 €"
echo $translator->trans('value_of_object', ['value' => 9988776.65]);