功能插件定价资源
更改语言
资源使用 msgctxt:为 Gettext 翻译添加上下文

使用 msgctxt:为 Gettext 翻译添加上下文

SimplePoTranslate Team2026年5月15日
使用 msgctxt:为 Gettext 翻译添加上下文

您将英文单词“Book”翻译成德语,您的译者自信地返回了“Buch”。三周后,一份错误报告出现:您的预订表单上的“Book a table”按钮现在显示为“Buch a table”——是名词,而不是动词。单词本身没错,但缺少了上下文。这是软件本地化中最常见的隐性故障之一,而Gettext中解决此问题的方法已经存在几十年了:msgctxt

如果您曾经发布过一份译文,其中一个英文字符串在一个地方是正确的,而在另一个地方却毫无意义,那么您就遇到了歧义问题。同一个源词根据其出现的上下文,会映射到不同的译文。如果没有 msgctxt,译者(无论是人工还是AI)就无法区分这些情况,因为他们所看到的只是裸字符串 msgid "Book"。本指南将解释 msgctxt 在 Gettext 中如何工作,如何使用 _x()_ex() 标记您的源代码,它如何出现在您的 .po 文件中,以及为什么上下文感知的翻译流程是大规模正确处理这些字符串的唯一可靠方法。

歧义问题:一个词,多种含义

自然语言中充满了这样的词:它们在英语中是一个单一的标记,但在其他语言中却会分成多个词。译者只看到源字符串,因此当两个不相关的UI元素共享同一个英文词时,它们会共享同一个 msgid——而 Gettext 会将它们合并为一个条目。

跨语言的词义分歧

考虑三个经典例子:

  • “Book” - 名词(书架上的物体)与动词(“预订航班”)。德语:Buch 对比 buchen
  • “Post” - 发布内容与寄送邮件。法语:publier 对比 courrier
  • “Order” - 序列/排序与购买。西班牙语:orden 对比 pedido

在英语中,您的代码在这两个地方使用相同的字面字符串:

// Somewhere in a library catalog screen
echo __( 'Book', 'mytextdomain' );

// Somewhere in a reservation form button
echo __( 'Book', 'mytextdomain' );

相同的字符串如何合并成一个条目

当您运行 wp i18n make-potxgettext 时,这两个调用都会合并成一个 .po 条目:

#: catalog.php:42 reservation-form.php:88
msgid "Book"
msgstr ""

只有一个 msgstr 可以填写。无论译者选择什么,两个屏幕中都会有一个是错误的。即使译者理解了问题,他们也无法修复,因为格式本身不允许他们为一个 msgid 提供两个译文。歧义已经嵌入到数据中。

什么是 msgctxt?它如何消除歧义?

msgctxt 是附加到翻译条目上的上下文字符串。简单来说:它在 msgid 旁边添加了第二个键,因此 Gettext 将 (context, msgid) 视为一个独特的对。两个具有相同 msgid 但不同 msgctxt 的条目会变成两个独立的、可独立翻译的字符串。

在 Gettext 的查找模型中,翻译通常只通过源字符串进行索引。有了 msgctxt,键就变成了上下文与源字符串的组合。这就是它的全部机制,也是它如此清晰有效的原因:您没有改变显示的文本,只是改变了内部查找键。

.po 文件中的 msgctxt 行

.po 文件中,上下文会单独一行,直接出现在 msgid 的上方:

#: catalog.php:42
msgctxt "noun"
msgid "Book"
msgstr "Buch"

#: reservation-form.php:88
msgctxt "verb"
msgid "Book"
msgstr "Reservieren"

现在有两个条目。它们具有相同的 msgid 值,但 msgctxt 值不同,因此每个条目都有自己的 msgstr。目录显示 Buch;预订按钮显示 Reservieren。运行时会选择正确的那个,因为您的代码告诉了它使用哪个上下文。

将其与上面损坏的单条目版本进行比较。区别不在于翻译质量——而在于数据模型是否允许正确答案的存在。

标记您的代码:_x()_ex()

要将 msgctxt 发射到您的 .po 模板中,您需要调用一个上下文感知的翻译函数,而不是普通的翻译函数。在 WordPress 中,它们是 _x() 及其回显兄弟 _ex();在原始 Gettext 中,它们映射到 pgettext()

选择 _x()_ex()

普通的 __() 函数接受字符串和文本域。上下文变体将上下文作为第二个参数插入:

// Without context - ambiguous
__( 'Book', 'mytextdomain' );

// With context - the second argument is the msgctxt
_x( 'Book', 'noun', 'mytextdomain' );        // returns the translated string
_ex( 'Book', 'verb', 'mytextdomain' );       // echoes it directly

// Real-world disambiguation
_x( 'Post', 'verb: publish content', 'mytextdomain' );
_x( 'Post', 'noun: mail item', 'mytextdomain' );
_x( 'Order', 'sequence or sorting', 'mytextdomain' );
_x( 'Order', 'customer purchase', 'mytextdomain' );

