浅析 Linux 的国际化与本地化机制(2)

来源:developworks 作者:developworks
  


字符及字符集处理

字符以及字符集的处理,是 Linux 国际化与本地化的另一重要内容。在计算机的早期字符集中,每个字符仅使用 6 个、7 个或 8 个位 (bits),但这对于类似东方语系的来说是明显不够的,因此出现了双字节字符集甚至更多字节。在字符集编码上有着两个重要的概念,即内码与外码。内码是在计算机内存中使用的编码,而外码则是在计算机外部使用的编码,如存储和传输等。常用的宽字符集内码有 Unicode 和 ISO 10646 ( 也叫做 UCS,即 Universal Character Set)。Unicode 的最初设计是 16 位,而 ISO 10646 使用的是 31 位。经发展这两个标准现几乎没有差异,Unicode 标准对应于 ISO 10646 实现级别 3 ( 即支持所有的 UCS 组合字符 )。而我们常用的 UTF-8 即 UCS 变形格式 8,是一种兼容于 ASCII 编码和所有 POSIX 文件系统的可变长编码。

通常,程序要依靠一些分类函数来处理字母、数字及空白等字符,而这些函数是会受到当前 locale 中的 LC_CTYPE 值的影响的。在 ISO C 标准中描述了两种不同的字符处理方式,即是 char 类型和 wchar_t 的宽字符 ( 即 wc)。它们的分类函数分别被定义在头文件 ctype.h 和 wctype.h 中。对于多字节字符串 (mbs) 以及宽字符串 (wcs) 的处理函数则被定义在头文件 wchar.h 中。显然,宽字符分类函数更为通用,因为它允许字符集分类的扩展可超过它的可用值。而这有在 POSIX 中描述的字符集扩展已在 glibc 的程序 localedef 中实现。另外,glibc 考虑到 locale 对程序字符的影响,提供了独立于 locale 的更为通用的字符集处理函数及工具 iconv ( 见清单 7)。


清单 7. iconv 相关函数
				
 #include <iconv.h> 
 iconv_t iconv_open(const char *tocode, 
                   const char *fromcode); 
 int iconv_close(iconv_t cd); 
 size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, 
                   char **outbuf, size_t *outbytesleft); 

下面我们通过查看文件系统 /proc 中的内容,以此来观察 Linux 国际化与本地化机制在内存中的情况 ( 见 清单 8)。使用命令 cat 查看到的 /proc/self/maps 文件内容应与命令 locale 返回的系统当前区域环境值相一致 ( 即将 清单 8清单 1 相比较 )。


清单 8. 当前进程的内存映射及访问权限
				
 $ cat /proc/self/maps 
 ... 
 085a2000-085c3000 rw-p 085a2000 00:00 0          [heap] 
 b7a90000-b7acf000 r--p 00000000 08:08 740190     /usr/lib/locale/zh_CN.utf8/LC_CTYPE 
 b7acf000-b7ad0000 r--p 00000000 08:08 729171     /usr/lib/locale/en_US.utf8/LC_NUMERIC 
 b7ad0000-b7ad1000 r--p 00000000 08:08 781364     /usr/lib/locale/en_US.utf8/LC_TIME 
 b7bbd000-b7dbd000 r--p 00000000 08:08 704987     /usr/lib/locale/locale-archive 
 b7dbd000-b7dbe000 rw-p b7dbd000 00:00 0 
 b7dbe000-b7f1a000 r-xp 00000000 08:08 852866     /lib/tls/i686/cmov/libc-2.9.so 
 ... 
 b7f27000-b7f2e000 r--s 00000000 08:08 704989     /usr/lib/gconv/gconv-modules.cache 
 ... 
 b7f32000-b7f4e000 r-xp 00000000 08:08 827406     /lib/ld-2.9.so 
 ... 

清单 8 中我们看到了 locale-archive 文件,如上述这个文件等同于其他含 locale 相关数据的目录文件 ( 如 /usr/lib/locale/en_US.utf8/LC_TIME 等 )。文件 gconv-modules.cache 是由命令 iconvconfig 以文件 gconv-modules 生成的 iconv 配置缓存文件,但该文件并不影响 iconv 的使用。当 gconv-modules.cache 不存在时,iconv 将会尝试打开配置文件 gconv-modules。





简述 gettext

