Linux下的本地化与国际化 – 2. gettext

GNU gettext 是一套GNU下的国际化工具,几乎支持所有Linux下的计算机语言。在gettext的帮助下,可以轻松完成程序的国际化。

一、相关文件

.pot
po 的模版,可由xgettext生成
.po
翻译的源,可由msginit + .pot生成
.gmo
编译后的po,可由 msgfmt + .po 生成
.mo
同上,只是gmo通常指的是未安装的mo,可由 install + .gmo 生成

二、相关工具

xgettext
从源代码中提取需要翻译的字串,生成pot文件
msginit
替换pot中的Entry信息,如译者,文件编码等
msgmerge
合并现有的.po文件
msgfmt
把.po文件生成.gmo或.mo文件

三、相关函数

bindtextdomain(const char *domain, const char *dirname)
通过domain与dirname绑定mo文件的域
textdomain (const char *domainname)
指定gettext所用的域
gettext(const char *msgid):
由locale与textdomain指定的domain得到翻译后的字串
ngettext(const char *msgid1, const char *msgid2, unsigned long int n):
通过n返回单数的msgid1 或 复数的msgid2
dgettext(const char *domain, const char *msgid)
指定domain的gettext
dngettext(const char *domain, const char *msgid, unsigned long int n);
指定domain的ngettext
dcgettext(const char *domain, const char *msgid, int category):
指定domain与category的gettext
dcngettext(const char *domain, const char *msgid1, const char *msgid2, unsigned long int n, int category):
指定domain与category的ngettext

gettext的dirname, locale domain, category, 在磁盘上的结构为
dirname/locale/category/domain.mo

/usr/share/locale/zh_CN/LC_MESSAGES/gettext_demo.mo
默认的category为LC_MESSAGES

四、实例

1. 新建文件 gettext_demo.c

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

#define _(string) gettext(string)

const char *DOMAIN  = "gettext_demo";
const char *DIRNAME = "languages"; 

int main(int argc, char **argv) {

  /* 设置locale与系统一致 */
  setlocale(LC_ALL, "");

  /* 添加域 gettext_test 在目录 po */
  bindtextdomain(DOMAIN, DIRNAME);

  /* 设定默认域为 gettext_test */
  textdomain(DOMAIN);

  /* gettext(Hello World) */
  puts(_("Hello"));
  return 0;
}

2. 制作zh_CN.UTF-8的语言包

gettext_demo$ ls
gettext_demo.c
gettext_demo$ mkdir po
gettext_demo$ cd po

由xgettext生成pot
gettext_demo/po$ xgettext -k_  --package-name gettext_demo --package-version 0.1 ../*.c  -o gettext_demo.pot
gettext_demo/po$ ls
gettext_demo.pot

由gettext_demo.pot生成zh_CN.po
gettext_demo/po$ msginit #因为我的系统locale为zh_CN.UTF-8,因此msginit自动生成针对zh_CN.UTF-8的po
gettext_demo/po$ ls
gettext_demo.pot  zh_CN.po

修改po文件
#: ../gettext_demo.c:22
msgid "Hello"
msgstr ""
为
#: ../gettext_demo.c:22
msgid "Hello"
msgstr "你好"

由zh_CN.po生成zh_CN.mo
gettext_demo/po$ msgfmt zh_CN.po -o zh_CN.mo
gettext_demo/po$ ls
gettext_demo.pot  zh_CN.mo  zh_CN.po

复制zh_CN.mo到../languages/zh_CN/LC_MESSAGES/gettext_demo.mo
gettext_demo/po$ mkdir -p ../languages/zh_CN/LC_MESSAGES
gettext_demo/po$ cp zh_CN.mo ../languages/zh_CN/LC_MESSAGES/gettext_demo.mo 

编译并测试
gettext_demo$ gcc gettext_demo.c -o gettext_demo
gettext_demo$ ls
gettext_demo  gettext_demo.c  languages  po
gettext_demo$ ./gettext_demo
你好

测试英文环境
gettext_demo$ LC_ALL=en_US ./gettext_demo
Hello
如果尝试 LC_ALL=zh_CN.GBK ./gettext_demo 在UTF-8环下显示"乱码",因为显示的是GBK编码,gettext转换到相应的编码了。</pre>
当系统发生变化时,添加了新的语句,为此通过工具msgmerge进行和并
修改 gettext_demo.c
/* gettext(Hello World) */
puts(_("Hello"));
为
/* gettext(Hello World) */
puts(_("Hello"));
puts(_("World"));

gettext_demo/po$ xgettext -k_  --package-name gettext_demo --package-version 0.1 ../*.c  -o gettext_demo.pot
gettext_demo/po$ msgmerge zh_CN.po gettext_demo.pot -o zh_CN.po
. 完成。

修改文件 zh_CN.po
#: ../gettext_demo.c:23
msgid "World"
msgstr "世界"

得新生成并复制zh_CN.mo到../languages/zh_CN/LC_MESSAGES/gettext_demo.mo
测试
gettext_demo$ ./gettext_demo
你好
世界

五、相关链接

GNU gettext 官方文档

共6条评论
  1. richard_ma @ 2010-12-24 09:27:01 回复

    我按照您的方法实验过了,可是最后运行的时候不能出线中文字符。
    系统是Ubuntu 10.04,安装了gettext包
    locale使用zh_CN.utf8: export LANG=zh_CN.utf8
    不知道是哪里出了问题,已经看过很多教程了,可是没有一个能成功的。

    • JiangMiao @ 2010-12-24 17:53:35

      可否贴出源码?

  2. richard_ma @ 2010-12-25 23:12:48 回复

    就是复制程序代码,如何修改locale呢,我的修改方法正确么?
    我默认的locale是en_US.utf8 LANGUAGE=en这个有关系么?

    • JiangMiao @ 2010-12-27 00:35:27

      可能是你相应locale没有安装。
      locale -a 查看
      或者是LC_MESSAGES的locale不正确
      gettext一般使用的是LC_MESSAGES,本文例子也是。
      可以指定LC_MESSAGES = zh_CN.UTF-8
      或者
      LC_ALL = zh_CN.UTF-8
      更多关于locale的信息可以参见我的另外一篇文章
      http://www.jiangmiao.org/blog/1265.html

  3. ZHaoSHuai @ 2016-12-29 18:48:49 回复

    我用的 debian 6 locale使用zh_CN.utf8,测试下来 都没问题,就是最后再设置成英文LC_ALL=en_US时,并执行程序,还是显示中文 ,代码是你的测试源码

    • ZHaoSHuai @ 2016-12-30 13:24:02

      把xshell 的 编码由 gb2312 改成 默认的 utf-8 就好了

发表评论

电子邮件地址不会被公开。 必填项已用*标注