函数 bindtextdomain 的作用是通过给定的 domain 查找路径,可用信息的路径被指定为:
dirname/locale/category/domainname.mo
|
dirname 即为参数 dirname,若参数 dirname 为 NULL,则函数返回程序当前路径值 ( 默认是
/usr/share/locale),若 dirname 值为 "",则返回值为空。locale 即为 locale 名字,category 是
locale 的分类,如 LC_MESSAGES 等。domainname 即为参数 domainname。函数
bind_textdomain_codeset 的功能与 bindtextdomain 相近,因此 glibc 在实现时采用了一个内部函数
set_binding_values 并通过对该函数输入参数的控制分别实现以上 2 个函数。
/* intl/bindtextdom.c */ static void set_binding_values (domainname, dirnamep, codesetp) const char *domainname; const char **dirnamep; const char **codesetp; { ... }
/* 函数 bindtextdomain */ char * BINDTEXTDOMAIN (domainname, dirname) const char *domainname; const char *dirname; { set_binding_values (domainname, &dirname, NULL); return (char *) dirname; }
/* 函数 bind_textdomain_codeset */ char * BIND_TEXTDOMAIN_CODESET (domainname, codeset) const char *domainname; const char *codeset; { set_binding_values (domainname, NULL, &codeset); return (char *) codeset; }
|
另外,还有一个可访问 locale 相关信息的重要函数 nl_langinfo,该函数定义于头文件 langinfo.h
中,其作用是通过给定的 item 返回与 locale 相关的信息。
#include char *nl_langinfo(nl_item item);
/* locale/nl_langinfo.c */ char * nl_langinfo (item) nl_item item; { return __nl_langinfo_l (item, _NL_CURRENT_LOCALE); }
|
通过以上的描述我们大致了解了 Linux 对国际化和本地化的支持,下面我们编写一个获取当前系统时间的小程序来更好的理解函数
setlocale 对整个程序的影响 ( 见 清单 12)。
#include #include #include
#define SIZE 80
int main (int argc, char *argv[]) { time_t now; struct tm *timeinfo; struct lconv *lc; char buffer[SIZE];
setlocale (LC_ALL, ""); printf ("LC_TIME = %s\n", setlocale (LC_TIME, NULL)); printf ("LC_MONETARY = %s\n", setlocale (LC_MONETARY, NULL));
time (&now); timeinfo = localtime (&now);
strftime (buffer, SIZE, "%c", timeinfo); printf ("Date : %s\n", buffer);
lc = localeconv(); printf ("Currency symbol : %s\n", lc->currency_symbol);
return 0; }
$ gcc -Wall locale-time.c -o locale-time $ LC_ALL=zh_CN.UTF-8 ./locale-time LC_TIME = zh_CN.UTF-8 LC_MONETARY = zh_CN.UTF-8 Date : 2009 年 11 月 05 日 星期四 19 时 47 分 46 秒 Currency symbol : ¥
|
在 清单 12 中,我们不仅展示了 setlocale 函数的 "" 和 NULL
这两个特殊参数值的使用,还使用了 lconv 这个数据结构用于打印与区域相一致的货币符号 (lconv
这个有关数字和货币规则信息的结构体及相关函数 localeconv 被定义在头文件 locale.h 中,与 locale 中的
LC_NUMERIC 和 LC_MONETARY 相关 )。在程序执行时,我们动态的修改了 locale 环境变量的值以此来更好的观察
setlocale 函数对程序的影响 ( 如前述使用 date 命令时一样,见 清单 5)。在没有调用函数 setlocale 的程序中,程序将使用默认环境值 "C" 或
"POSIX"。由于我们在上面提过 glibc 提供了两组不同的接口实现程序的国际化和本地化,为了更好的理解这两种方式,我们在下面分别进行展示 (
见 清单 13 和 清单 15)。
#include #include #include
#define PACKAGE "gettext-hello" #define LOCALEDIR "po" #define N_(msgid) gettext(msgid)
int main (int argc, char *argv[]) { setlocale (LC_CTYPE, "zh_CN.UTF-8"); setlocale (LC_MESSAGES, "zh_CN.UTF-8");
bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE);
/* Translators: 这里仅是一个注释 */ printf (N_("Are you ok?\n"));
return 0; }
|
我们指定使用 locale 名字为 "zh_CN.UTF-8" 以此方便我们建立目录进行测试,但是更通常的做法是使用 ""
作为参数值使程序适应不同的语言 ( 区域 ) 环境。另外,我们需要为这个简单的程序做一个翻译,并生成一个可用的二进制翻译文件。我们使用由 GNU
gettext 提供的工具 xgettext 及 msgfmt 来完成翻译档的生成,但这仅是用于制作、维护 PO (Portable
Object) 和 MO (Machine Object) 文件的部分工具集 ( 见 清单 14)。
$ xgettext --add-comments --keyword=N_ gettext-hello.c -o \ > gettext-hello.pot --from-code=UTF-8 $ cp gettext-hello.pot gettext-hello.po $ cat locale-hello.po ... "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" ... #. Translators: 这里仅是一个注释 #: locale-hello.c:23 msgid "Are you ok?\n" msgstr "你还好吗? \n"
$ mkdir -p po/zh_CN.UTF-8/LC_MESSAGES/ $ msgfmt gettext-hello.po -o gettext-hello.mo $ mv gettext-hello.mo po/zh_CN.UTF-8/LC_MESSAGES/ $ gcc -Wall gettext-hello.c -o gettext-hello $ LC_ALL=zh_CN.UTF-8 ./ gettext-hello 你还好吗?
|
#include #include #include
#define CATALOG_NAME "catgets-hello.cat"
int main (int argc, char *argv[]) { nl_catd catd; setlocale (LC_ALL, ""); printf ("LC_MESSAGES = %s\n", setlocale (LC_MESSAGES, NULL));
catd = catopen (CATALOG_NAME, NL_CAT_LOCALE); if(catd == (nl_catd) -1) { perror("catopen"); return 1; }
int set_no=11; int msg_id=14; printf("%s\n", catgets (catd, set_no, msg_id, "Are you OK?"));
if(catclose(catd) < 0) { perror ("catclose"); return 1; }
return 0; }
|
我们在 catgets
示例代码中加上了错误处理,但这仅是为了更好的展示。通常情况下这是不需要的,因为我们应尽量使程序运行下去而不是中断。在编辑完程序所需的翻译档后,我
们执行这个简单的 catgets 示例 ( 见 清单 16)。
$ cat catgets-hello.msg
...
$set 11
14 你还好吗?
15 I am fine,thanks.
...
$ gencat catgets-hello.msg -o catgets-hello.cat
$ mv catgets-hello.cat po/zh_CN.UTF-8/LC_MESSAGES/
$ gcc -Wall catgets-hello.c -o catgets-hello
$ export NLSPATH=po/%L/LC_MESSAGES/%N
$ LC_ALL=zh_CN.UTF-8 ./ catgets-hello
LC_MESSAGES = zh_CN.UTF-8
你还好吗?
我们使用命令 gencat 生成程序所需的二进制翻译档并通过 NLSPATH
指定该文件存放的位置。若不指定该变量,默认的位置将会是如下所示。
[1] prefix/share/locale/%L/%N [2] prefix/share/locale/%L/LC_MESSAGES/%N
|
其中 %L 用于指定 locale 名字,%N 为文件名。对于上面几个示例及命令,更好的执行方式是通过 strace
等调试工具进行跟进,这不仅能熟悉相关函数的作用,并可更好的理解 Linux 国际化与本地化机制。
我们从国际化与本地化的概念深入到 Linux 实现国际化与本地化机制的 glibc 源码,并尝试从多个角度理解 Linux
上的国际化与本地化机制的运作,最后我们还编写了一些示例,但仍有一些内容被我们忽略,如多语言化、X Window
系统的国际化环境等。另外,一个值得关注的项目是 uClibc,它是一个为嵌入式 Linux 设计的 C 库,相比于 glibc
小巧许多但功能及实现上稍有差异。
阅读(2611) | 评论(0) | 转发(0) |