支持多语言gettext

有时,程序展现给用户的信息需要支持多种语言,并且允许在语言之间做灵活的切换,最为常见的场景是支持中英文,GNU的gettext是一个支持多语言的成熟解决方案。

编程接口

使用gettext需要用到的API如下所示:

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

char* setlocale(int category, const char *locale);
char* bindtextdomain(const char *domainname, const char *dirname);
char* textdomain(const char *domainname);

其中:

使用示例

以输出hello为例,以下是相关源码hello.c,假设mo文件取名为scott.mo。

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

#define _(str) gettext(str)

int main(int argc, char *argv[]) {
    setlocale(LC_ALL, "");
    bindtextdomain("scott", "lang");
    textdomain("scott");
    puts(_("hello"));
    return 0;
}

编译时需要链上intl库。

gcc -o hello hello.c -g -Wall -lintl

通过xgettext工具从代码中自动提取需要翻译的内容,得到scott.po文件。

$ xgettext -k_ hello.c -o scott.po

$ cat  scott.po
# 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: 2018-06-19 10:17+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"

#: hello.c:11
msgid "hello"
msgstr ""

$ mkdir -p lang/{en_US,zh_CN}/LC_MESSAGES
$ cp scott.po lang/en_US/LC_MESSAGES
$ cp scott.po lang/zh_CN/LC_MESSAGES

修改各po文件的msgstr项,设成翻译后的内容。作为例子,这里生成了zh_CN和en_US两个po文件,其中zh_CN的scott.po里msgstr置成”say hello in chinese”, 而en_US的scott.po设为”say hello in english”。另外,默认生成的po文件charset未设置,建议改成UTF-8。

接着根据po文本文件成mo二进制文件。

$ msgfmt scott.po -o scott.mo

最后,运行程序,将根据当前语言输出对应的内容。

$ echo $LANG
zh_CN.UTF-8
$ ./hello 
say hello in chinese
$ export LANG=en_US
$ ./hello 
say hello in english
Table of Contents