Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

使用 gettext(PO 文件)进行本地化

除了导入 CSV 格式的翻译之外,Godot 还支持加载使用 GNU gettext 格式的翻译文件(基于文本的 .po 文件和编译后的 .mo 文件)。

备注

有关 gettext 的介绍,请查看快速 Gettext 教程。它是针对 C 项目编写的,但其中许多建议也适用于 Godot(xgettext 除外)。

完整文档见 GNU Gettext

优势

  • gettext 是一种标准格式,可以使用任何文本编辑器或图形用户界面编辑器(如 Poedit)进行编辑。这一点很重要,因为它为翻译人员提供了许多工具,例如标记过时的字符串、查找未翻译的字符串等。

  • TransifexWeblate 等翻译平台也支持 gettext,让人们可以更容易地进行本地化协作。

  • 与 CSV 相比,gettext 文件更适合 Git 这样的版本控制系统,因为每个区域设置都有自己的消息文件。

  • 与 CSV 文件相比,在 gettext PO 文件中编辑多行字符串更方便。

缺点

  • gettext PO 文件的格式比 CSV 更复杂,对于刚接触软件本地化的人来说可能更难掌握。

  • 维护本地化文件的人员必须在其系统上安装 gettext 工具。但是,由于 Godot 支持使用基于文本的消息文件(.po),翻译人员无需安装 gettext 工具即可测试他们的工作。

  • gettext PO 文件通常以英语作为基础语言。译者将基于此语言翻译至其他语种。虽然也可选用其他语言作为基础语言,但此做法并不常见。

安装 gettext 工具

命令行 gettext 工具是执行维护操作(例如更新消息文件)所必需的。因此,强烈建议安装它们。

  • Windows:该页面下载安装程序。任何架构和二进制类型(共享或静态)均可;如有疑问,请选择 64 位静态安装程序。

  • macOS:使用 Homebrewbrew install gettext 命令来安装 gettext,或使用 MacPortssudo port install gettext 命令来安装。

  • Linux:在大多数发行版上,请使用发行版的包管理器安装 gettext 包。

至于 GUI 工具,你可以从官方网站获取 Poedit。基础版是开源的,采用 MIT 许可证发布。

创建 PO 模板

使用编辑器自动生成

编辑器可以从指定的场景和 GDScript 文件中自动生成 PO 模板。如果在脚本中使用,此 POT 生成还支持翻译上下文和复数形式,使用 tr()tr_n() 方法的可选的第二个参数。

打开 项目 > 项目设置 > 本地化 > 模板生成,然后使用 Add… 按钮,指定包含可本地化字符串的项目场景和脚本的路径:

在项目设置的本地化 > 模板生成选项卡中创建 PO 模板

项目设置本地化 > 模板生成 选项卡中创建 PO 模板

添加至少一个场景或脚本后,点击右上角的 生成 ,然后指定输出文件的路径,文件扩展名为 pot。该文件可以放在项目目录中的任何位置,但建议将其保存在子目录(如 locale)中,因为每个区域设置都将在其自己的文件中定义。

请参阅下文了解如何添加面向翻译者的注释,以及如何从 GDScript 文件的 PO 模板中排除某些字符串。

然后就可以转到《从 PO 模板创建消息文件》。

备注

在对可本地化的字符串进行任何更改或添加新场景或脚本后,请记住重新生成 PO 模板。否则,新添加的字符串将不可本地化,并且翻译人员将无法更新过时字符串的翻译。

手动创建

如果该自动生成的方法不能满足你的需求,你可以在文本编辑器中手动创建 PO 模板。该文件可以放在项目目录中的任何位置,但建议将其保存在子目录中,因为每个区域设置都将在其自己的文件中定义。

在项目目录中创建一个名为 locale 的目录。在该目录中,保存一个名为 messages.pot 的文件,其内容如下:

# Don't remove the two lines below, they're required for gettext to work correctly.
msgid ""
msgstr ""

# Example of a regular string.
msgid "Hello world!"
msgstr ""

# Example of a string with pluralization.
msgid "There is %d apple."
msgid_plural "There are %d apples."
msgstr[0] ""
msgstr[1] ""

# Example of a string with a translation context.
msgctxt "Actions"
msgid "Close"
msgstr ""

gettext 中的消息由 msgidmsgstr 对组成。msgid 为源字符串(一般为英文),msgstr 为翻译后的字符串。

警告

PO 模板文件(.pot)中的 msgstr 值应始终为空。本地化会在生成的 .po 文件中进行。

从 PO 模板创建消息文件

msginit 命令用于将 PO 模板转换为消息文件。例如,要创建法语本地化文件,请在 locale 目录中使用以下命令:

msginit --no-translator --input=messages.pot --locale=fr

上面的命令会在 PO 模板所在的目录下创建一个名为 fr.po 的文件。

你也可以使用 Poedit 以图形方式完成此操作,或者通过将 POT 文件上传到你选择的 Web 平台。

在 Godot 中加载消息文件

