使用 GNU gettext 来翻译软件

GNU gettext 是使用最广泛的自由软件国际化工具之一。它为软件的本地化提供了一种简单而灵活的方法。它对复数有很好的支持,可以为已翻译字符串添加更多的上下文,并且有很多围绕它构建的工具。当然,它在 Weblate 中有很好的支持(请参见 GNU gettext PO (Portable Object) 文件格式说明)。

备注

如果您打算在专有软件中使用它,请先查阅许可证,它可能不适合您。

GNU gettext 可以用于多种语言(C、Python、PHP、Ruby、JavaScript 等等),通常 UI 框架已经提供了对其的一些支持。标准用法是通过 gettext() 函数调用,该函数通常具有 _() 别名,以使代码更简单易读。

此外,它提供了 pgettext() 调用,可用于为译者提供更多的上下文;还提供了 ngettext(),可以处理目标语言定义的复数类型。

作为广泛传播的工具,它具有很多封装,使其使用相当简单,你也许会想尝试其中之一,例如 intltool,而非下面描述的对 gettext 的手动调用。

工作流程概览

GNU gettext 使用几个文件来管理本地化工作:

  • PACKAGE.pot 包含从您的源代码中提取的字符串,典型地使用 xgettext 或一些高级封装,如 intltool

  • LANGUAGE.po 包含翻译为单一语言的字符串。一旦 PACKAGE.pot 更新,那么必须由 msgmerge 来更新它。您可以使用 msginit 或在 Weblate 内新建新的语言文件。

  • LANGUAGE.mo 包含 LANGUAGE.po 的二进制表达,并且应用运行时使用。它典型地并不保持在版本控制之下,但在编译时使用 msgfmt 生成。在您想要使它处于版本控制中时,可以使用 生成 MO 文件 附加组件在 Weblate 中生成它。

GNU gettext 总体工作流程如下所示:

digraph translations { graph [fontname = "sans-serif", fontsize=10]; node [fontname = "sans-serif", fontsize=10, shape=note, margin=0.1, height=0]; edge [fontname = "monospace", fontsize=10]; "Source code" -> "PACKAGE.pot" [label=" xgettext "]; "PACKAGE.pot" -> "LANGUAGE.po" [label=" msgmerge "]; "LANGUAGE.po" -> "LANGUAGE.mo" [label=" msgfmt "]; }

示例程序

使用 gettext 的 C 语言简单程序看起来会像后面这样:

#include <libintl.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int count = 1;
    setlocale(LC_ALL, "");
    bindtextdomain("hello", "/usr/share/locale");
    textdomain("hello");
    printf(
        ngettext(
            "Orangutan has %d banana.\n",
            "Orangutan has %d bananas.\n",
            count
        ),
        count
    );
    printf("%s\n", gettext("Thank you for using Weblate."));
    exit(0);
}

提取可翻译的字符串

一旦您具有了使用 gettext 调用的代码,您可以使用 xgettext 从中提取消息,并将它们存储在 .pot 中:

$ xgettext main.c -o po/hello.pot

备注

有另外的程序从代码中提取字符串,例如 pybabel

这新建了模板文件,您可以用它来开始新的翻译(使用 msginit),或在代码更改后更新现有的翻译(为此您将使用 msgmerge)。结果文件是简单的结构化文本文件:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-23 11:02+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#: main.c:14
#, c-format
msgid "Orangutan has %d banana.\n"
msgid_plural "Orangutan has %d bananas.\n"
msgstr[0] ""
msgstr[1] ""

#: main.c:20
msgid "Thank you for using Weblate."
msgstr ""

每个 msgid 行定义了要翻译的字符串,开头的特殊空字符串是包含关于翻译的元数据的文件标头。

开始新的翻译

准备好模板后,我们就可以开始我们的第一个翻译了:

$ msginit -i po/hello.pot -l cs --no-translator -o po/cs.po
Created cs.po.

刚刚创建的 cs.po 已经填写了一些信息。最重要的是,它为所选语言提供了正确的复数形式定义,您可以看到复数数量也随之改变:

# Czech translations for PACKAGE package.
# Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-23 11:02+0200\n"
"PO-Revision-Date: 2015-10-23 11:02+0200\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"

#: main.c:14
#, c-format
msgid "Orangutan has %d banana.\n"
msgid_plural "Orangutan has %d bananas.\n"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""

#: main.c:20
msgid "Thank you for using Weblate."
msgstr ""

这个文件被编译为优化的二进制形式,.mo 文件,由 GNU gettext 函数在运行时使用。时使用。

更新字符串

一旦在您的程序中添加了更多的字符串或更改了一些字符串,您再次执行 xgettext,生成模板文件:

$ xgettext main.c -o po/hello.pot

然后您可以更新各自的翻译文件来匹配新建的模板(这包括重新排序字符串,赖于新的模板匹配):

$ msgmerge --previous --update po/cs.po po/hello.pot

导入到 Weblate

要将这样的翻译导入 Weblate,您只需要在创建部件时定义以下字段(字段的详细说明,请参见 部件配置):

字段

源代码仓库

您的项目的版本控制系统仓库的 URL

文件掩码

po/*.po

新语种的翻译模版

po/hello.pot

文件格式

选择 gettext PO 文件

新语言

选择 创建新语言文件

就这样,现在您可以开始翻译您的软件了!

参见

你可以在 GitHub 上的 Weblate Hello 项目中找到包含多种语言的 gettext 示例:<https://github.com/WeblateOrg/hello>。