gettext 国际化使用总结

#gettext 是什么

gettext 是 GNU 的国际化与本地化 (i18n) 函数库。它常被用于编写多语言程序。

GNU gettext 支持 C、C++、Objective-C、Pascal/Object Pascal、sh 脚本、bash 脚本、Python、GNU CLISP,Emacs Lisp、ibrep、GNU Smalltalk、Java、GNU awk、wxWidgets(通过 wxLocale 类)、YCP(YaST2 语言)、Tcl、Perl、PHP、Pike、Ruby 以及 R。

具体来说,GNU gettext 是一组工具,其他软件包可以在其中生成国际化消息。这些工具包括:

  • 一套关于如何编写程序以支持消息目录的约定: mo、po 和 pot 文件格式。
  • 消息目录本身的目录和文件命名组织: locale 目录结构。
  • 一个支持检索翻译消息的运行时库: gettext 库。
  • 一些独立的程序,用于以各种方式处理可翻译字符串集或已翻译字符串: xgettext、msgmerge、msgfmt 等。
  • 一个支持解析和创建包含翻译消息的文件的库。
  • Emacs 的一个特殊模式,它有助于准备这些集合并使其保持最新。

#gettext i18n 使用流程

  1. 在源代码中绑定语言文件,并使用 gettext 函数获取翻译后的字符串。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <libintl.h>
    #include <locale.h>
    #include <stdio.h>

    #define PACKAGE "myprogram"
    #define LOCALEDIR "." // default to "/usr/local/share/locale"
    #define _(String) gettext(String)

    int main() {
    // 设置当前环境的本地化信息
    setlocale(LC_ALL, "");

    // 将 domain 绑定到本地目录 LOCALEDIR
    // gettext 将使用路径: LOCALEDIR/language/LC_MESSAGES/PACKAGE.mo 查找 .mo 文件
    // 不以前缀 '/' 开头时,LOCALEDIR 被认为是相对路径
    bindtextdomain(PACKAGE, LOCALEDIR);

    // 设置当前使用的 domain
    textdomain(PACKAGE);

    printf(_("Hello, World!\n"));
    }

    libintl.h 头文件定义了 gettext 函数。宏 _() 用于标记需要翻译的字符串。

    libintl 已经包含在 glibc 中,因此链接 glibc 时无需额外链接。

  2. 使用 xgettext 工具从源代码中提取标记的字符串,生成一个 POT (Portable Object Template) 文件:

    1
    xgettext -k_ -o myprogram.pot myprogram.c

    文件 myprogram.pot 将包含所有需要翻译的字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 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: 2025-11-09 13:06+0800\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"

    #: myprogram.c:21
    #, c-format
    msgid "Hello, World!\n"
    msgstr ""
  3. 为需要翻译的目标语言创建/更新对应的 PO (Portable Object) 文件:

    如果是第一次创建某个语言的 PO 文件,可以使用 msginit 工具:

    1
    msginit -l zh_CN -i myprogram.pot -o zh_CN.po

    如果 PO 文件已经存在(已经有一部分翻译,程序中增加了新的条目),可以使用 msgmerge 工具更新:

    1
    msgmerge -U zh_CN.po myprogram.pot
  4. 翻译人员进行翻译,更新 LANG.po 文件中的 msgstr 字段。

    1
    2
    3
    4
    #: myprogram.c:21
    #, c-format
    msgid "Hello, World!\n"
    msgstr "你好,世界!\n"
  5. 使用 msgfmt 工具将 PO 文件编译为供程序读取的 MO (Machine Object) 文件:

    1
    msgfmt zh_CN.po -o zh_CN.mo

    MO 文件是二进制格式,供程序在运行时使用。

  6. 将生成的 MO 文件安装到系统中,例如:

    1
    install -Dm644 zh_CN.mo ./zh_CN/LC_MESSAGES/myprogram.mo
  7. 运行程序时,设置正确的环境变量,即可切换到对应的语言:

    1
    2
    3
    4
    5
    $ LANG=zh_CN.UTF-8 ./myprogram
    你好,世界!

    $ LANG=en ./myprogram
    Hello, World!