支持多语言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);
其中:
- mo文件位置一般是dirname/locale/category/domainname.mo。
- bindtextdomain与textdomain中的domainname参数必须一致,为mo文件的文件名,不带.mo后缀。
使用示例
以输出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