GNU软件本地化技术初探

GNU软件本地化技术初探
最近在阅读coreutils下面的一些工具的源码,对其中采用的本地化(国际化)技术进行了一些学习,在这里简单的进行了整理,希望能起到抛砖引玉的作用.

这其中涉及到一些开发的技术,但重点在于介绍本地化技术,这些对有兴趣成为GNU软件翻译者(可能是非开发人员)也是有一定帮助的,当然如果您是软件开发者,那么GNU中采用的本地化技术对您也会有一定借鉴意义
.

我们的目标很简单:实现Hello, World的国际化 --- 开发软件的人不必关必太多各国语言版本的问题,翻译人员不必关心开发的问题
.

在这里,先引出一个概念MESSAGE DOMAIN, 它表现为一个.mo文件,包含有原文及翻译的集合的信息
.
比如有一个叫zh_CN/LC_MESSAGES/testlocale.mo MESSAGE DOMAIN,它可能包含类似的信息

代码:

#简体中文翻译

原文: "Hello, World!"

译文: "你好, 世界!"

类似,可能还有一个ja/LC_MESSAGES/testlocale.mo

代码:

#日语

原文: "Hello, World!"

译文: "#$%^&)(*&!"

:.mo文件是经编译的二进制信息

有了包括这样信息的文件,GNU软件就可能利用这个MESSAGE DOMAIN(.mo文件)实现软件的国际化.
但有个疑问
:
某个软件到底是采用哪个MESSAGE DOMAIN进行本地化呢?要知道在你的系统中可能存在成千上万个MESSAGE DOMAIN(每个软件都可能对应多种语言的多份
.mo)


好了,现在介绍几个函数

代码:

#include <libintl.h>

char * bindtextdomain (const char * domainname, const char * dirname);

指定某个MESSAGE DOMAIN的位置, 其中domainname是它的名称(文件名去掉后缀),dirname 所在位置,这里有点注意的地方
MESSAGE DOMAIN
文件的存放位置是这样的: dirname/语言类(zh_CN,ja)/LOCALE的分类(LC_MESSAGE LC_TIME)/xxx.mo
我机器上的目录结构是这样的

/home/coder/testlocaledir/zh_CN/LC_MESSAGES/testlocale.mo
/home/coder/testlocaledir/ja/LC_MESSAGES/testlocale.mo
所以在调用这个函数时传入的dirname/home/coder/testlocaledir
第二个函数

代码:

char * textdomain (const char * domainname);

让你的程序使用某一个domain(也可以在具体调用翻译函数时指定,如下文),这样配合前面的bindtextdomain函数,就能找到要到哪个dirname下去找.mo文件了.
dirname下有 很多子目录 zh_CN ja fr .. 要采用哪个目录下哪个LOCALE分类下的文件呢?(这也是上面的第二个问题)

代码:

 char * dcgettext (const char * domainname, const char * msgid,int category);

这个函数是翻译函数,通过传入原文msgid,返回经过翻译后的译文. 其中 domainname 如果是NULL则采用之前textdomain选定的MESSAGE DOMAIN, int category是指LOCALE分类,系统通过它来确定要翻译成哪种语言
category
可能为LC_MESSAGE LC_TIME,它的意义是说通过得到本程序指定LOCALTE分类(int category)的当前值 (zh_CN, C, ..)来确定将原消息msgid翻译成哪种语言,这样,在程序调用dcgettext,系统会先通过domainname确定dirname,然后通过程序中指定LOCALE分类(LC_MESSAGE)的值如zh_CN.GB2312 确定下一及目录 zh_CN,再加上LC_MESSAGE这样就确定了.mo文件的位置, 在这个位置下的domainname.mo就是存有翻译信息的文件.(如果locale类为C或是没找到.mo,则不翻译)

dcgettext
同族中还有一个简单的

char * gettext (const char * msgid);
它的意思是使用之前textdomain调用时指定的domain, LOCALE类别使用LC_MESSAGE
程序中的LC_MESSAGE LC_TIME等的值是如何设定的呢?

代码:

 #include <locale.h>

 char *setlocale(int category, const char *locale);

其中 char *locale 是设定category对应的值,zh_CN.GB2312 zh_CN.GBK,可用的值可以通过 $locale -l 查看.
如果locale"",则采用环境变量中的值
.

说得比较乱,可能有些朋友还有些糊涂.看例子:

代码:

#include <locale.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <libintl.h>

 

#define TEXTDOMAIN "testlocale"

#define TEXTDOMAINDIR "/home/coder/progs/testlocaledir"

int

main(int argc, char *argv[])

{

         setlocale(LC_ALL, "");

         bindtextdomain(TEXTDOMAIN, TEXTDOMAINDIR);

         textdomain(TEXTDOMAIN);

 

         printf("%s/n",gettext("Hello, World!"));

         return 0;

}

