使用 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 "]; }

也參考

GNU gettext 概覽

樣本程式

使用 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.

The just created cs.po already has some information filled in. Most importantly it got the proper plural forms definition for chosen language and you can see number of plurals have changed according to that:

# 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

備註

When using Weblate, you typically don't need to run msgmerge manually. Weblate can handle this automatically through the 更新 PO 檔案以符合 POT (msgmerge) add-on, or you can upload the updated POT file using the Update source strings upload method. See 更新目標語言檔案 for details.

匯入到 Weblate

為了將這樣的翻譯匯入 Weblate,當新增組件時,所有需要您定義的是後面的欄位(欄位的具體描述請參閱 組件配置):

欄位

原始碼儲存庫

您的專案的版本控制系統(VCS)儲存庫的 URL

檔案遮罩

po/*.po

新翻譯的範本

po/hello.pot

檔案格式

選擇 gettext PO 檔案

新語言

選擇 Create new language file (新增新語言文件)

就是這樣,您現在已準備好開始翻譯您的軟體了!

也參考

您可以在 GitHub: <https://github.com/WeblateOrg/hello> 上的 Weblate Hello 專案中找到帶有很多語言的 gettext 範例。