52.2. 给程序员 #
52.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这样(分别标识单数和复数消息参数)。
构建系统会自动处理消息目录的构建和安装。
52.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;而在其他语言中,可能存在许多不同的复数形式。翻译者会把这两个英语形式视为一组,并有机会提供多个替代字符串,再根据运行时的n值选择合适的那个。如果你需要对一条不是直接用于
errmsg或errdetail报告的消息做复数处理,就必须使用底层函数ngettext。参见 gettext 文档。如果你想向翻译者传达某些信息,例如一条消息应如何与其他输出对齐,就在该字符串出现之前放置一个以
translator开头的注释,例如:/* translator: This message is not what it seems to be. */
这些注释会被复制到消息目录文件中,这样翻译者就能看到它们。