其中setlocale(LC_ALL, "");LC_ALL(即所有的分类),设置为与当前环境变量中的locale值一至(具体的对应方式man 3 setlocale).
如果在程序运行前, EXPORT LC_ALL=zh_CN.GB2312 则在这句代码执行后程序中的locale 各项值(LC_MESSAGE LC_TIME ..)都被设为
zh_CN.GB2312.
bindtextdomain
一句是设定了 testlocale 这个MESSAGE DOMAIN的位置
.
textdomain
一句是说程序中使用名称为testlocale
DOMAIN.
gettext("Hello, World!")
通过LC_MESSAGE的值zh_CN.GB2312 确定.mo文件在
/home/coder/progs/testlocaledir/zh_CN/LC_MESSAGES/testlocale.mo
这样函数就返回了"世界,你好
!"

这样的程序在设计时,设计者只需要调用这几个函数就不用关心翻译的细节了[可能过宏定义#define _(X) gettext(X),这样有翻译需要的地方只需要_("string")即可
].
翻译者也不必去了解程序代码,只需要把需要翻译的字串提出来加上翻译后的字串形成.mo文件放到指定位置即可
.

开始时我们提过.mo是编译后的二进制,它是怎么制作的呢
?
有一个工具叫msgfmt,能把源文件.po 编译成目标文件 .mo ,如我们用的中文po 文件

$more testlocale.po

msgid "Hello, World!"
msgstr "
你好, 世界!"

$msgfmt testlocale.po
把生成的 messages.mo mv
/home/code/progs/testlocaledir/zh_CN/LC_MESSAGES/testlocale.mo

这样全部工作就完成了.

代码:

coder@deb3:~/progs$ export LC_ALL=C

coder@deb3:~/progs$ ./testlocale

Hello, World!

coder@deb3:~/progs$ export LC_ALL=zh_CN.GB2312

coder@deb3:~/progs$ ./testlocale

你好, 世界!

有其它语种翻译需要时只需要制作po编译成mo放到指定位置即可.

(
)

 

 

GNU libiconv-1.15和libintl-0.19.8.1,VS2015工程和 Makefile 命令行编译文件 CSDN-tags: libiconv1.15 libintl 0.19.8.1 VS2015 Makefile 声明: 代码为本人良心制作,虽然花费了一定的时间和精力,但不保证完全没有错误。如果您下载并使用了本代码,将其包含在您发布的应用中,给您带来了经济上,心理上,生活上的损失,本人不负有责任。 所有代码都基于官网进行修改,iconv的代码来源于libiconv-1.15的lib目录,intl的代码来源于gettext-runtime的intl目录。 intl需要iconv的支持,如果想单独编译不需要iconv的intl, 请自行修改intl目录下的config.h文件, /* Define if you have the iconv() function and it works. */ #define HAVE_ICONV 1 /* Define to 1 if you have the header file. */ #define HAVE_ICONV_H 1 为: /* Define if you have the iconv() function and it works. */ /* #undef HAVE_ICONV */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ICONV_H */ 并修改VS工程中预处理器定义,删掉/DDEPENDS_ON_LIBICONV=1 VS的工程为VS2015的工程文件,使用VC140工具集,如果没有2015,也可以用Makefile.mak文件来编译。 nmake /f Makefile.mak [DLL] [DEBUG] 指定 DLL=1 编译dll版本,默认是lib版本,libiconv.lib , libintl.lib。 指定 DEBUG=1 编译debug版本,默认是release版本。 同理,如果intl不需要iconv,请修改上面的配置文件选项,并, 删掉Makefile.mak中/DDEPENDS_ON_LIBICONV=1 配置文件的制作,参考了以下几处,修改的大概原则是,能定义的全定义了,就这样: https://github.com/kahrl/gettext-msvc 这个地址提供了libiconv和libintl的VS工程和配置文件。 https://github.com/winlibs 为编译PHP而修改的libiconv和libintl源码,libiconv是最新1.15版本,gettext不是。 附带的Cygwin目录中的工具 libiconv官方的制作文档,用cygwin配合VC,生成的配置文件。 一大堆的宏和函数搞得我头晕脑涨。所以配置文件如果有错误,也在所难免,所有修改的文件,如config.h,iconv.h,libintl.h都附带了原始文件。 如: 配置文件 原始文件 DLL原始文件 LIB原始文件 config.h config.h.in iconv.h iconv.h.build.in iconv.h.in localcharset.h localcharset.h.build.in localcharset.h.in libgnuintl.h libgnuintl.in.h 欢迎同学们下载测试。有问题可以给我发邮件travel981cn@139.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值