如何翻译PO文件而不影响应用程序运行
目录
PO 文件看起来像普通文本文件,但一旦翻译的 %s 出错、遗漏复数形式或修改了 msgid,你的应用就可能崩溃。请按照以下流程翻译面向用户的字符串,同时保持 gettext 结构不变。
不要把整个文件粘贴到普通文本翻译器里。PO 文件属于源码相关文件:其中的词语可以翻译,但文件结构、占位符、注释和复数索引必须保持不变。
方法一:使用 PO 文件翻译工具
如果你想要最快速且安全的初稿,并且不想手动编辑 msgid / msgstr 对,应选择此方法。
-
备份原始
.po文件。 在发送给翻译人员之前,务必在你的代码库中保留一份干净的副本。如果翻译后的文件出现问题,你需要一个已知无误的版本进行比对。 -
打开支持 PO 的翻译工具。 使用专为 gettext 文件设计的工具,例如 OpenL PO Translator,这是一个按需付费的文档翻译工具。PO 文件专用工具会翻译目标字符串,同时保留源字符串、注释、占位符和文件结构。如果你还在选择工具,可以参考我们的 最佳 PO 文件翻译工具指南进行比较。
-
上传
.po文件。 使用包含msgid和msgstr的语言文件。如果你只有.pot模板,先根据模板创建目标语言的.po文件,然后再上传该.po文件。 -
选择源语言和目标语言。 源语言要与
msgid内的文本一致,而不是你的管理界面语言。例如,如果文件中的msgid是英文,而你需要西班牙语输出,则选择英文到西班牙语。 -
下载翻译后的文件。 按你的框架要求的本地化命名规范保存文件。WordPress 插件通常采用文本域加语言代码的命名方式,而 Django 通常将文件存放在
locale/<language>/LC_MESSAGES/目录下。 -
优先检查风险字符串。 在翻译文件中搜索
%、{、}、<、>、msgid_plural、msgctxt和#, fuzzy。这些条目最容易影响运行时行为。 -
在应用中测试翻译文件。 本地加载该语言,并逐一点击使用翻译字符串的界面。PO 文件并不是翻译完成就算结束;只有在应用依然能正确渲染时才算真正完成。
方法二:在 Poedit 中翻译 PO 文件
当你需要人工审核、WordPress 兼容性或逐条细致翻译流程时,选择此方法。
-
在 Poedit 中打开文件。 Poedit 是专为 PO 及其他本地化格式设计的翻译编辑器;基础编辑器免费,付费专业版适合更复杂的工作流程。对于 WordPress,官方 Polyglots 手册说明 Poedit 可以根据 POT 文件创建
.po和.mo文件,并支持复数形式和 UTF-8 编码。 -
如果源文本有变,先用 POT 模板更新。 如果开发者修改了应用文本,在翻译前先用最新的
.pot文件更新.po文件。这样可以让新增、删除和模糊字符串都可见,避免悄悄发布过时的界面文本。 -
只翻译
msgstr字段。msgid是应用用于查找翻译的源字符串。在标准 gettext 工作流程中,译者只需编辑msgstr,不要修改msgid。 -
严格保持占位符不变。 不要翻译或更改变量如
%s、%d、%1$s、{name}、%(count)s、:name或 HTML 标签。如果需要调整语序,请整体移动占位符。 -
复数形式需分别翻译。 复数条目包含
msgid、msgid_plural和多个msgstr[n]值。请根据目标语言填写每个复数项,不要简单复制同一句话到所有位置。 -
保存并编译
.mo文件(如果你的应用需要)。 有些技术栈在开发阶段会直接读取.po文件,但 WordPress 和许多 gettext 环境在运行时使用编译后的.mo文件。Poedit 可以在保存时自动编译.mo文件;Django 可以通过django-admin compilemessages命令编译消息文件。 -
在上传前解决所有警告。 在 Poedit 中,警告图标通常提示占位符损坏、变量缺失或复数形式不匹配。在将翻译导入 WordPress、Django、Drupal 或你的发布分支之前,务必修复这些问题。
方法三:使用本地化平台
当有多位译者、审校人员或发布经理需要协作处理同一个 PO 文件时,建议选择这种方式。
-
将 PO 文件导入支持 gettext 的平台。 Weblate 等本地化平台支持 PO 文件工作流。团队本地化平台通常是付费产品,不过 Weblate 也提供开源自托管选项。它们对注释、头部信息、模糊字符串和占位符的处理方式各有不同,上传生产文件前请先检查格式设置。
-
设置占位符和标签检查。 启用针对 printf 风格占位符、命名变量、HTML/XML 标签和复数形式的 QA 规则。这些检查能发现普通拼写检查无法检测的错误。
-
保持开发者注释可见。 PO 文件中的注释可以包含上下文信息,如源代码引用、开发者提取的备注、标记和之前的源字符串。当遇到像“Open”这样简短的界面标签时,译者需要这些备注来判断它是动词、形容词还是菜单命令。
-
结合审校使用翻译记忆库。 翻译记忆库对于重复的界面字符串很有用,但它可能会将旧翻译复制到新的上下文中。当
msgctxt、源引用或周边界面发生变化时,要审查复用的字符串。 -
导出 PO 文件并进行本地检查。 不要盲目相信导出的文件。将翻译文件重新放回应用中,必要时编译,并在合并前测试界面。
PO 文件绝对不能违反的规则
| PO 项目 | 是否翻译? | 安全示例 | 为什么重要 |
|---|---|---|---|
msgid | 否 | msgid "Save changes" | 应用程序在许多 gettext 工作流程中使用此源字符串作为查找键。 |
msgstr | 是 | msgstr "Guardar cambios" | 这是用户看到的目标语言文本。 |
msgctxt | 否 | msgctxt "button" | 上下文用于区分相同的源字符串。 |
%s, %d, %1$s | 否 | Hello, %s -> Hola, %s | 运行时代码会用实际值替换这些占位符。 |
{name}, %(count)s, :name | 否 | Welcome, {name} | 命名变量必须与应用代码保持一致。 |
| HTML 标签 | 通常不翻译 | <strong>Warning</strong> | 只翻译文本,不要改动标签语法。 |
msgid_plural | 否 | msgid_plural "%d files" | 源复数属于原始代码路径。 |
msgstr[0], msgstr[1] | 是,但需谨慎 | msgstr[0] "%d file" | 每种目标语言都有自己的复数规则。 |
#, fuzzy | 需先审核 | #, fuzzy | fuzzy 表示翻译可能已过时或未确认。 |
#. 开发者注释 | 通常不翻译 | #. Button label | 这些注释帮助译者理解上下文。 |
快速示例:安全与错误的 PO 翻译
以下是一个常规的 gettext 条目:
#. %s 是用户的显示名称。
#, c-format
msgid "Welcome back, %s"
msgstr ""
安全的西班牙语翻译会保持 %s 不变:
#. %s 是用户的显示名称。
#, c-format
msgid "Welcome back, %s"
msgstr "Bienvenido de nuevo, %s"
错误的翻译则改动了占位符:
msgid "Welcome back, %s"
msgstr "Bienvenido de nuevo, % s"
这个微小的空格可能会造成问题。GNU msgfmt --check-format 就是用来检测格式字符串不匹配,比如错误的 % 占位符,Poedit 也会提醒常见的占位符问题。想了解更多需要保持不变的字符串,请参考我们的 不可翻译内容指南。
如何检查已翻译的 PO 文件
- 如果你已安装 gettext,请运行 gettext 校验。
msgfmt --check --check-format -o /tmp/messages.mo path/to/messages.po
此命令会检查语法、头部信息和格式字符串,并在文件有效时生成一个临时编译目录。
- 按照你的框架要求编译文件。
django-admin compilemessages
对于 Django 项目,compilemessages 会将由 makemessages 创建的 .po 文件编译成 .mo 文件,以支持 gettext。
- 查找空翻译项。
grep -n 'msgstr ""' path/to/messages.po
空的 msgstr 字段可能是故意保留未翻译,但在发布时不应让你感到意外。
- 查找模糊翻译。
grep -n '#, fuzzy' path/to/messages.po
模糊翻译在发布前应由人工审核。默认情况下,msgfmt 不会使用模糊翻译,除非你编译时加上 --use-fuzzy,因此模糊项在最终目录中会表现为未翻译字符串。
- 测试真实界面。 打开包含表单、复数计数、错误信息、账户菜单和支付流程的页面。PO 文件校验能发现文件问题,只有界面测试才能发现措辞不自然、内容溢出和上下文缺失等问题。
应该使用哪种方法?
| 情况 | 最佳方法 | 原因 |
|---|---|---|
| 你需要为单个 PO 文件快速生成初稿 | PO 文件翻译器 | 保持结构的最快路径 |
| 你维护 WordPress 插件或主题 | Poedit | 熟悉的 WordPress 工作流程,支持 .mo 编译 |
| 你维护 Django 应用 | PO 翻译器或 Poedit,然后运行 compilemessages | 翻译可以很快完成,但框架编译仍然必需 |
| 你有多种语言和多个审核人员 | 本地化平台 | 更好的分配、历史记录、质量控制和审核管理 |
| 你在翻译面向开发者的字符串 | 机器翻译后人工审核 | 代码术语、占位符和上下文更为重要 |
| 你还要本地化 JSON 或前端 i18n 文件 | 使用针对格式的工作流程 | PO 规则并不总适用于 JSON、YAML 或 ICU 消息 |
如果你的项目同时使用 gettext PO 文件和 JSON 语言文件,请分别用能理解其结构的工具进行翻译。PO 文件以 msgid 和 msgstr 为核心;JSON 本地化则围绕键和值展开。相关工作流程请参考我们的2026 年最佳 JSON 翻译工具指南。
常见问题
我可以用 Google Translate 翻译 PO 文件吗?
你可以将单个 msgstr 内容复制到通用翻译器中,但将整个 PO 文件上传或粘贴到纯文本翻译器存在风险。通用翻译器可能会修改 msgid、注释、引号转义、复数索引或占位符。建议使用支持 PO 格式的翻译器、Poedit 或本地化平台。
.po、.pot 和 .mo 有什么区别?
.pot 是从源代码提取的模板,通常只包含原始字符串,不含翻译内容。.po 是针对某种目标语言的可编辑翻译文件。.mo 是编译后的二进制目录,许多基于 gettext 的应用在运行时加载它。
我应该翻译 msgid 吗?
不,正常的工作流程中不会这样做。请翻译 msgstr。GNU gettext 手册将 msgid 描述为原始未翻译字符串,而 msgstr 是翻译内容;msgid 字符串由 gettext 工具生成和管理。
如何检查 PO 文件是否有效?
如果你安装了 gettext,可以运行 msgfmt --check --check-format,也可以用 Poedit 打开文件,或者使用你的本地化平台的 QA 检查功能。之后,将文件编译并在应用内测试。校验是必要的,但不能替代 UI 测试。
如果我破坏了占位符会发生什么?
最好的情况是应用显示一个奇怪的字符串。更糟糕的是,运行时格式化器会抛出错误,因为翻译后的字符串不再与代码传入的变量匹配。占位符只能作为完整的标记移动,绝不能翻译或部分编辑。
OpenL 能翻译 PO 文件吗?
可以。OpenL PO Translator 专为 gettext .po 文件设计,并声明在翻译超过 100 种语言时会保持占位符和变量不变。它采用按需付费的文档翻译流程,因此当你更看重保留 PO 结构而不是手动处理时,可以用它快速生成初稿。如果你只有 .pot 模板,请先创建目标语言的 .po 文件再使用 OpenL。
参考资料
- GNU gettext 手册:PO 文件 — 官方对 PO 文件格式的解释。
- GNU gettext 手册:PO 文件条目 — 关于
msgid、msgstr、注释、标志和条目结构的说明。 - GNU gettext 手册:带复数形式的条目 — 关于复数形式条目结构的说明。
- GNU gettext 手册:msgfmt 调用 — 关于
msgfmt --check和格式字符串校验的说明。 - OpenL PO Translator — OpenL 产品页面,支持翻译
.po文件并保留占位符和变量。 - Poedit — 官方 Poedit 网站,提供 PO 翻译编辑器及专业版服务。
- WordPress Polyglots 手册:Poedit — WordPress 关于使用 Poedit、POT 文件、PO 文件、MO 编译及占位符警告的指导。
- Django 文档:翻译 — Django 消息文件和翻译工作流程说明。
- Django 文档:compilemessages — Django 命令参考,介绍如何将
.po文件编译为.mo文件。 - Drupal 文档:PO 和 POT 文件 — Drupal 关于
.po、.pot、上下文、复数形式、注释和变量的说明。 - Weblate 文档:GNU gettext PO — 本地化平台关于 PO 文件头、历史源字符串、废弃字符串和生成 MO 文件的说明。