PHP项目Symfony translation与复数

wen PHP项目 1

Symfony Translation 复数处理详解

基本配置

Symfony 使用 ICU MessageFormat 来处理复数规则,支持多种语言的复杂复数形式。

PHP项目Symfony translation与复数

消息格式

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'])
            );
        }
    }
}

注意事项

  1. 复数规则差异:不同语言复数规则不同

    • 英语:2种 (singular/plural)
    • 中文:1种
    • 俄语:3种
    • 阿拉伯语:6种
  2. 性能考虑:频繁的复数翻译可能影响性能,考虑缓存

  3. 向后兼容transChoice() 在 Symfony 5+ 中被标记为废弃

  4. 格式选择:推荐使用 ICU 格式而不是 pipe 分隔格式

调试工具

# 查看所有可用的翻译
php bin/console debug:translation en
# 检查特定翻译
php bin/console translation:extract en --prefix="messages"

这样你就能在 Symfony 项目中正确处理各种语言的复数形式了!

抱歉,评论功能暂时关闭!