如果要将消息文件注册为项目的翻译,请打开项目设置,然后进入本地化 > 翻译,点击添加…,然后在文件对话框中选择该 .po 或者 .mo 文件。区域设置将从消息文件的 "Language: <区域设置代码>\n" 属性中推断出来。

备注

关于在 Godot 中导入和测试翻译的更多信息,请参阅《游戏的国际化》。

按照 PO 模板更新消息文件

更新 PO 模板后,你必须更新消息文件以使其包含新字符串,同时删除已经在 PO 模板中不复存在的字符串。这可以使用 msgmerge 工具自动完成:

# The order matters: specify the message file *then* the PO template!
msgmerge --update --backup=none fr.po messages.pot

如果你想保留原始消息文件的备份(在本例中会保存为 fr.po~),请删除 --backup=none 参数。

备注

运行 msgmerge 后,在 .po 文件中,源语言中修改过的字符串的前面会添加一条“fuzzy”注释。该注释表示翻译应更新以匹配新的源字符串,因为翻译在更新之前很可能是不准确的。

Godot 不会读取带有“fuzzy”注释的字符串,直到翻译更新并删除“fuzzy”注释。

检查 PO 文件或模板的有效性

可以检查 gettext 文件的语法是否有效。

使用 Poeditor 打开文件时,若存在语法错误会显示相应警告。也可以通过以下 gettext 命令验证:

msgfmt fr.po --check

如果有语法错误或警告就会显示在控制台中。否则 msgfmt 不会输出任何内容。

使用二进制 MO 文件(仅适用于大型项目)

大型项目会有成千上万的字符串要翻译,相比于基于文本的 PO 文件,使用(编译为)二进制的 MO 消息文件可能更加划算。二进制 MO 文件比对应的 PO 文件更小、读起来更快。

可以使用以下命令生成 MO 文件:

msgfmt fr.po --no-hash -o fr.mo

如果该 PO 文件有效,该命令将在 PO 文件旁边创建一个 fr.mo 文件。然后可以按照上述方法在 Godot 中加载该 MO 文件。

原始 PO 文件应保留在版本控制中,以便你将来更新翻译。如果你丢失了原始 PO 文件,并希望将 MO 文件反编译为基于文本的 PO 文件,你可以这样做:

msgunfmt fr.mo > fr.po

反编译出的文件不包含注释和模糊字符串,因为它们一开始就没有编译进 MO 文件里。

从 GDScript 文件中提取可本地化的字符串

内置的编辑器插件能够识别源代码中的多种模式,可以从 GDScript 文件中提取可本地化的字符串,包括但不限于以下内容:

  • tr()tr_n()atr()atr_n() 的调用;

  • textplaceholder_texttooltip_text 这三个属性赋值;

  • add_tab()add_item()set_tab_title() 以及其他类似的调用;

  • FileDialog 的过滤器格式如 "*.png ; PNG 图像"

备注

参数和右操作数必须为字符串常量,否则插件无法对表达式求值,会将其忽略。

如果插件提取了不必要的字符串,你可以使用 NO_TRANSLATE 注释来忽略。你也可以使用 TRANSLATORS: 注释为翻译人员提供额外信息。这些注释必须放在与识别模式相同的行上,或者放在前一行。

$CharacterName.text = "???" # NO_TRANSLATE

# NO_TRANSLATE: Language name.
$TabContainer.set_tab_title(0, "Python")

item.text = "Tool" # TRANSLATORS: Up to 10 characters.

# TRANSLATORS: This is a reference to Lewis Carroll's poem "Jabberwocky",
# make sure to keep this as it is important to the plot.
say(tr("He took his vorpal sword in hand. The end?"))

使用上下文

context 参数可用于区分翻译的使用场景或处理多义词。

例如:

tr("Start", "Main Menu")
tr("End", "Main Menu")
tr("Shop", "Main Menu")
tr("Shop", "In Game")

在 gettext PO 文件中,可以按如下方式定义带有上下文的字符串:

# Example of a string with a translation context.
msgctxt "Main Menu"
msgid "Shop"
msgstr ""

# A different source string that is identical, but with a different context.
msgctxt "In Game"
msgid "Shop"
msgstr ""

更新 PO 文件

在将来的某个时候,你会向游戏中添加新内容,会有新的字符串需要翻译。当这种情况发生时,你需要更新现有的 PO 文件以包含新字符串。

首先,生成一个包含所有现有字符串以及新添加字符串的新 POT 文件。然后,将现有的 PO 文件与新的 POT 文件合并。具体有两种方式:

  • 使用 gettext 编辑器,这种编辑器中通常提供通过 POT 文件更新 PO 文件的选项。

  • 使用 gettext 的 msgmerge 工具:

# The order matters: specify the message file *then* the PO template!
msgmerge --update --backup=none fr.po messages.pot

如果你想保留原始消息文件的备份(在本例中会保存为 fr.po~),请删除 --backup=none 参数。

自定义 POT 生成插件

如需处理额外文件格式,可编写自定义插件来解析并提取其中的字符串。点击生成 POT 时,该插件会将提取的字符串写入 POT 文件。插件开发指南详见 EditorTranslationParserPlugin