如何翻译 XLIFF 文件 (Drupal, Symfony, Angular, iOS)

一位 Drupal 开发者上周在 Stack Overflow 上发布了一个故事,这个故事每年在企业本地化团队中上演数千次。她的团队从 Drupal 站点导出了一个 40MB 的 XLIFF 文件。她在一个文本编辑器中打开它,将文本块粘贴到 Google Translate 中,重新组装文件,然后重新导入到 Drupal 中——结果一半页面无法渲染,因为翻译后的字符串包含格式错误的 XML、错误位置的转义字符以及内联格式被破坏而导致的损坏的 <g> 标签。
XLIFF 成为企业本地化标准是有原因的。它基于 XML,与工具无关,并支持严肃翻译项目所需的丰富元数据:翻译状态、备选翻译、译者与开发者之间的注释以及结构化的内联标记。但其高度灵活性也使其容易损坏,并且安全处理 .po 文件的工具在处理 XLIFF 时往往会失效。
本指南将详细介绍 XLIFF 的独特之处、为什么通用翻译方法会破坏它,以及为 Drupal、Symfony、Angular 和 iOS(这四个平台在 2026 年最常使用 XLIFF)翻译 XLIFF 文件的正确方法。
什么是 XLIFF(以及为什么企业团队使用它)
XLIFF 是 XML 本地化交换文件格式 (XML Localization Interchange File Format) 的缩写。它是一个 OASIS 标准,专门设计用于在工具之间(内容管理系统、翻译记忆数据库、CAT 工具和本地化平台)移动翻译内容。与 Gettext .po 文件(存储扁平的键值对)或 JSON 区域设置文件(任意嵌套)不同,XLIFF 是一个具有标准化模式的结构化 XML 文档。
XLIFF 1.2 对比 XLIFF 2.0
目前存在两个版本,它们并非完全兼容。
XLIFF 1.2 是较旧且更广泛部署的版本。它使用 <trans-unit> 元素来包裹可翻译内容,并包含 <source> 和 <target> 子元素。内联格式使用 <g>、<x> 和 <bpt> / <ept> 成对标签。Drupal 的 Translation Management Tool 和许多较旧的平台仍然导出 1.2 版本。
XLIFF 2.0 是 2014 年修订版,更简单、更清晰。它使用 <unit> 和 <segment>,并包含 <source> 和 <target>。内联标记使用 <pc>(成对代码)和 <ph>(占位符)。Symfony 的翻译组件和现代 iOS 导出默认使用 2.0 版本。
一个处理 1.2 版本的翻译工具不一定能自动处理 2.0 版本。标签词汇不同,转义规则也略有差异。在选择翻译流程之前,务必检查您的平台导出的是哪个版本。
XLIFF 单元的结构
一个最简的 XLIFF 1.2 trans-unit 如下所示:
<trans-unit id="msg_welcome" datatype="plaintext">
<source>Welcome, <g id="1">%name%</g>!</source>
<target state="needs-translation">Welcome, <g id="1">%name%</g>!</target>
<note>Displayed on the homepage after login</note>
</trans-unit>
<g id="1"> 包裹了一个占位符变量。state 属性告诉平台此字符串需要翻译。<note> 是开发者的提示。理解 XLIFF 的译者应该生成:
<target state="translated">¡Bienvenido, <g id="1">%name%</g>!</target>
将文件视为纯文本的译者可能会生成以下任何损坏的版本:
<target>¡Bienvenido, <g id="1">%nombre%</g>!</target>
<target>¡Bienvenido, <g id="1">%name%</g>!</target>
<target>¡Bienvenido, %name%!</target>
每种情况都会以不同的方式破坏导入。第一个重命名了变量。第二个转义了 XML。第三个则完全删除了格式标签。
错误解决方案(为什么不能直接翻译 XML)
大多数团队最初尝试相同的三种方法,并在浪费几天时间后放弃。
将 XLIFF 输入给通用 AI
复制文件,粘贴到 Claude 或 ChatGPT 中,请求翻译。模型通常能很好地处理文本,但对 XLIFF 标签的处理不一致。有时它会保留 <g> 标签,有时会翻译 id 属性,有时则会完全去除它们。验证失败。您的导入会抛出 XML 解析错误。
使用不具备 XLIFF 支持的 CAT 工具
像 Poedit 这样的工具是为 .po 格式构建的。它们可能可以打开 XLIFF,但会将其视为通用文本容器。内联标签未被锁定。占位符未受保护。您得到的翻译在编辑器中看起来没问题,但在导入时会失败模式验证。
编写自定义脚本
您的团队编写了一个 Node 或 Python 脚本,使用 xml2js 解析 XLIFF,提取源字符串,调用 Google Translate,然后写回目标字符串。它对 90% 的字符串都有效。而另外 10%——包含嵌套格式、复数组或特殊字符的字符串——会以一种只有在您发布之后才会显现的方式损坏。
同样的“灵活格式遇到天真译者”的失败模式也影响着 i18next JSON 和 Gettext .po 文件。我们关于 为 React 和 Next.js 翻译 i18next JSON 文件 的指南以及关于 如何在不破坏代码变量的情况下翻译 .po 文件 的文章涵盖了这些格式的类似问题。
正确方法:语法感知型 XLIFF 处理
一个正确的 XLIFF 翻译流程遵循与我们的 PO 引擎相同的原则,并为 XML 进行了适配。
解析,而非正则匹配
将 XLIFF 视为结构化文档。使用真正的 XML 解析器进行解析,构建一棵树,然后遍历 <trans-unit>(对于 2.0 则是 <unit>)元素。尝试使用正则表达式匹配源内容和目标内容是导致文件损坏的捷径。
翻译前锁定内联标签
<source> 内的每个 <g>、<x>、<bpt>、<ept>、<ph>、<pc> 都必须通过位置和 id 属性进行保留。在将文本发送给大型语言模型 (LLM) 之前,用数字占位符替换它们,然后在翻译返回后重新插入带有其属性的原始标签。
尊重状态机
XLIFF 单元具有状态属性:new、needs-translation、translated、reviewed、final、signed-off。一个流程应该只翻译处于 new 或 needs-translation 状态的单元,并将输出状态设置为 translated(而不是 final——审阅者仍应进行验证)。
维护翻译单元之外的结构
XLIFF 文件包含标题、元数据、文件级属性、注释和备选翻译 (<alt-trans>)。这些在往返过程中必须保持不变。剥离或重新排序它们会破坏与源平台的往返兼容性。
交付前验证
在返回翻译后的 XLIFF 之前,请对照模式进行验证。XLIFF 1.2 有官方 XSD。XLIFF 2.0 也有自己的。一个无法进行自验证的工具将给您交付损坏的文件。
平台特定注意事项
每个使用 XLIFF 的主要平台都有值得注意的特性。
Drupal
Drupal 的 Translation Management Tool (TMGMT) 导出 XLIFF 1.2。内容类型包括节点(页面、文章)、分类术语和配置。TMGMT 将每个可翻译字段包装在一个单独的 <trans-unit> 中,并带有 Drupal 特定的 ID 格式(fieldname:delta:format)。
注意点:Drupal 将文本格式信息(过滤后的 HTML、完整 HTML、纯文本)与内容一起存储。在格式允许的情况下,翻译必须保留 HTML 标记;在格式不允许的情况下,则应剥离为纯文本。您的流程需要对每个字段有感知。
Symfony
Symfony 的翻译组件默认使用 XLIFF 2.0(自 Symfony 4 起)。字符串位于 translations/messages.xx.xliff 中。Symfony 支持 XLIFF 内部的 ICU 消息格式,这意味着单个单元可以包含 {count, plural, one {...} other {...}} 结构。
注意点:XLIFF 内部的 ICU 复数类别需要双重保护。XML 标签必须保持完整,并且 ICU 关键词(plural、one、other、=0)不得被翻译。许多 XLIFF 工具可以处理一层,但不能同时处理两层。
Angular i18n
Angular 通过 ng extract-i18n 命令导出 XLIFF 1.2 或 2.0。文件包含组件模板字符串,其中 <x> 标签表示 Angular 表达式和插值,如 {{ count }}。
注意点:Angular 在相同的源字符串之间使用 id 哈希冲突。翻译必须精确地保持单元 ID,否则 Angular 在导入时无法匹配它们。在处理过程中重命名 id 属性会导致立即中断。
iOS (Xcode 导出)
Xcode 通过 Product > Export Localizations 导出 XLIFF 1.2 用于应用本地化。字符串来自 Localizable.strings、Info.plist 条目、storyboards 和 XIB 文件。iOS 复数规则存在于作为额外 trans-unit 导出的 .stringsdict 文件中。
注意点:iOS storyboard 字符串引用 UI 元素 ID。这些不能被更改。此外,Xcode 要求 target-language 属性精确匹配其预期的区域设置格式(在某些情况下是 es,而不是 es-ES),否则它会静默忽略导入。
使用 SimplePoTranslate 翻译 XLIFF
SimplePoTranslate 在 Pro 和 Lifetime 计划中支持 XLIFF。工作流程与 .po 文件相同。
1. 导出您的 XLIFF
从您的源平台导出或多个 .xliff 文件。对于 Drupal,使用 TMGMT 的导出操作。对于 Symfony,找到 translations/messages.en.xliff。对于 Angular,运行 ng extract-i18n --format=xlf2。对于 Xcode,使用本地化导出。
2. 上传到 SimplePoTranslate
将文件拖到仪表板中。平台会自动检测 XLIFF 版本(1.2 或 2.0),解析结构,并识别可翻译单元。选择您的目标语言和语调。
3. 语法感知型翻译
内联标签、ICU 参数、占位符和单元 ID 在翻译前被锁定。底层 AI 引擎只看到带有上下文的干净文本。翻译后的文本被重新插入到完全原始的结构中,状态会更新,并在交付前对照 XLIFF 模式对文件进行验证。
4. 下载和导入
下载翻译后的 XLIFF(如果您需要跨平台,还可以下载 .po、.json 和 .php 等效文件)。导入到您的源平台。在正式发布之前,通过渲染几个翻译页面或视图来验证。
# Angular example
ng extract-i18n --format=xlf2 --output-path=src/locale
# upload src/locale/messages.xlf to SimplePoTranslate
# download messages.es.xlf
# reference in angular.json i18n configuration
ng build --localize
5. 集成到 CI
一旦您信任该流程,就可以将其自动化。在每次发布时导出 XLIFF,通过 API 提交,下载翻译文件,提交到仓库,然后部署。这与许多代理机构用于 WordPress .po 翻译的 CI 友好模式相同——请参阅我们关于 无需网站损坏的云端翻译 的文章以了解其架构模式。
总结
XLIFF 是严肃本地化工作的正确工具——结构化、与工具无关,并且足够丰富以在系统之间传输项目元数据。但其 XML 结构也很脆弱。每个标签、属性和状态值都具有语义权重,不理解 XLIFF 格式的译者会以可能直到导入失败或用户报告 UI 损坏时才显现的方式破坏您的文件。
安全的方法是语法感知型:解析 XML,锁定结构元素,仅翻译带有上下文的文本表面,并在交付前对照模式进行验证。无论您是发布 Drupal 站点、Symfony API、Angular SPA 还是 iOS 应用程序,这都是如此。平台可能不同,但 XLIFF 的规范不变。
准备好为 Drupal、Symfony、Angular 或 iOS 翻译 XLIFF 文件而无需担心标签损坏了吗?免费试用 SimplePoTranslate——无需信用卡。上传
.xliff,下载安全的翻译,导入到您的平台。