50.2. 寄语程序员
50.2.1. 机理
本节描述如何在属于 PostgreSQL 版本的程序或者库里面支持本地语言。 目前它只适用于 C 语言。
向程序中增加 NLS 支持
把下面的代码插入到程序的开头:
#ifdef ENABLE_NLS #include <locale.h> #endif ... #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain("_progname_", LOCALEDIR); textdomain("_progname_"); #endif
(这里的
_progname_
实际上可以自由选择。)如果发现一条需要翻译的消息,那么就需要插入一个对
gettext()
的调用。比如fprintf(stderr, "panic level %d\n", lvl);
会改成
fprintf(stderr, gettext("panic level %d\n"), lvl);
(如果没有配置 NLS ,那么
gettext
会定义成空操作。)这么干会出现一堆东西。一种常用的缩写是
#define _(x) gettext(x)
如果程序只通过一个或者少数几个函数做大部分的消息传递,比如后端里的
ereport()
, 那么也可以用另外一个方法。在这些函数的内部对所有输入字符串调用gettext
。在程序源代码所在的目录里加一个文件
nls.mk
。 这个文件将被当做 makefile 读取。在这里需要做下面一些变量的赋值:CATALOG_NAME
那些在
textdomain()
调用里提供的程序的名字。AVAIL_LANGUAGES
提供的翻译的语言列表,开始的时候是空的。
GETTEXT_FILES
一列包含可翻译字符串的文件,也就是那些用
gettext
或者其它相应手段标记了的文件。 最终,这里会包括几乎所有的程序源文件。如果列表太长,你可以把第一个"文件"写成一个+
和第二个词组成,第二个词是一个文件,在这个文件里每行包含一个文件名。GETTEXT_TRIGGERS
生成给翻译者使用的消息表的工具,以便知道哪些函数调用包含可翻译字符串。 缺省时只知道
gettext()
调用。 如果你使用了_
或其它标识符,那么你需要把它们列在这里。 如果可翻译字符串不是第一个参数,那么该项需要是这样的形式:func:2
(用于第二给参数)。 如果函数支持多个消息,那么该项看起来就像这样:func:1,2
(识别单个和多个消息参数)。
编译系统将自动制作和安装消息表。
50.2.2. 消息书写指导
这里是一些关于如何书写易于翻译的消息的指导:
不要在运行时构造语句,比如像
printf("Files were %s.\n", flag ? "copied" : "removed");
语句里这样的单词顺序会让其它语言很难翻译。 而且,即使你记得在每个片断上调用
gettext()
,这些片断也不一定能很好地独立翻译。 最好复制一些代码,好让每条消息可以当作有机的整体进行翻译。 只有数字,文件名和类似的运行时变量才应该在运行时插入消息文本。出于类似的原因,下面的东西不能用:
printf("copied %d file%s", n, n!=1 ? "s" : "");
因为它假设了如何形成复数形。如果你看到这样的东西,你可以用下面方法解决
if (n==1) printf("copied 1 file"); else printf("copied %d files", n):
不过还是有让人失望的时候,有些语言在某些特殊规则上有超过两种形式。 通常最好是通过消息的设计避免这些东西,比如你可以这样写:
printf("number of copied files: %d", n);
如果你真的要构造一条恰当的复数形消息,也是支持的,但形式上有一点笨拙。 比如当通过
ereport()
生成一条概要或者详细消息时,你可以这样写:errmsg_plural("copied %d file", "copied %d files", n, n)
第一个参数是对应英文单数形的格式字符串,第二个参数是对应英文复数形的格式字符串,第三个参数是决定是否是复数形的一个整数值。 随后的参数和通常一样对应与格式字符串中的参数值。(通常,控制复数形的值也是格式字符串中的其中一个参数。) 英语中只关心
_n_
是不是1,但其它语言中可能有多个复数形。 针对英文中作为一组的2个格式字符串,翻译者能够为根据_n_
的运行值选中的恰当的那个提供多个替代字符串。如果你需要一条不直接调用
errmsg
或errdetail
的复数形消息, 必须使用下层的ngettext
函数。具体参考gettext的文档。如果你想和翻译者进行交流,比如说一条消息是如何与其它输出对齐的,那么在该字符串出现之前,放上一条以
translator
开头的注释,比如/* translator: This message is not what it seems to be. */
这些注释都拷贝到消息表文件里,这样翻译者就可以看见它们了。