53.2. 给编程者
53.2.1. 技术
这一节描述如何在PostgreSQL发布中的一个程序或库中实现本地语言支持。当前,这些知识只适用于 C 程序。
为一个程序增加 NLS 支持
-
将这里的代码插入到该程序的启动序列中:
#ifdef ENABLE_NLS #include <locale.h> #endif ... #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextDOMain("progname", LOCALEDIR); textdomain("progname"); #endif
(程序名实际上可以自由选择)。
-
不管在哪里找到一个可被翻译的消息,需要插入一个
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
或另一种替代方案标记的文件。最终,这将包括该程序近乎所有的源文件。如果这个列表太长你可以让第一个"file"变成一个+并且第二个词变成一个包含那些文件名的文件,第二个词指向的文件中每个文件名一行。- GETTEXT_TRIGGERS
为翻译者产生在其上工作的消息目录的工具需要知道,哪些函数调用包含可翻译的字符串。默认情况下,只有
gettext()
调用是已知的。如果你使用_
或其他标识符,你需要在这里列出它们。如果可翻译的字符串不是第一个参数,该条目需要是形式func:2(用于第二个参数)。如果你有一个支持复数消息的函数,该条目应该看起来像func:1,2(标识单数和复数消息参数)。
构建系统将自动处理消息目录的编译和安装。
53.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是否唯一有关系,但是在其他语言中可以有多种不同的复数形式。翻译者将两种英语形式看成一组并且有机会提供多个替补字符串,基于n的运行时值会选择其中合适的那一个。
如果你需要复数化一个不直接进入到
errmsg
或errdetail
报告的消息,你必须使用底层函数ngettext
。见 gettext 文档。 -
如果你想要与翻译者沟通(例如关于一个消息如何在其他输出上对齐),在该字符串出现的地方之前放上一个以translator开始的注释,例如:
/* translator: This message is not what it seems to be. */
这些注释被复制到消息目录文件中,这样翻译者可以看到它们。