C 多语言支持

2015-05-06 language c/cpp

可以通过 gettext 进行处理,一个在 Linux 上的 i18n 国际化常用的解决方案。

示例

在进行翻译的时候需要指定翻译哪些字符串,如下是一个简单示例。

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

#define _(string) gettext(string)

int main(void)
{
    //char *info = _("World"); // NOT WORK
    char info[64];

    setlocale(LC_ALL, "");     // OR "zh_CN.utf8"
    bindtextdomain("foobar", "langs");
    textdomain("foobar");

    strncpy(info, _("World"), sizeof(info));
    printf(_("Hello %s!\n"), info);

    return 0;
}

使用 gcc -o foobar foobar.c 生成对应二进制文件,然后通过 LC_ALL="en_US.utf8" ./foobar 或者 LC_ALL="zh_CN.utf8" ./foobar 进行测试,此时一般会输出是 Hello World!

对上述的代码比较核心的内容介绍如下。

  • setlocale(LC_ALL, ""); 设置区域,如果第二参数为 "" 表示设置成系统环境变量指定的值;
  • bindtextdomain("foobar", "langs"); 设置语言文件的查找路径,其中 langs 指定的是当前路径下的目录;
  • textdomain("foobar"); 设置当前程序需要的 domain 需要与上述的第一个参数保持一致。

如果程序使用了多个 .mo 文件,那么就需要通过 bindtextdomain() 指定多次,然后通过 textdomain() 选择对应的文件。

在 Linux i18n 中,资源文件是二进制的 .mo 文件,使用工具生成,之所以是二进制,估计是为了性能,那么,上述的 bindtextdomain() 设置,实际上就会查找 langs/foobar.mo 文件。

然后将所有要翻译的字符串写成 gettext("STR") 的形式,上述是使用的一个宏,方便进行修改。

生成翻译文件

首先生成翻译模板 (Portable Object Template) 文件 foobar.pot

$ xgettext -k_ foobar.c --add-comments --add-location --default-domain foobar \
        --no-wrap --copyright-holder="Foobar Copyright Message"               \
        --package-name="Foobar Package Name" --package-version="v1.0.0"       \
        --msgid-bugs-address="foobar@mail.com" -o foobar.pot

此时如果直接通过 msgfmt 转换会报错,可以通过如下方式初始化为 Portable Object 文件。

$ msginit --no-translator --locale zh_CN.UTF-8 --output-file foobar.po --input foobar.pot

然后,修改上述文件中的一些常见字段,包括了编辑第一作者 (FIRST AUTHOR)、翻译者 (Last Translator)、语言工作组 (Language Team) 等信息,大致如下。

msgid ""
msgstr ""
"Project-Id-Version: Foobar Package Name v1.0.0\n"
"Report-Msgid-Bugs-To: foobar@mail.com\n"
"POT-Creation-Date: 2012-06-01 19:33+0800\n"
"PO-Revision-Date: 2012-08-03 14:14+0800\n"
"Last-Translator: Your Name<foobar@email.com>\n"
"Language-Team: Simplified Chinese <foobar@hi.org>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: foobar.c:16
msgid "World"
msgstr "世界"

#: foobar.c:17
#, c-format
msgid "Hello %s!\n"
msgstr "你好 %1$s!\n"

然后创建一个子目录,用来保存生成的 .mo 文件。

# mkdir -p language/zh_CN/LC_MESSAGES
# msgfmt -o langs/zh_CN/LC_MESSAGES/foobar.mo foobar.po

如果此时再执行 foobar 文件会直接翻译成中文。

其它

可以通过 msgunfmt *.mo -o *.po 命令反向生成 po 文件,如果有新的翻译项,可以直接通过 msgmerge 合并新的 pot 文件。

注解

在使用 gettext 工具进行分析时,也可以使用一些特殊的注解,通常以 , 开头,常见的有:

  • , fuzzy 翻译可能不正确,需要重新 review 之后再删除;
  • , c-format 信息会使用到 C 语言的 printf ,例如 %d %s 等。

参考