在项目上,开发基于USBKEY的双因子认证功能时要提示用户输入PIN码,因为我们是基于CentOS 7.4的中文版开发的,所以想给用户输出中文提示。查了一些资料,发现CentOS系统的每个源码包里都有一个po目录,这个文件里面是一些.po文件,比如zh_CN.po这个文件是简体中文对应的翻译文件。在CentOS系统的/usr/share/locale/目录下有系统支持所有的语言翻译包对应的二进制文件,我们进去zh_CN目录下的LC_MESSAGES目录里可以看到有多个mo文件,这些mo文件就是po文件的二进制形式,比如shadow.mo就是shadow模块对应的简体中文的翻译、Linux-PAM.mo是pam模块对应简体中文的翻译。
这些zh_CN.po是怎么生成的?mo文件又是怎么生成的呢?
1.po文件
po文件是通过xgettext工具生成的,从指定文件中获取要翻译的字符串,命令如下所示:
xgettext -f POTFILEE.in -d test –keyword=_ –keyword=N_ –from-code=UTF-8
-f:参数指定文件列表,可以使用命令生成:find src/ -name “*.c” >po/POTFILEE.in
-d:指定默认域,这个域的概念很关键,被困扰了很久,具体后面再介绍
–keyword:指定关键字,一般我们把要翻译的字符串用关键字包裹,例如:printf(_(“hello world”));
–from-code:指定输入文件的编码格式
执行完xgettext命令后输出test.po文件,该文件是一些msgid和msgstr对,msgid是要翻译的内容,msgstr需要人工翻译后填入。
紧接着就需要手动修改test.po文件了:
1.修改charset的值:charset=UTF-8
2.修改要翻译的内容,例如:
msgid “hello world”
msgstr “你好, 世界”
2.mo文件
.po文件修改好以后就要编译po文件生成mo文件,使用的工具是msgfmt。
例如:将test.po文件编译生成test.mo文件就用下面的命令:
msgfmt -o test.po
将编译好的test.mo文件拷贝到/usr/share/locale/zh_CN/LC_MESSAGES目录,因为我这里是英汉之间的翻译,所以将test.mo文件放到zh_CN目录下的LC_MESSAGES目录。
3.应用
3.1.在源代码中要增加头文件和宏
#define LOCALEDIR “/usr/share/locale”
#define PACKAGE “test”
#include <libintl.h>
#define (msgid) dgettext(PACKAGE, msgid)
#define N(msgid) msgid
上面的宏PACKAGE必须是test,这个值就是xgettext工具的-d选项指定的域,两者必须一致。
3.2.设置
bindtextdomain(domainname, dirname);
bind_textdomain_codeset(domainname, “UTF-8”);
textdomain(domainname);
setlocale(LC_ALL, “”);
3.3.解释
上面设置中的几个函数都可以通过man命令查看帮助文档。
1.bindtextdomain函数用来设置包含消息条目的路径,比如应用程序的翻译文件的路径名为:dirname/locale/category/domainname.mo,其中locale是locale名,比如中文就是zh_CN,category是locale面,比如LC_MESSAGES。前面我们提到的xgettext指令的-d选项指定默认域,这里第一个参数domainname就是-d指定的内容,两者必须一致,否则程序无法找到这个翻译文件,其实就是上面说的宏PACKAGE。
2.用bind_textdomain_codeset函数设置翻译文件的编码格式为UTF-8。
3.textdomain函数切换当前应用程序的domainname,意思是设置使用哪个翻译文件。
4.setlocale函数设置当前的区域选项,第一个参数category – 这是一个已命名的常量,指定了受区域设置影响的函数类别,其值可以下面中的一种:
LC_ALL 包括下面的所有选项。
LC_COLLATE 字符串比较。参见 strcoll()。
LC_CTYPE 字符分类和转换。例如 strtoupper()。
LC_MONETARY 货币格式,针对 localeconv()。
LC_NUMERIC 小数点分隔符,针对 localeconv()。
LC_TIME 日期和时间格式,针对 strftime()。
LC_MESSAGES 代表可以本地化的消息。
第二个参数是”“空字符串,表示根据当前的环境变量的locale。可以在终端中执行locale命令查看当前环境变量的locale值。
比如我装的系统是CentOS中文版本,执行locale后,上述category的值都是zh_CN.UTF-8。
上面几个函数执行完成后,我的应用程序会去/usr/share/locale/zh_CN/LC_MESSAGES/目录下找test.mo。
4.结论
主程序执行printf(_(“hello world”));语句时输出:你好,世界