功能插件定价资源
更改语言
资源如何使用 AI 翻译 Drupal .po 文件

如何使用 AI 翻译 Drupal .po 文件

SimplePoTranslate Team2026年6月6日
如何使用 AI 翻译 Drupal .po 文件

大多数人将 .po 文件与 WordPress 相关联,但 gettext 格式早于 WordPress 几十年,并为整个开源世界的界面翻译提供支持。Drupal 是其最大的用户之一。如果您运行一个多语言的 Drupal 站点,每个菜单标签、字段描述、错误消息和模块字符串都通过 .po 文件处理,就像在 WordPress 中一样,只是占位符方言和导入工作流不同。而且,就像 WordPress 一样,一旦您的站点字符串数量超出少量,手动翻译这些文件就会成为瓶颈。

本指南旨在介绍如何使用 AI 翻译 Drupal po 文件,同时不破坏 Drupal 的核心特性。我们将详细介绍 Drupal 的翻译系统、字符串的实际来源、与 WordPress 不同且必须保留的占位符语法,以及完整的导出、翻译和重新导入循环,包括使其可重复的 drush 命令。

Drupal 的翻译系统如何运作

Drupal 通过核心的 locale 模块(在现代 Drupal 中有时称为“界面翻译”)处理界面翻译。启用后,它会管理一个包含源字符串及其每种语言翻译的数据库表,并且可以以标准 gettext .po 文件格式导入和导出这些翻译。

管理界面位于 /admin/config/regional/translate。从那里,您可以搜索未翻译的字符串,在线编辑它们,最重要的是,导入一个 .po 文件以批量加载翻译,或将当前状态导出到 .po 文件以进行离线编辑。这种导出/编辑/导入循环是我们正在优化的工作流程。

与 WordPress 不同的是,WordPress 的每个插件和主题都在 languages/ 目录中附带自己的 .po.mo 文件,而 Drupal 将界面字符串集中存储在其数据库中,并将 .po 视为交换格式。您导出文件,对其进行处理,然后将其导回。WordPress 所需的编译 .mo 步骤在内部处理。

字符串的来源

Drupal 字符串源自三个地方,一个完整的翻译必须涵盖所有这些:

  • Core 包含数千个可翻译字符串,用于管理界面、表单和系统消息。
  • Contrib modules(Views、Webform、Commerce 等)每个都通过 t() 函数注册自己的字符串。
  • Themes 贡献模板字符串、区域标签和主题设置描述。

当您从 /admin/config/regional/translate 导出时,可以将导出范围限定为已翻译、未翻译或所有字符串。对于首次翻译,仅导出未翻译的字符串会为您提供一个集中的 .po 文件,其中包含所有剩余工作。

这种三源结构的一个实际后果是:您的翻译工作永远不会真正“完成”。每次您添加 contrib 模块、启用新功能或更新 Drupal core 时,都会有新一批未翻译的 msgid 条目出现在 locale 表中。这就是为什么 Drupal 团队将翻译视为一个持续的流程,而不是一次性的发布任务。相同的导出、翻译、重新导入循环在每次涉及模块的部署中运行,并且该循环越快,发布之间积累的翻译债务就越少。

值得注意的是,Drupal 可以自动从 localize.drupal.org 获取 core 和流行的 contrib 模块的社区贡献翻译。这些涵盖了常用字符串,但很少涵盖您的自定义模块、主题或您的站点实际使用的项目特定短语。社区翻译与完全本地化站点之间的差距正是 AI 翻译能够快速弥补的工作。

Drupal 占位符并非 WordPress 占位符

在将任何 Drupal .po 文件发送给 AI 翻译器之前,最重要的一点是:Drupal 不使用 WordPress 中常见的 printf 风格的 %s%1$s 占位符。Drupal 的 t() 函数使用三种不同的占位符前缀,每种都有不同的转义行为:

  • @variable — 值经过 HTML 转义,是用户提供文本的安全默认值。
  • %variable — 值经过转义并用 <em> 强调标记包装。
  • :placeholder — 用于 URL 属性,经过 URL 清理。

在导出的 .po 文件中,典型的 Drupal 源字符串如下所示:

#: core/modules/node/node.module
msgid "@count comments"
msgid_plural "@count comments"
msgstr[0] ""
msgstr[1] ""

#: core/modules/user/user.module
msgid "Welcome @name, you last logged in on %date."
msgstr ""

如果翻译人员将 @count 更改为 @cuenta,将 @name 替换为“name”的翻译词,或者插入一个空格将 @count 变成 @ count,那么占位符就不再与 t() 调用传入的内容匹配。然后 Drupal 会在页面上打印字面令牌 @count,而不是实际数字。翻译看起来完成了,但网站却明显损坏了。

这类错误与影响 WordPress %s 字符串的错误相同,我们在如何翻译 PO 文件而不破坏代码变量中深入介绍了这一通用原则。Drupal 的特殊之处在于有三种占位符样式而不是一种,并且翻译工具必须识别所有这些样式。

为什么通用翻译器会在此处失败

将该 node.module 字符串粘贴到消费者机器翻译框中,您会经常发现 @count 被损坏,<em> 绑定的 %date 被本地化为不同的标记,或者复数形式被合并为一种。这些工具都没有考虑到 gettext 语义而构建。它们翻译文本;它们不理解 @count 是与应用程序代码的约定。