GNU gettext 是为程序实现国际化和本地化而设计的,它作为 GNU 实现软件翻译项目的一个重要部分,为程序开发者、翻译者甚至用户提供了一个可生成多语言信息的框架 ( 通过 gettext 提供的一些接口可使程序与系统的语言环境相一致 )。由此 gettext 可帮助我们快速的完成程序或软件的国际化与本地化,而 gettext 工具集则帮助我们更好的管理和维护翻译文档,其具体包含了:

  • 一组关于如何编写程序来支持信息分类的约定;
  • 一个支持信息分类的目录以及文件的命名管理;
  • 一个支持获取翻译信息的动态库文件;
  • 一个支持信息分类的目录以及文件的命名管理;
  • 一些用于管理翻译信息 ( 或已翻译文件 ) 的独立程序;
  • 一个支持解析与创建翻译信息的库文件;
  • 一个专为 Emacs 提供的模块,方便设置和获取时间戳。

图 2. 使用 gettext 工具集处理程序国际化与本地化
图 2. 使用 gettext 工具集处理程序国际化与本地化

图 2所示为使用 gettext 工具集处理程序的国际化和本地化的流程。事实上,在 glibc 中提供了两组不同的接口来完成信息的翻译,但是它们都没被 POSIX 标准所接受。其中一组接口就是 gettext,而另一组接口则是 catgets。虽然 catgets 被定义进了 X/Open 标准中,但这是来着产业的决定,因此它可能并不合理。相比于 gettext 方案,catgets 的不足之处是函数 catgets 的第三个参数 ( 即消息 ID) 是唯一的,这将导致程序编写者及翻译者在字符信息管理和维护上的困难,因此建议使用 gettext。清单 9 所示的是一些与 catgets 相关的函数。


清单 9. 与 catgets 相关的一些函数
				
 #include <nl_types.h> 
 nl_catd catopen(const char *name, int flag); 
 int catclose(nl_catd catalog); 
 char *catgets(nl_catd catalog, int set_number, 
                      int message_number, const char *message); 





一些相关的函数

在 Linux 实现国际化和本地化时的一个重要函数就是 setlocale(),我们可以在系统工具集 ( 如 date 命令,相关源码片段已在 清单 6 中展示。命令 locale 及 localedef 均有使用该函数。) 或是一些软件 ( 如文本编辑软件 gedit ) 的源码中发现其存在的痕迹。

 #include <locale.h> 
 char *setlocale(int CATEGORY, const char *LOCALE); 

函数 setlocale 被用于设置或查询程序当前的 locale 值。参数 CATEGORY 一共有 13 个可用值 ( 如 LC_COLLATE 等 ),它们被定义在头文件 locale.h 中,分别表示从 0 至 12 的整数值。第二个参数 LOCALE 应为 locale 名称 ( 如 zh_CN.UTF-8 等 ),但是仍有两个特殊的值可使用 "" 和 NULL。如果参数 LOCALE 的值是 "",则函数返回系统当前的环境值 ( 即置程序的环境值与系统相一致 )。若 LOCALE 的值为 NULL,则仅返回程序当前 locale 的设置。程序在设置 locale 值之前,其环境值默认是 "C" 或 "POSIX"。对于国际化的程序,函数 setlocale 总被调用,同时为了保持程序的通用性,我们一般把 LOCALE 置为 ""。另外,在 清单 6 中的另两个函数均被定义在头文件 libintl.h 中。

 #include <libintl.h> 
 char * textdomain (const char * domainname); 

函数 textdomain 的作用是重设当前的 domain 值以供 gettext() 等函数使用,其参数 domainname 为期望设置的新的 domain 值。如果参数 domainname 为 "",那么函数将返回默认值 "messages",但是似乎没人愿意使用该值,因为那样会使程序间出现冲突以至混乱。若 domainname 值为 NULL,则返回程序当前的 domain 值 ( 在先前没有设置的情况下,仍会返回预定值 "messages")。另外需注意的是,dcgettext() 等自带有参数 domainname 的函数,将不受函数 textdomain 的影响 ( 除非有人把它们的参数 domainname 值设为 NULL)。

 #include <libintl.h> 
 char * bindtextdomain (const char * domainname, const char * dirname); 
 char * bind_textdomain_codeset (const char * domainname, 
                                                 const char * codeset); 

时间:2009-12-22 08:44 来源:developworks 作者:developworks 原文链接

好文,顶一下
(2)
50%
文章真差,踩一下
(2)
50%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量