PostgreSQL数据库之国际化语言支持学习总结

学习参考书籍、网站或博文:

  1. PostgreSQL本国语言支持 官方文档,点击前往
  2. GNU gettext官方文档
  3. GNU gettext简介


GNU概述 gettext

通常在编写程序时,源码中首选使用英文进行编写,为了实现程序的国际化,使用gettext为交互语句添加多语言支持机制。使程序在运行过程时,以不同的语言呈现给用户
GNU gettext是GNU Translation Project的重要步骤,它是我们可以构建许多其他步骤的资产。该软件包为程序员,翻译人员甚至用户提供了一套完善的工具和文档集。具体来说,GNU gettext实用程序是一组工具,提供了一个框架来帮助其他GNU软件包生成多语言消息。这些工具包括一组关于如何编写程序以支持消息目录的约定、消息目录本身的目录和文件命名组织、支持检索已翻译消息的运行时库,以及一些以各种方式处理可翻译字符串集的独立程序,或者已经翻译的字符串

Po文件简介

PO是Portable Object(可移植对象)的缩写形式,它是面向翻译人员的、提取于源代码的一种资源文件。它将给定包的每个原始可翻译字符串与特定目标语言的译文相关联。单个PO文件专用于一种目标语言。如果一个软件包支持多种语言,则每种语言都支持一个这样的PO文件,并且每个软件包都有自己的一组PO文件。这些PO文件最好由xgettext程序创建,然后通过msgmerge程序进行更新或刷新。程序xgettext从一组C文件中提取所有标记的消息,并使用空翻译初始化PO文件。程序msgmerge负责在相应源的发行版之间调整PO文件,注释过时的条目,初始化新条目以及更新所有源代码行引用。
MO是Machine Object(机器对象)的缩写形式,它是面向计算机的、由.po文件通过GNU gettext工具包编译而成的二进制文件,应用程序通过读取.mo文件使自身的界面转换成用户使用的语言。

  1. po文件的命名
    Postgresql源码的src/backend/po目录下是目前支持11中语言
[postgres@local99:~/src/postgresql-13.0/src/backend/po]$ ls
de.po  fr.po  ja.po  ru.po  tr.po  zh_CN.po
es.po  it.po  ko.po  sv.po  uk.po
[postgres@local99:~/src/postgresql-13.0/src/backend/po]$

po文件命名为语言.po,其中语言ISO 639-1 双字符语言代码(小写形式),例如法语是fr.po。如果对于一种语言有需要多个翻译任务那么这些文件也可以被命名为语言_区域.po,其中region是 ISO 3166-1 双字符国家代码(大写形式)例如巴西的葡萄牙语是pt_BR.po

  1. po文件的格式
    PO文件由许多条目组成,每个条目都包含原始未翻译的字符串与其对应的翻译之间的关系。给定PO文件中的所有条目通常都属于一个项目,并且所有翻译都以一种目标语言表示
    PO 文件的文件格式示例如下:
white-space
#  translator-comments
#. extracted-comments
#: reference…
#, flag…
#| msgid previous-untranslated-string
msgid “translated-string”
msgstr “anslated-string”
...
  • GNU gettext工具生成时,每个条目之间由空白行分割
  • # 字符引入一个注释。空白紧跟着 # 字符,则是一个由翻译者维护的注释。也可能有自动注释
  • #. 注释是xgettext从源代码中提取出的注释内容
  • #: 注释指示该消息在源代码中被使用的确切位置
  • #, 注释包含以某种方法描述该消息的标志。当前有两种标志:如果该消息可能由于程序源码中的改变而过时,则fuzzy被设置;
    另一种标志是c-格式,它指示该消息是一个printf-风格的格式模板。这意味着翻译也应当是一个格式字符串,其中有相同个数和相同类型的占位符
  • #|翻译器为之前待翻译的字符串提供的翻译
    注释之后显示两个字符串
    msgid “程序源码中抽取的字符串”
    msgstr “翻译之后的字符串”

简单翻译单元实例

#: ../common/config_info.c:130 ../common/config_info.c:138
#: ../common/config_info.c:146 ../common/config_info.c:154
#: ../common/config_info.c:162 ../common/config_info.c:170
#: ../common/config_info.c:178 ../common/config_info.c:186
#: ../common/config_info.c:194
msgid "not recorded"
msgstr "没有被记录"

gettext 实现国际化语言支持的步骤

本节主要介绍使用gettext工具为程序或库实现本地化语言支持的主要过程。首先来介绍几个重要的函数,在为程序实现本地化语言支持的第一步需要在源程序中导入gettext声明以及增加触发gettext操作

