Symfony Translation 复数处理详解
基本配置
Symfony 使用 ICU MessageFormat 来处理复数规则,支持多种语言的复杂复数形式。

消息格式
YAML 格式:
# translations/messages.en.yaml
apples: '{count} apple|{count} apples'
# translations/messages.pl.yaml (波兰语有3种复数形式)
apples: '{count} jabłko|{count} jabłka|{count} jabłek'
# translations/messages.ar.yaml (阿拉伯语有6种复数形式)
apples: '{count} تفاحة|{count} تفاحتين|{count} تفاحات|{count} تفاحة|{count} تفاحة|{count} تفاحة'
使用方式
在模板中:
{# 直接使用 #}
{{ 'apples'|trans({'count': 5}) }}
{# 带参数 #}
{% set count = 3 %}
{{ '{count} item|{count} items'|transchoice(count) }}
在控制器中:
// 使用 TranslatorInterface
use Symfony\Contracts\Translation\TranslatorInterface;
class ProductController extends AbstractController
{
public function index(TranslatorInterface $translator)
{
$translated = $translator->trans(
'{count} item|{count} items',
['count' => 5],
'messages'
);
// 或使用 transChoice (已废弃,但旧项目可能使用)
$translated = $translator->transChoice(
'{0} No items|{1} One item|]1,Inf[ %count% items',
5,
['%count%' => 5]
);
}
}
ICU 消息格式高级用法
# translations/messages.en.yaml
order:
status: |
{status, select,
pending {Order is pending}
confirmed {Order confirmed}
shipped {Order shipped}
delivered {Order delivered}
other {Unknown status}
}
product:
count: |
{count, plural,
=0 {No products}
=1 {One product}
one {# product}
few {# products}
many {# products}
other {# products}
}
# 组合使用
summary: |
{gender, select,
male {{count, plural, =0{He has no items} one{He has # item} other{He has # items}}}
female {{count, plural, =0{She has no items} one{She has # item} other{She has # items}}}
other {{count, plural, =0{They have no items} one{They have # item} other{They have # items}}}
}
XLIFF 格式
<!-- translations/messages.en.xlf -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="en" datatype="plaintext">
<body>
<trans-unit id="1">
<source>apples</source>
<target>{count} apple|{count} apples</target>
<note>Apple count</note>
</trans-unit>
</body>
</file>
</xliff>
自定义复数规则
创建自定义复数解析器:
// src/Translation/PluralizationRules.php
namespace App\Translation;
use Symfony\Component\Translation\PluralizationRules;
class CustomPluralizationRules
{
public static function getRule($number, $locale)
{
// 为特定语言自定义复数规则
if ('zh' === $locale) {
// 中文通常没有复数变化
return 0;
}
return PluralizationRules::get($number, $locale);
}
}
在配置中注册:
# config/packages/translation.yaml
framework:
translator:
paths:
- '%kernel.project_dir%/translations'
# 自定义复数规则
pluralization:
rules: 'App\Translation\CustomPluralizationRules::getRule'
最佳实践
# translations/messages.en.yaml
# ✅ 好的做法:明确指定所有可能的值
items: |
{count, plural,
=0 {No items}
=1 {One item}
=2 {Two items}
other {# items}
}
# ✅ 使用数字范围
age: |
{age, select,
child {You are a child}
teen {You are a teenager}
adult {You are an adult}
senior {You are a senior}
other {You are {age} years old}
}
# ❌ 避免:依赖默认值
items: '{count} items|{count} items' # 同一字符串两次
# ✅ 正确:使用 plural 而不是 manual pipe
items: |
{count, plural,
one {# item}
other {# items}
}
测试复数翻译
// tests/Translation/PluralTranslationTest.php
namespace App\Tests\Translation;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class PluralTranslationTest extends KernelTestCase
{
public function testPluralTranslations()
{
self::bootKernel();
$translator = self::$container->get('translator');
$testCases = [
['count' => 0, 'expected' => 'No items'],
['count' => 1, 'expected' => 'One item'],
['count' => 2, 'expected' => '2 items'],
['count' => 5, 'expected' => '5 items'],
];
foreach ($testCases as $testCase) {
$translated = $translator->trans(
'{count, plural, =0{No items} =1{One item} other{# items}}',
['count' => $testCase['count']]
);
$this->assertEquals(
$testCase['expected'],
$translated,
sprintf('Translation for count %d failed', $testCase['count'])
);
}
}
}
注意事项
-
复数规则差异:不同语言复数规则不同
- 英语:2种 (singular/plural)
- 中文:1种
- 俄语:3种
- 阿拉伯语:6种
-
性能考虑:频繁的复数翻译可能影响性能,考虑缓存
-
向后兼容:
transChoice()在 Symfony 5+ 中被标记为废弃 -
格式选择:推荐使用 ICU 格式而不是 pipe 分隔格式
调试工具
# 查看所有可用的翻译 php bin/console debug:translation en # 检查特定翻译 php bin/console translation:extract en --prefix="messages"
这样你就能在 Symfony 项目中正确处理各种语言的复数形式了!