如何使用 ICU MessageFormat 翻譯訊息
應用程式中的訊息 (即字串) 幾乎從來都不是完全靜態的。它們包含變數或其他複雜邏輯,例如複數化。為了處理這個問題,Translator 组件 支援 ICU MessageFormat 語法。
提示
您可以在這個 線上編輯器 中測試 ICU MessageFormatter 的範例。
使用 ICU 訊息格式
為了使用 ICU 訊息格式,訊息網域必須加上後綴 +intl-icu
一般檔案名稱 | ICU 訊息格式檔案名稱 |
---|---|
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 |
這個檔案中的所有訊息現在都將由 MessageFormatter 在翻譯期間處理。
訊息預留位置
MessageFormat 的基本用法允許您在訊息中使用預留位置 (在 ICU MessageFormat 中稱為引數)
1 2
# translations/messages+intl-icu.en.yaml
say_hello: 'Hello {name}!'
警告
在先前的翻譯格式中,預留位置通常包在 %
中 (例如 %name%
)。這個 %
字元在使用 ICU MessageFormat 語法時不再有效,因此如果您是從先前的格式升級,則必須重新命名您的參數。
大括號 ({...}
) 內的所有內容都由格式器處理,並由其預留位置取代
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']);
根據條件選取不同訊息
大括號語法允許「修改」變數的輸出。其中一個函數是 select
函數。它的作用類似於 PHP 的 switch 語句,並允許您根據變數的值使用不同的字串。一個典型的用法是性別
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!}
}
這看起來可能非常複雜。所有函數的基本語法是 {variable_name, function_name, function_statement}
(您稍後會看到,function_statement
對於某些函數是可選的)。在這種情況下,函數名稱是 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',
]);
{...}
語法在「文字」和「程式碼」模式之間切換。這允許您在 select 語句中使用文字
- 第一個
{organizer_gender, select, ...}
區塊啟動「程式碼」模式,這表示organizer_gender
會被當作變數處理。 - 內部的
{... has invited you to her party!}
區塊會將您帶回「文字」模式,表示文字不會被處理。 - 在這個區塊內,
{organizer_name}
再次啟動「程式碼」模式,允許organizer_name
被當作變數處理。
提示
雖然只在 switch 語句中放入 her
、his
或 their
可能看起來更符合邏輯,但在訊息的最外層結構中使用「複雜引數」會更好。這樣一來,字串對於翻譯人員來說更易於閱讀,而且如您在 multiple
案例中看到的,句子的其他部分可能會受到變數的影響。
提示
可以直接在程式碼中翻譯 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'
);
複數化
另一個有趣的函數是 plural
。它允許您處理訊息中的複數化 (例如 There are 3 apples
與 There is one apple
)。這個函數看起來與 select
函數非常相似
1 2 3 4 5 6 7
# translations/messages+intl-icu.en.yaml
num_of_apples: >-
{apples, plural,
=0 {There are no apples}
=1 {There is one apple...}
other {There are # apples!}
}
複數化規則實際上非常複雜,並且因語言而異。例如,俄語對於以 1 結尾的數字;以 2、3 或 4 結尾的數字;以 5、6、7、8 或 9 結尾的數字;甚至對此規則還有一些例外情況,使用不同的複數形式!
為了正確翻譯這一點,plural
函數中可能的案例也因語言而異。例如,俄語有 one
、few
、many
和 other
,而英語只有 one
和 other
。可能的案例完整列表可以在 Unicode 的 語言複數規則 文件中找到。透過加上 =
前綴,您可以比對確切的值 (如上述範例中的 0
)。
這個字串的用法與變數和 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]);
注意
您也可以設定 offset
變數,以判斷複數化是否應該偏移 (例如,在句子中,如 You and # other people
/ You and # other person
)。
提示
當組合 select
和 plural
函數時,請嘗試仍然讓 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.}
}}
}
其他預留位置函數
除了這些之外,ICU MessageFormat 還提供了一些其他有趣的函數。
序數
與 plural
類似,selectordinal
允許您將數字用作序數尺度
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]);
此功能的可能案例也顯示在 Unicode 的 語言複數規則 文件中。
日期和時間
日期和時間函數允許您使用 IntlDateFormatter 以目標地區設定格式化日期。
1 2
# translations/messages+intl-icu.en.yaml
published_at: 'Published at {publication_date, date} - {publication_date, time, short}'
time
和 date
函數的「函數語句」可以是 short
、medium
、long
或 full
其中之一,它們對應於 IntlDateFormatter 類別定義的常數
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')]);
數字
number
格式器允許您使用 Intl 的 NumberFormatter 格式化數字。
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]);