#include <libintl.h>
int main(int argc,char * argv []{ 
  … 
  setlocale(LC_ALL,“”); 
  bindtextdomain(PACKAGE,LOCALEDIR); 
  textdomain(PACKAGE);}

函数原型:char* setlocale( int category, const char* locale);

setlocale 函数将指定的系统语言环境或其部分安装为新的C语言环境。修改将一直有效,并影响所有对语言环境敏感的C库函数的执行,直到下一次调用为止。如果locale为null指针,表示使用环境变量中定义的LC_ALL的值

语言环境类别标识符说明
LC_ALL选择整个C语言环境
LC_COLLATE选择C语言环境的排序规则类别
LC_CTYPE选择C语言环境的字符分类类别
LC_MESSAGES消息语言类别
LC_MONETARY选择C语言环境的货币格式类别
LC_NUMERIC选择C语言环境的数字格式类别
LC_TIME选择C语言环境的时间格式类别

bindtextdomain()textdomain() 函数声明如下:

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

bindtextdomain用来设置文本域目录。所谓的文本域文件就是 mo 文件,domainname指生成的mo文件的名字,在下面的案例中,生成的MO文件命名为zh_CN.mo,则在对应的源程序中domainname参数的值为zh_CNdirname指MO文件存放的路径,linux中一般会将该文件放在 /usr/share/locale/zh_CN/LC_MESSAGES 中,下面的案例中将该路径设置为/home/postgres/study/locale
textdomain() 函数设置需要使用的文本域 。这些文本域之前都是经过 bindtextdomain() 指定的,再经过 textdomain() 函数设置后,那么此后 gettext 库(及其中的相关函数)便能找到相应的 mo 文件并操作它们。

简单案例

#include <libintl.h>
#include <locale.h>
#include <stdio.h>

#define PACKAGE "zh_CN"
#define LOCALEDIR "/home/postgres/study/locale"
#define _(x) gettext(x)
int main( void )
{
    /* triggering gettext declaration */
	setlocale (LC_ALL, "zh_CN.UTF-8");
    bindtextdomain( PACKAGE, LOCALEDIR );
    textdomain( PACKAGE );
    
    printf( _( "Hello world\n" ) );
    printf( _( "Where there is a will, there is a way!\n" ) );
    return 0;
}

下面以该案例来介绍实现本地化支持的步骤
1、提取可翻译的字符串

xgettext --add-comments --keyword=_ hello.c -o zh_CN.pot --from-code=UTF-8

  • --add-comments 在输出文件中将所有注释块置于关键字行之前
  • --keyword 不要使用默认关键字_
  • -o 将输出写入指定文件
  • --from-code 输入文件的编码

2、创建po文件

msginit --input=zh_CN.pot -o zh_CN.po -l zh_CN.UTF-8

  • --input 输入的.pot文件
  • -o 将输出写入指定文件
  • --local 设置目标区域设置

3、我们指定使用locale名称为zh_CN.UTF-8,创建mo文件存放的路径

[postgres@local99:~/study]$ mkdir -p locale/zh_CN.UTF-8/LC_MESSAGES

4、修改生成的zh_CN.po文件,增加翻译

mkdir -p locale/zh_CN.UTF-8/LC_MESSAGES

5、修改生成的zh_CN.po文件,增加翻译

# Chinese translations for PACKAGE package
# PACKAGE 软件包的简体中文翻译.
# Copyright (C) 2021 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#  <hello@163.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-15 18:47+0800\n"
"PO-Revision-Date: 2021-01-16 19:07+0800\n"
"Last-Translator:  <hello@163.com>\n"
"Language-Team: Chinese (simplified)\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#. say hello here
#: hello.c:16
#, c-format
msgid "Hello world\n"
msgstr "你好 世界\n"

#. say hello again
#: hello.c:19
#, c-format
msgid "Where there is a will, there is a way!\n"
msgstr "有志者事竟成\n"

6、生成zh_CN.mo二进制文本文件

msgfmt zh_CN.po -o zh_CN.mo

7、将生成的zh_CN.mo文件放到locale/zh_CN.UTF-8/LC_MESSAGES目录下

[postgres@local99:~/study]$ mv zh_CN.mo locale/zh_CN.UTF-8/LC_MESSAGES/

现在看一下汉化之后源程序执行的结果

[postgres@local99:~/study]$ gcc hello.c -o hello
[postgres@local99:~/study]$ ./hello 
你好 世界
有志者事竟成

PostgreSQL生成国际化文件的过程

在Postgres源码中的c.h中有如下定义,以支持gettext

/*
 * gettext support
 */

#ifndef ENABLE_NLS
/* stuff we'd otherwise get from <libintl.h> */
#define gettext(x) (x)
#define dgettext(d,x) (x)
#define ngettext(s,p,n) ((n) == 1 ? (s) : (p))
#define dngettext(d,s,p,n) ((n) == 1 ? (s) : (p))
#endif

#define _(x) gettext(x)

源码src目录下的nls-global.mk文件定义了消息目录用的Makefile文件的规则,下面仅列出一些主要的编译规则

...
FRONTEND_COMMON_GETTEXT_FILES = $(top_srcdir)/src/common/logging.c

FRONTEND_COMMON_GETTEXT_TRIGGERS = \
    pg_log_fatal pg_log_error pg_log_warning pg_log_info pg_log_generic:2 pg_log_generic_v:2

FRONTEND_COMMON_GETTEXT_FLAGS = 
    pg_log_fatal:1:c-format pg_log_error:1:c-format pg_log_warning:1:c-format pg_log_info:1:c-format pg_log_generic:2:c-pg_log_generic_v:2:c-


all-po: $(MO_FILES)

%.mo: %.po
    $(MSGFMT) $(MSGFMT_FLAGS) -o $@ $<
 ...
init-po: po/$(CATALOG_NAME).pot   

# For performance reasons, only calculate these when the user actually
# requested update-po or a specific file
ifneq (,$(filter update-po %.po.new,$(MAKECMDGOALS)))
ALL_LANGUAGES := $(shell find $(top_srcdir) -name '*.po' -print | sed 's,^.*/\([^/]*\).po$$,\1,' | LC_ALL=C sort -u)
all_compendia := $(shell find $(top_srcdir) -name '*.po' -print | LC_ALL=C sort)
else
ALL_LANGUAGES = $(AVAIL_LANGUAGES)
all_compendia = FORCE
FORCE:
endif

ifdef WANTED_LANGUAGES
ALL_LANGUAGES := $(filter $(WANTED_LANGUAGES), $(ALL_LANGUAGES))
endif

update-po: $(ALL_LANGUAGES:%=po/%.po.new)

$(AVAIL_LANGUAGES:%=po/%.po.new): po/%.po.new: po/%.po po/$(CATALOG_NAME).pot $(all_compendia)
    $(MSGMERGE) --lang=$* $(word 1, $^) $(word 2,$^) -o $@ $(addprefix --compendium=,$(filter %/$*.po,$(wordlist 3,$(words $^),$^)))

首先,必须在编译前指定 --enable-nls="语言名",否则不会产生多语言支持,比如 --enable-nls="zh_CN"

[postgres@local99:~/postgresql-13.0]$ ./configure  --exec-prefix=/home/postgres/pg13 --enable-nls="zh_CN"

启用之后我们在 Makefile.global 中会看到:

enable_nls  = yes
...

##########################################################################
#
# Native language support

ifeq ($(enable_nls), yes)
ifneq (,$(wildcard $(srcdir)/nls.mk))
include $(top_srcdir)/src/nls-global.mk

endif # nls.mk
endif # enable_nls

可以看出,只有目录下有nls.mk这个文件才会有nls动作

新建消息目录

Postgres源码bin目录下的pgbench工具未提供国际化语言支持,接下来我们为该目录生成国际化文件zh_CN.mo

  1. 创建nls.mk文件
# src/bin/ux_diagnose/nls.mk
CATALOG_NAME     = pgbench
AVAIL_LANGUAGES  = zh_CN
GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) \
                  pgbench.c exprparse.c exprscan.c
GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS)
GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS)
  • CATALOG_NAME 程序名,如textdomain()调用中所提供的
  • AVAIL_LANGUAGES 提供/支持的语言列表
  • GETTEXT_FILES 包含可翻译字符串的文件列表,即那些被用gettext或另一种替代方案标记的文件
  • GETTEXT_TRIGGERS 为翻译者产生在其上工作的消息目录的工具需要知道,哪些函数调用包含可翻译的字符串。默认情况下,只有gettext()调用是已知的。如果你使用_或其他标识符,你需要在这里列出它们。
  • GETTEXT_FLAGS 要标记的GETTEXT–flag参数的列表,包含C格式字符串的函数参数
  1. 创建消息目录
[postgres@local99:~/postgresql-13.0/src/bin/pgbench]$ make init-po
/bin/xgettext -ctranslator --copyright-holder='PostgreSQL Global Development Group' --msgid-bugs-address=pgsql-bugs@lists.postgresql.org --no-wrap --sort-by-file --package-name='pgbench (PostgreSQL)' --package-version='13' -D . -D . -n -kpg_log_fatal -kpg_log_error -kpg_log_warning -kpg_log_info -kpg_log_generic:2 -kpg_log_generic_v:2 -k_ --flag=pg_log_fatal:1:c-format --flag=pg_log_error:1:c-format --flag=pg_log_warning:1:c-format --flag=pg_log_info:1:c-format --flag=pg_log_generic:2:c-format --flag=pg_log_generic_v:2:c-format --flag=_:1:pass-c-format ../../../src/common/logging.c pgbench.c exprparse.c exprscan.c
sed -e '1,18 { s/SOME DESCRIPTIVE TITLE./LANGUAGE message translation file for pgbench/;s/PACKAGE/PostgreSQL/g;s/VERSION/13/g;s/YEAR/'`date +%Y`'/g; }' messages.po >po/pgbench.pot
rm messages.po