一个具备 语法锁定 功能的 gettext 感知翻译流程将 @variable%variable:placeholder 视为不可变标记。它们在翻译前被锁定,翻译后恢复,因此周围的句子被翻译,而占位符则保持不变。同样的锁定机制也适用于 WordPress %s%1$s、i18next {{name}} 以及内联 HTML,这就是为什么一个工具可以像服务 WordPress 项目一样轻松地服务 Drupal、Symfony 或 Laravel 项目。通过 msgid_plural 的 Drupal 复数形式被保留为复数形式而不是扁平化,这很重要,因为 Drupal 语言可以有一到六种复数变体。

还有一个更微妙的失败模式值得指出。Drupal 字符串经常在可翻译文本中嵌入内联标记和链接,例如 Read the <a href=":url">documentation</a>,其中 :url 是一个占位符,<a> 标签必须保留。不理解这种结构的翻译器可能会翻译 href 属性,删除结束标签,或本地化占位符名称。上下文感知 AI 作为一个单元读取整个字符串,并结合标记锁定,在翻译中间人类可读的“documentation”文本时,保持标记和 :url 约定完好无损。

导出、翻译、重新导入循环

可重复的工作流程分为三个阶段,drush 使导出和导入可编写脚本,这样您就不必每次都通过管理界面进行点击操作。

步骤 1:导出 PO 文件

您可以通过在 /admin/config/regional/translate 选择语言和导出范围从 UI 导出,或从命令行进行导出。locale 模块通过 Drupal 的翻译服务公开导出功能,大多数团队都会编写脚本来处理。一个典型的 drush 调用来触发翻译导入(我们将在第三步中使用其反向操作)如下所示:

# Re-import a translated PO file for Spanish, overwriting customized strings
drush locale:import es /var/www/translations/es-untranslated.po \
  --type=customized --override=all

# Rebuild caches so the new strings render immediately
drush cache:rebuild

在导出方面,管理导出表单会生成 .po 文件;许多团队会将 locale 导出服务封装在一个小的自定义 Drush 命令中以实现完全自动化。无论哪种方式,您最终都会得到一个包含未翻译 msgid 条目的标准 gettext .po 文件。

步骤 2:翻译文件

在这个阶段,AI 取代了数小时的手动编辑工作。将导出的 .po 文件上传到云翻译器,选择目标语言,然后让它进行处理。由于大型站点的 Drupal .po 文件在核心、contrib 和主题合并后通常会超过 10MB,因此 智能批处理 在这里很重要:文件被分块、翻译和重新组装,而无需您手动分割或达到大小限制。输出是一个完整的 .po 文件,其中每个 msgstr 都已填充,并且每个 @count%date:url 占位符都精确地保留在原始位置。

步骤 3:重新导入 Drupal

通过 /admin/config/regional/translate 或上述 drush locale:import 命令将翻译后的文件重新导入,然后重建缓存。Drupal 将翻译合并到其 locale 表中,字符串会立即在整个站点显示。每当您添加模块或更新核心时,请再次运行此循环,因为每次都会带来新的未翻译字符串。

一个重要的导入细节需要注意:--override 标志控制传入的翻译是否替换现有的翻译。当您希望 AI 翻译的文件具有权威性时,使用 --override=all;如果您在 Drupal UI 中手动调整了某些不希望被覆盖的字符串,则使用更保守的设置。对于大多数自动化流程,将 .po 文件视为事实来源并覆盖所有内容可以保持系统可预测性:版本控制中的文件就是站点显示的内容,仅此而已。

对于拥有多种目标语言的多语言站点,循环每种语言运行一次。导出未翻译集,翻译成德语,导入;再次导出,翻译成西班牙语,导入;依此类推。因为每种语言都是一个独立的 .po 文件,所以您可以并行运行它们,而一个并发处理它们的云翻译器可以将原本需要承包商一周的时间缩短到半天。

Drupal PO 只是众多格式之一

值得注意的是,gettext .po 并非 Drupal 处理翻译的唯一方式。配置和内容实体也可以作为 XLIFF 进行交换,这是翻译管理工具 (TMGMT) 模块在专业翻译供应商工作流中依赖的标准。如果您的 Drupal 本地化通过 XLIFF 而不是界面 .po 文件运行,那么占位符保留原则是相同的,但文件结构不同,我们在 翻译 Drupal、Symfony 和 Angular 的 XLIFF 文件中介绍了这条路径。

然而,对于界面翻译层,.po 仍然是主力。导出、AI 翻译、重新导入的循环将耗时多天的手动工作变成了几分钟的处理过程,只要您使用的工具真正理解 gettext 占位符,您的 @variable 约定就能完好无损地保留,并且您的 Drupal 站点在您发布的每种语言中都能保持完整无缺。

准备好翻译您的 Drupal .po 文件而不破坏任何 @variable 吗?免费试用 SimplePoTranslate — 无需信用卡。免费套餐支持标准的 gettext .po.pot 文件,并提供完整的语法锁定功能,因此您的 Drupal 占位符将完全按照代码预期的方式传递。