发布于 ,更新于 

使用 GNU gettext 实现程序的多国语言本地化

使用 GNU gettext 实现程序的多国语言本地化

1. 基本操作

gettext 是 GNU 出品的一个项目,主要用于将应用程序本地化和国际化的一个实用套件。

首先,我们的程序,如果在源代码中写死了字符串,则我们无法对这个字符串进行国际化和本地化,例如:

1
2
3
4
int main()
{
printf("hello world. \n");
}

上面的程序,无论在什么语言版本的系统中,其显示的都是英文的字符串 “hello world” 。

gettext 的方法是,需要进行国际化与本地化的字符串中,全部加上一个叫 gettext() 的函数调用,如下:

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

#define PACKAGE "test1" // 语言字符串资源包名

int main()
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, "locale");
textdomain(PACKAGE);

printf("%s\n", gettext("I come from China."));
printf("%s\n", gettext("I like Java language."));
// 上面的 gettext() 可以用宏来代替,进行简化,例如:
// #define _(str) gettext(str)
// #define N(str) gettext(str)
// 等等
return 0;
}
// 使用如下命令编译:
// gcc -o a1 a1.c

上面示范程序中的字符串 “I come from China.” 和 “I like Java language.” ,在使用之前都加上了 gettext() 函数进行包裹。gettext() 函数的作用是:以原始字符串作为 key,去加载并获取到并返回其对应语言的的本地字符串。

使用 xgettext 命令,将源文件中被 gettext() 函数包裹的字符串全部抓出来,命令如下(用 test1 作为语言包名):

1
xgettext  a1.c  -o test1.po

xgettext 命令可以指定多个原文文件,输出的 .po 文件是一个文本文件,里面包含了需要翻译的所有字符串,文件名是上面源码中指定的对应的语言资源包 PACKAGE 名。

打开 test1.po 文件,把字符编码设置为 UTF-8,将里面的 "Content-Type: text/plain; charset=CHARSET\n" 改为 "Content-Type: text/plain; charset=UTF-8\n"。然后,对相应的文本进行翻译:

1
2
3
4
5
6
7
#: a1.c:13
msgid "I come from China."
msgstr ""

#: a1.c:14
msgid "I like Java language."
msgstr ""

上面的 msgid 就是原始字符串,作为 key,不要修改,下面的 msgstr 则应该修改,将翻译过去的文本填进去,如下:

1
2
3
4
5
6
7
#: a1.c:13
msgid "I come from China."
msgstr "我来自中国。"

#: a1.c:14
msgid "I like Java language."
msgstr "我喜欢 Java 语言"

然后使用 msgfmt 命令将 .po 文件编译为 gettext() 函数可以识别的 .mo 文件,命令如下:

1
msgfmt  test1.po  -o  test1.mo

由于上面 C 程序中指定了 .mo 资源的加载路径,语句 bindtextdomain(PACKAGE, "locale"); 指定了 .mo 存放在当前目录的 locale 子目录下面。所以,把生成的 test1.mo 文件放在当前目录的如下路径下:

1
locale/zh_CN/LC_MESSAGES/test1.mo

这里假设系统为简体中文,当前 locale 设置为 zh_CN,上面 C 程序的语句 setlocale(LC_ALL, ""); 就是使用系统默认的语言环境。执行上面 C 程序 ./a1 得到结果为:

1
2
我来自中国。
我喜欢 Java 语言

上面例子说明了 gettext 套件的基本使用流程。

2. 批量操作

xgettext 命令可以一次性指定多个输入源文件,如:

1
xgettext  a1.c  a2.c  a3.c  -o  package.po

xgettext 命令也可以通配符,如:

1
xgettext  *.c  -o  package.po

可以把所有需要提取的源文件放在一个列表中(list1.txt 中列出所有源文件名):

1
xgettext -f list1.txt -o package.po

增加其他关键字,例如 增加 N() 函数,则 N(“string”) 也会被抓取:

1
xgettext  *.c -kN -o  package.po

如果已经翻译好了字符串,后来源代码发生了更改,增加了一些新的字符串,可以使用 -j 选项,把新增的字符串追加到已经存在的翻译好的 .po 文件中,例如:

1
xgettext  a1.c  a2.c  a3.c  -j  -o package.po

msginit 命令可以把 xgettext 生成的 .po 文件(POT)转换成对应语言的翻译文本 .po (会修改好一些编码与语言的相关字段):

1
msginit  --no-translator --locale zh_CN -o package_new.po -i package.po

可以用 msgmerge 命令把新生成的 .po 合并到已经翻译好的 .po 文件中:

1
msgmerge  -o package.po  locale/zh_CN/LC_MESSAGES/package.po  package.po