执行完成之后可以看到pgbench目录下会自动创建一个po目录,该目录下包含生成的pgbench.pot文件

[postgres@local99:~/postgresql-13.0/src/bin/pgbench/po]$ ls
pgbench.pot
  1. pgbench.pot重命名为zh_CN.po,并为之添加翻译
[postgres@local99:~/postgresql-13.0/src/bin/pgbench/po]$ mv pgbench.pot zh_CN.po

在这里插入图片描述
4. 生成对应的mo文件
执行makemake all-po
执行之后可以看到po目录下会生成对应的zh_CN.mo文件

[postgres@local99:~/postgresql-13.0/src/bin/pgbench/po]$ ls
zh_CN.mo  zh_CN.po

至此为一个新工具目录增加国际化支持的步骤就结束了

更新消息目录

如果一个目录已经提供了国际化语言支持,假设我们修改了 initdb 的输出信息,如何更新国际化文件呢?

  1. make update-po
[postgres@local99:~/postgresql-13.0/src/bin/pgbench]$ cd ../initdb/
[postgres@local99:~/postgresql-13.0/src/bin/initdb]$ ls
findtimezone.c  findtimezone.o  initdb  initdb.c  initdb.o  localtime.c  localtime.o  Makefile  nls.mk  po  t
[postgres@local99:~/postgresql-13.0/src/bin/initdb]$ make update-po
/bin/msgmerge --no-wrap --previous --sort-by-file --lang=zh_CN po/zh_CN.po po/initdb.pot -o po/zh_CN.po.new --compendium=../../../src/backend/po/zh_CN.po --compendium=../../../src/bin/initdb/po/zh_CN.po --compendium=../../../src/bin/pg_archivecleanup/po/zh_CN.po --compendium=../../../src/bin/pg_basebackup/po/zh_CN.po --compendium=../../../src/bin/pg_checksums/po/zh_CN.po --compendium=../../../src/bin/pg_config/po/zh_CN.po --compendium=../../../src/bin/pg_controldata/po/zh_CN.po --compendium=../../../src/bin/pg_ctl/po/zh_CN.po --compendium=../../../src/bin/pg_dump/po/zh_CN.po --compendium=../../../src/bin/pg_resetwal/po/zh_CN.po --compendium=../../../src/bin/pg_rewind/po/zh_CN.po --compendium=../../../src/bin/pg_test_fsync/po/zh_CN.po --compendium=../../../src/bin/pg_test_timing/po/zh_CN.po --compendium=../../../src/bin/pg_upgrade/po/zh_CN.po --compendium=../../../src/bin/pg_verifybackup/po/zh_CN.po --compendium=../../../src/bin/pg_waldump/po/zh_CN.po --compendium=../../../src/bin/psql/po/zh_CN.po --compendium=../../../src/bin/scripts/po/zh_CN.po --compendium=../../../src/interfaces/ecpg/ecpglib/po/zh_CN.po --compendium=../../../src/interfaces/ecpg/preproc/po/zh_CN.po --compendium=../../../src/interfaces/libpq/po/zh_CN.po --compendium=../../../src/pl/plperl/po/zh_CN.po --compendium=../../../src/pl/plpgsql/src/po/zh_CN.po --compendium=../../../src/pl/plpython/po/zh_CN.po --compendium=../../../src/pl/tcl/po/zh_CN.po
............................. done.
[postgres@local99:~/postgresql-13.0/src/bin/initdb]$

执行make update-po之后从输出信息可以看到,initdb.pot(翻译模板)重新生成,并且只有我们需要的 zh_CN 被 merge,生成一个新文件 zh_CN.po.new
在这里插入图片描述
2. 编辑zh_CN.po.new,添加对应的翻译,并将其重命名为zh_CN.po
3. 重新生成.mo文件
make all-po
至此,新的 .mo 文件已经重新生成

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值