第二个参数是上下文标签。它绝不会显示给您的用户——它纯粹是为了消除歧义而存在,无论是为了 Gettext 查找,还是为了翻译者(无论是人还是机器)。上下文应该写成简短、描述性的提示,例如:'noun''verb''button label''admin menu'。像 '1' 这样模糊的上下文在技术上是有效的,但对译者毫无用处。

当您重新生成模板时,提取器会识别这些调用并发出 msgctxt 行。Poedit 和其他 PO 编辑器等工具随后会根据上下文对条目进行可见分组,因此人工译者会立即看到“Book (noun)”和“Book (verb)”是两项不同的工作。如果您仍在配置提取工具链,或者在处理变量如何与这些字符串交互时遇到困难,我们的指南在不破坏代码变量的情况下翻译 PO 文件深入介绍了相关工作流程。

为什么“天真”的译者——以及许多 AI 工具——会犯错

接下来是令人不适的部分。将 msgctxt 添加到您的源代码是必要而非充分的。上下文只有在执行翻译的工具真正读取它时才能发挥作用。

丢失上下文的故障模式

一个“天真”的批量翻译脚本会这样做:它循环遍历条目,获取每个 msgid,将其发送到翻译 API,然后将结果写入 msgstr。它从不查看 msgctxt 行。因此,即使您仔细地编写了 _x( 'Book', 'noun' )_x( 'Book', 'verb' ),脚本也会发送两个相同的请求——“翻译 Book”——并将相同的答案粘贴到两者中。您所有的标记工作都在最后一步被丢弃了。

许多通用型 AI 翻译工具都有同样的盲点。它们旨在翻译文本,而 msgctxt 是元数据,不是文本。如果工具在将 .po 文件发送到模型之前将其展平为字符串列表,那么上下文就永远不会到达模型的提示。模型由于缺乏任何信号,会默认选择该词在统计上最常见的含义——通常“Book”是名词,“Post”是发布的意思——并悄悄地错误翻译少数情况。您不会看到错误。您只会在几周后收到一份来自德国用户的错误报告。

上下文与复数形式共享相同的根本原因

这也是复数处理和上下文交汇的地方,因为两者都依赖于工具理解 Gettext 的结构,而不是将文件视为扁平文本。如果您的字符串还涉及基于计数的格式,同样的结构意识也很重要——我们在理解 Gettext 复数形式中详细阐述了这一点。

上下文感知流程如何使用 msgctxt

一个尊重 msgctxt 的翻译流程与“天真”的脚本恰恰相反。它将 .po 文件解析为结构化数据,将每个条目的上下文附加到其源字符串,并将该上下文作为提示的一部分传递给 AI——这样模型就知道它正在专门将“Book”翻译为动词,而不是抽象地翻译。

上下文作为一级信号

这正是 SimplePoTranslate 解决问题的方式。它的上下文感知 AImsgctxt 行作为一级信号读取:当两个条目共享 msgid 但上下文不同时,它们会被翻译成各自独立的字符串,并且上下文提示会指导模型的词语选择。结果是,“Book (noun)”返回 Buch,而“Book (verb)”返回 buchenreservieren——自动完成,无需您手动纠正每一个歧义词。

同样重要的是,该流程会在输出中保留 msgctxt 行。一个较弱的工具可能会在往返过程中剥离上下文,悄悄地将您的两个条目合并回一个,并在下次合并时重新引入歧义。完整的 Gettext 支持意味着上下文在翻译、.mo 编译以及任何后续重新导入中都能得以保留——因此您的消歧是持久的,而不是一次性的手动修补。结合针对这些字符串内占位符的语法锁定,您将获得既在上下文上正确又在结构上完整的翻译。

您仍然拥有标记决策权——只有您知道某个“Book”是动词。但一旦您使用 _x()_ex() 标注了您的源代码,上下文感知流程就会将该标注转化为所有目标语言的正确翻译,而无需逐个字符串进行“看护”。

结论

歧义问题——一个英文词,多种含义——不是您可以忽略的怪癖;它是 Gettext 存储字符串方式的一个结构性特征。解决方案是 msgctxt:在您的源代码中使用 _x()_ex() 标注有歧义的字符串,为每个出现的位置提供一个清晰的上下文标签,并让 Gettext 根据 (context, msgid) 对来索引翻译。这部分工作由您来完成,只需几分钟。

较难的部分是确保您的翻译步骤真正尊重该上下文而不是将其丢弃。“天真”的脚本和纯文本 AI 工具会丢弃 msgctxt,并重新引入您试图阻止的错误。上下文感知流程会读取上下文,正确翻译每个已消歧的条目,并在编译和重新导入过程中保留它。

准备好停止发布“盲目”的错误翻译了吗?免费试用 SimplePoTranslate — 无需信用卡。免费套餐支持处理真实的 .po.pot 文件,并提供完整的 msgctxt 上下文支持,因此您消歧后的字符串第一次就能正确翻译。

相关主题