功能插件定价资源
更改语言
资源如何翻译 XLIFF 文件 (Drupal, Symfony, Angular, iOS)

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

SimplePoTranslate Team2026年4月16日
如何翻译 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, &lt;g id="1"&gt;%name%&lt;/g&gt;!</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 单元具有状态属性:newneeds-translationtranslatedreviewedfinalsigned-off。一个流程应该只翻译处于 newneeds-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 关键词(pluraloneother=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,下载安全的翻译,导入到您的平台。