如何使用 ICU MessageFormat 翻译消息
应用中的消息(即字符串)几乎从不是完全静态的。它们包含变量或其他复杂的逻辑,例如复数化。为了处理这种情况,Translator 组件 支持 ICU MessageFormat 语法。
提示
你可以在这个 在线编辑器 中测试 ICU MessageFormatter 的示例。
使用 ICU MessageFormat
为了使用 ICU Message Format,消息域必须以后缀 +intl-icu
结尾
普通文件名 | ICU Message Format 文件名 |
---|---|
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
,其语句包含此选择的“cases”。此函数应用于 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
处理为变量。
提示
虽然只将 her
、his
或 their
放在 switch 语句中可能看起来更合乎逻辑,但最好在消息的最外层结构中使用“复杂参数”。这样,字符串对于翻译人员来说更易读,正如你在 multiple
case 中看到的那样,句子的其他部分可能会受到变量的影响。
提示
可以直接在代码中翻译 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
vs 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
函数中可能的 cases 对于每种语言也不同。例如,俄语有 one
、few
、many
和 other
,而英语只有 one
和 other
。可能的 cases 的完整列表可以在 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]);
此功能的可能 cases 也显示在 Unicode 的 语言复数规则 文档中。
日期和时间
日期和时间函数允许你使用 IntlDateFormatter 以目标区域设置格式化日期
1 2
# translations/messages+intl-icu.en.yaml
published_at: 'Published at {publication_date, date} - {publication_date, time, short}'
time
和 date
函数的“function statement”可以是 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]);