支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
设想你要把字符串 "Symfony is great" 翻译成法语:
1 2 3 4 5 6 7 8 9 10 | use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array(
'Symfony is great!' => 'J\'aime Symfony!',
), 'fr_FR');
var_dump($translator->trans('Symfony is great!')); |
本例中,信息(message) "Symfony is great!" 将被翻译为设置在构造器中的 (fr_FR
) locale,如果该条信息存在于目录中的话。
有时,一条信息包含着需要被翻译的变量:
1 2 3 4 | // ...
$translated = $translator->trans('Hello '.$name);
var_dump($translated); |
然而,为这个字符串创建翻译却是可能的,因为translator会尝试寻找确切的信息,包括那个变量部分 (如 "Hello Ryan" 或 "Hello Fabien")。不必为 $name
变量编写每一种可能的翻译,你可以用 "占位符" 来替换此变量:
Symfony将查找原始信息(Hello %name%
)的翻译,然后用它们的值来替换占位符。创建翻译时仍和以前一样:
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Hello %name%</source>
<target>Bonjour %name%</target>
</trans-unit>
</body>
</file>
</xliff> |
1 2 3 | return array(
'Hello %name%' => 'Bonjour %name%',
); |
1 | 'Hello %name%': Bonjour %name% |
Note
占位符可以是任何形式的,因为完整信息被PHP的 strtr function
重新构造了。但是推荐 %...%
形式,可以避免在Twig中出现问题。
如你所见,创建翻译是一个“两步”流程:
Translator
来处理” 的待译信息。第二步在创建信息目录(message catalogs)时被完成,信息目录中可以定义任意数量的不同locale的翻译(内容)。
创建翻译文件的过程,是创建 "本地化(localization)" 的重要一环 (常被简称为 L10n)。翻译文件包含了 id-translation 键值对(键是翻译id,值是翻译内容)用于给定的domain和locale。source是每一条翻译信息的识别符,在你的程序的主力locale中它可以是message本身 (如 "Symfony is great") 或者是一个唯一的识别符 (如 symfony.great
- 参考下面的灰色区域文字)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="symfony_is_great">
<source>Symfony is great</source>
<target>J'aime Symfony</target>
</trans-unit>
<trans-unit id="symfony.great">
<source>symfony.great</source>
<target>J'aime Symfony</target>
</trans-unit>
</body>
</file>
</xliff> |
1 2 3 4 | return array(
'Symfony is great' => 'J\'aime Symfony',
'symfony.great' => 'J\'aime Symfony',
); |
1 2 | Symfony is great: J'aime Symfony
symfony.great: J'aime Symfony |
翻译信息(message)的复数处理,是个困难的话题,因为规则可能相当复杂。例如,这里有一个俄语复数规则的数学呈现:
1 2 3 4 5 6 7 8 9 | (($number % 10 == 1) && ($number % 100 != 11))
? 0
: ((($number % 10 >= 2)
&& ($number % 10 <= 4)
&& (($number % 100 < 10)
|| ($number % 100 >= 20)))
? 1
: 2
); |
你已看到,在俄语中,你可以有三种不同的复数形式,每一种的索引是0,1,2。对于每种形式,复数形态不同,所以翻译也是不同的。
当翻译因复数而有不同的形式时,你可以通过一个由pipe(|
)分隔的字符串,来提供出全部的形式。
1 | 'There is one apple|There are %count% apples' |
要翻译复数信息,使用 transChoice()
方法:
1 2 3 4 5 | $translator->transChoice(
'There is one apple|There are %count% apples',
10,
array('%count%' => 10)
); |
第二个参数 (本例是 10
) 是被描述对象的 number(数量),用于决定要使用哪种翻译,同时还要装载 %count%
占位符
根据给定的数字,translator 会选择正确的复数形式。在英文中,当仅有一个物体时,多数单词只有一个单数形式,而其复数形式可以是所有其他数字 (0, 2, 3...)。因此,如果 count
是 1
,translator 将使用第一个字符串 (There is one apple
) 作为翻译。否则它就使用 There are %count% apples
。
这里有一个法语翻译:
1 | 'Il y a %count% pomme|Il y a %count% pommes' |
就算字符串看起来(和英语的)很相似 (它由两个通过pipe分隔的子串构成),法语的规则却不同: 第一种形式 (没有复数) 用于当 count
是 0
或 1
。因此,当 count
是 0
或 1
时,translator将自动使用第一个字符串 (Il y a %count% pomme
)。
每一种locale有其自己的规则集,其中某些甚至有高达六种不同的复数形式,配合着背后的复杂规则,即哪个数字映射的是哪种复数形式。对于英语和法语来说,规则十分简单,但对于俄语,你可能不太想去搞清哪个规则对应哪个字符串。要帮助translators,你可以可选地对每个字符串“打标签”:
1 2 3 | 'one: There is one apple|some: There are %count% apples'
'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes' |
标签(tag)真的是只能对translator进行提示而不会影响到“用于决定使用哪种复数形式”的规则。标签可以是任何描述性的字符串,它以冒号(:
)结尾。标签并不需要和原始的待译信息相同。
Note
由于tag是可选的,translator并不使用它们(translator只根据tag在字符串中的位置来获取字符串)
对message(待译信息)进行复数处理的最简单方式就是让translator使用内部逻辑,基于给定的数字,来决定要使用哪个字符串。有时,你会需要更多的控制权,或者希望在个别情况下(例如对于0
或负值的count)使用不同的翻译。对于这类场景,你可以使用显式的算术区间:
1 | '{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples' |
区间(interval)遵循的是 ISO 31-11 注释。上面的字符串指定了四个区间: 确切的 0
, 确切的 1
, 2-19
, 和 20
以及更高。
你也可以显式地把算术规则(math rules)和标准规则混合起来指定。本例中,如果count没有匹配到一个特定区间,标准规则(standard rules)将在移除显式规则之后生效:
1 | '{0} There are no apples|[20,Inf[ There are many apples|There is one apple|a_few: There are %count% apples' |
例如,对于 1
个苹果,标准规则 There is one apple
将被使用。对于 2-19
个苹果,第二个标准规则 There are %count% apples
将被使用。
一个 Interval
可以呈现出无限的数字组合:
1 | {1,2,3,4} |
或者是两个数字之外的数字:
1 2 | [1, +Inf[
]-1,2[ |
左边的分隔符可以是 [
(inclusive/包括) 或 ]
(exclusive/排除)。 右边的分隔符可以是 [
(exclusive/排除) 或 ]
(inclusive/包括)。除去数字,你还可以使用 -Inf
和 +Inf
来表达无限。
当翻译一条信息(message)时,translator使用的是特定的locale或者在必要时使用 fallback
locale。你可以手动指定翻译时所要用到的locale:
如果你想使用程序之外的相同翻译目录(比如使用客户端的翻译源),那么取出原生的(raw)翻译信息是可能的。只要指定所需的locale即可:
1 2 3 4 5 | $catalogue = $translator->getCatalogue('fr_FR');
$messages = $catalogue->all();
while ($catalogue = $catalogue->getFallbackCatalogue()) {
$messages = array_replace_recursive($catalogue->all(), $messages);
} |
messages
变量将具备如下结构:
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。