从gettext来看linux下程序的internationalization
在看lftp的源代码的时候,看到了程序的一开头有这样几句代码:
setlocale (LC_ALL, "");
setlocale (LC_NUMERIC, "C");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
注:PACKAGE, LOCALEDIR都是宏,在autoconf的时候自动生成,PACKAGE宏就是lftp, LOCALEDIR就是/usr/share/locale
不明白有什么用,于是查了一下资料,写了一个测试程序,搞明白了。
setlocale是用来设置程序的locale的。程序可以设置任何自己喜欢的locale,但是一般linux下的程序都写成 setlocale(LC_ALL, "");这样的形式,这表示:程序的locale从环境变量中继承。setlocale这个函数本来是用来设置程序的locale的,第一个参数 LC_ALL表示要修改所有的locale变量(LANG, LC_CTYPE...),第二个参数就是要设置成什么locale。这里第二个参数是"",就表示locale信息从环境变量中继 承,setlocale以此检查LC_ALL环境变量是否设置了,如果没有依次往下检查,直到最后检查LANG环境变量。所以,简言之,这个函数的调用表 示程序继承环境变量中的locale设置。这样就可以保证,英文的linux系统将来看到的界面是英文的,中文的linux系统运行这个程序就能看到中文 的界面(当然,需要后面继续做工作)。
bindtextdomain用来设置一个给定的msgdomain,在哪个目录下查找msg domain file。这个可以通过gettext编程来理解。linux下的i18n其实和java的差不多,就是代码中写的字符串,可以用xgettext这个工 具摘出来,然后形成pot文件(portable object template),然后用命令msginit加工成po文件,最后用msgfmt将po变成mo(二进制的文件),mo就保存所有的文本资源信息,就像 java里面的properties文件一样。我们需要做的就是在po文件中,将对应英文的字符串翻译成中文的,最后程序运行,根据locale的设置, 去显式不同的字符串出来,这样就实现i18n了,很简单,right?
textdomain是用来设定当前的msgdomain的,msgdomain就可以理解成本程序使用的资源文件(mo文件)的名字,只不过需 要.mo扩展名而已。比如我们的程序资源文件是hello.mo,放在了/usr/share/locale/zh_CN/LC_MESSAGES目录 下,那么:
#define PACKAGE "hello"
#define LOCALEDIR "/usr/share/locale"
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
那么,bindtextdomain首先就是表示遇到要查找hello这个msgdomain,要到/usr/share/locale下去找; 而textdomain就显式的设定当前的msgdomain是hello,程序启动的时候,msgdomain是一个名为message的字符串,而 且,多个程序互相调用的时候(一个程序调用另外一个程序),他们的msgdomain都不一样,所以需要用textdomain来显式的指出本程序使用的 msgdomain。
这样就清楚了吧?做到了上面的内容,也有了mo文件了,那么,将来代码中只需要把字符串前面都套上一个gettext这个函数就可以实现i18n了,比如:
cout << "Hello, world" << endl;
改成:
cout << gettext("Hello, world") << endl;
这样,我们只需要准备不同locale的mo文件,放在恰当的/usr/share/locale的子目录下,然后设置好locale的环境变量,就可以让hello world变成不同语言的hello world了!
详细的gettext的做法有一篇文章,放在附件中(名为GuideArticle.txt)。此外,我写的gettext的一个测试程序也在附件中,可以参阅。
setlocale (LC_ALL, "");
setlocale (LC_NUMERIC, "C");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
注:PACKAGE, LOCALEDIR都是宏,在autoconf的时候自动生成,PACKAGE宏就是lftp, LOCALEDIR就是/usr/share/locale
不明白有什么用,于是查了一下资料,写了一个测试程序,搞明白了。
setlocale是用来设置程序的locale的。程序可以设置任何自己喜欢的locale,但是一般linux下的程序都写成 setlocale(LC_ALL, "");这样的形式,这表示:程序的locale从环境变量中继承。setlocale这个函数本来是用来设置程序的locale的,第一个参数 LC_ALL表示要修改所有的locale变量(LANG, LC_CTYPE...),第二个参数就是要设置成什么locale。这里第二个参数是"",就表示locale信息从环境变量中继 承,setlocale以此检查LC_ALL环境变量是否设置了,如果没有依次往下检查,直到最后检查LANG环境变量。所以,简言之,这个函数的调用表 示程序继承环境变量中的locale设置。这样就可以保证,英文的linux系统将来看到的界面是英文的,中文的linux系统运行这个程序就能看到中文 的界面(当然,需要后面继续做工作)。
bindtextdomain用来设置一个给定的msgdomain,在哪个目录下查找msg domain file。这个可以通过gettext编程来理解。linux下的i18n其实和java的差不多,就是代码中写的字符串,可以用xgettext这个工 具摘出来,然后形成pot文件(portable object template),然后用命令msginit加工成po文件,最后用msgfmt将po变成mo(二进制的文件),mo就保存所有的文本资源信息,就像 java里面的properties文件一样。我们需要做的就是在po文件中,将对应英文的字符串翻译成中文的,最后程序运行,根据locale的设置, 去显式不同的字符串出来,这样就实现i18n了,很简单,right?
textdomain是用来设定当前的msgdomain的,msgdomain就可以理解成本程序使用的资源文件(mo文件)的名字,只不过需 要.mo扩展名而已。比如我们的程序资源文件是hello.mo,放在了/usr/share/locale/zh_CN/LC_MESSAGES目录 下,那么:
#define PACKAGE "hello"
#define LOCALEDIR "/usr/share/locale"
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
那么,bindtextdomain首先就是表示遇到要查找hello这个msgdomain,要到/usr/share/locale下去找; 而textdomain就显式的设定当前的msgdomain是hello,程序启动的时候,msgdomain是一个名为message的字符串,而 且,多个程序互相调用的时候(一个程序调用另外一个程序),他们的msgdomain都不一样,所以需要用textdomain来显式的指出本程序使用的 msgdomain。
这样就清楚了吧?做到了上面的内容,也有了mo文件了,那么,将来代码中只需要把字符串前面都套上一个gettext这个函数就可以实现i18n了,比如:
cout << "Hello, world" << endl;
改成:
cout << gettext("Hello, world") << endl;
这样,我们只需要准备不同locale的mo文件,放在恰当的/usr/share/locale的子目录下,然后设置好locale的环境变量,就可以让hello world变成不同语言的hello world了!
详细的gettext的做法有一篇文章,放在附件中(名为GuideArticle.txt)。此外,我写的gettext的一个测试程序也在附件中,可以参阅。