
Symfony 翻译系统概述
symfony 提供了一个强大而灵活的翻译组件,允许开发者将应用程序的文本内容国际化。在 twig 模板中,我们通常使用 trans 过滤器或 {% trans %} 标签来标记需要翻译的文本,并可以方便地引入变量以实现动态内容。
例如,以下 Twig 代码展示了如何在翻译字符串中包含变量:
{# 使用 trans 标签 #}
{% trans with {'%name%': 'Hans'} %}Hello %name%{% endtrans %}
{# 使用 trans 过滤器 #}
{{ 'Hello filter %name%'|trans({'%name%': 'Hans'}) }}在初始阶段,这些代码通常会按预期工作,输出 Hello Hans 和 Hello filter Hans。然而,当开发者运行 php bin/console translation:update 命令来提取和更新翻译文件时,一个常见的陷阱可能会导致变量替换功能失效。
变量替换失效的根源:占位符语法不匹配
当运行 translation:update 命令(例如 php bin/console translation:update --force en)时,Symfony 会扫描代码中的翻译键,并将其添加到指定的翻译文件中。以 XLIFF 格式为例,生成的翻译文件可能如下所示:
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="en" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="SzX5ua9" resname="Hello %name%">
<source>Hello %name%</source>
<target>__Hello %name%</target> {# 注意这里 #}
</trans-unit>
<trans-unit id="6l2Ebbm" resname="Hello filter %name%">
<source>Hello filter %name%</source>
<target>__Hello filter %name%</target> {# 注意这里 #}
</trans-unit>
</body>
</file>
</xliff>请注意 <target> 标签中的内容。Symfony 的 translation:update 命令会为这些翻译键生成带有前缀(例如 __)的默认目标文本,以便开发者可以轻松识别并填充实际的翻译。然而,它会直接保留源文本中的占位符语法,即 %name%。
问题在于,Symfony 的翻译组件在处理 XLIFF 文件时,通常会利用 ICU MessageFormat。ICU MessageFormat 是一种强大的消息格式化标准,它要求其占位符使用花括号 {},而非百分号 %。因此,当翻译组件尝试解析 <target>__Hello %name%</target> 时,它无法识别 %name% 为一个有效的 ICU 占位符,从而导致变量无法被正确替换,最终在页面上显示为 __Hello %name% 而不是 __Hello Hans。
解决方案:修正翻译文件中的占位符
要解决这个问题,开发者需要手动编辑翻译文件,将 <target> 标签中的占位符语法从 %name% 更改为 ICU MessageFormat 兼容的 {name}。
以下是修正后的 XLIFF 文件示例:
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="en" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="SzX5ua9" resname="Hello %name%">
<source>Hello %name%</source>
<target>__Hello {name}</target> {# 修正后的占位符 #}
</trans-unit>
<trans-unit id="6l2Ebbm" resname="Hello filter %name%">
<source>Hello filter %name%</source>
<target>__Hello filter {name}</target> {# 修正后的占位符 #}
</trans-unit>
</body>
</file>
</xliff>关键点: 将 <target> 中的 %name% 修改为 {name}。
完成此修改后,当应用程序加载这些翻译文件时,Symfony 的翻译组件将能够正确识别 {name} 占位符,并将其替换为 Twig 模板中传入的相应变量值。
示例代码
Twig 模板(保持不变):
{# 使用 trans 标签 #}
{% trans with {'%name%': 'Hans'} %}Hello %name%{% endtrans %}
{# 使用 trans 过滤器 #}
{{ 'Hello filter %name%'|trans({'%name%': 'Hans'}) }}XLIFF 翻译文件 (messages.en.xlf) 修正示例:
<!-- ... 其他 XLIFF 内容 ... -->
<body>
<trans-unit id="SzX5ua9" resname="Hello %name%">
<source>Hello %name%</source>
<target>Hello {name}</target> {# 实际翻译时,请移除前缀并提供正确翻译 #}
</trans-unit>
<trans-unit id="6l2Ebbm" resname="Hello filter %name%">
<source>Hello filter %name%</source>
<target>Hello filter {name}</target> {# 实际翻译时,请移除前缀并提供正确翻译 #}
</trans-unit>
</body>
<!-- ... 其他 XLIFF 内容 ... -->经过上述修正并清空 Symfony 缓存后,页面将正确显示 Hello Hans 和 Hello filter Hans。
注意事项与最佳实践
- 理解 translation:update 的作用: translation:update 命令主要用于提取源代码中的翻译键并将其同步到翻译文件中。它不会自动将 Twig 的 %placeholder% 语法转换为 ICU 的 {placeholder} 语法。因此,手动修正翻译文件是必要的步骤。
- 保持占位符名称一致性: 确保 Twig 模板中传递的变量名(例如 name)与翻译文件中使用的占位符名(例如 {name})保持一致。
- ICU MessageFormat 的优势: 尽管这种占位符语法差异可能带来一些困惑,但 ICU MessageFormat 提供了更强大的功能,如复数规则(pluralization)、选择规则(selection)等,对于复杂的国际化场景非常有用。
- 其他翻译格式: 如果使用 YAML 或 PHP 等其他翻译文件格式,同样需要确保占位符语法与 Symfony 翻译组件所期望的格式(通常是 ICU 兼容的 {name})一致。
- 清除缓存: 在修改翻译文件后,务必清除 Symfony 缓存 (php bin/console cache:clear),以确保应用程序加载最新的翻译内容。
- 调试翻译: 如果遇到问题,可以使用 Symfony 的 Web Profiler Bar 来检查翻译是否正确加载,以及是否有任何翻译错误或警告。
通过理解 Twig 占位符与 ICU MessageFormat 占位符之间的差异,并正确地在翻译文件中应用 {name} 语法,开发者可以确保 Symfony 应用程序中的变量翻译功能顺畅无阻。










