使用GNU gettext实现本地化语言支持

PS:本地化过程中使用的工具会在另一篇博客中介绍,相关的gettext工具:xgettext、msginit、msgfmt、msgmerge等


为了便于操作,不必花费太多时间去阅读本文,我在最后简单地列出了步骤,如果不想看文章细节,就跳到最后,照着做就行。

这篇文章是我翻译的,原作者和题目如下:

A tutorial on Native Language Support using GNU gettext

G. Mohanty

Revision 0.3: 24 July 2004

参考网址如下:

http://oriya.sarovar.org/docs/gettext/memo.html


使用GNUgettext实现本地化语言支持


摘要

       这里介绍如何利用GNU的gettext工具实现本地语言的支持。 虽然,这篇文章讲解将要支持的语言是Oriya,但是文章中介绍的方法是通用的。同样,虽然这里使用的平台是Linux,但是其他任何系统如果也使用GNU gettext 应该是同样的工作原理。

       我们一步一步地来讲解怎样使一个程序打印在屏幕上的消息成为Oriya的而不是English的;从编程序的角度开始,然后以用户使用的角度结束。有些讲解也涉及到了如何进行翻译。



简介

       当前,无论是商业还是免费软件都是英文的,并用英文做为文档。直到现在,为使其它非英语语言用户也能够进行交互所做的工作仍然不足,所以这对非英语语言的国家很不利。然而,随着GNU gettext工具的发行,这种状况大大改善了,现在几乎所有的GNU程序是在一个特定框架下面编写的,在这个框架下,可以很容易把英文的程序消息翻译成其它语言。如果能够翻译,那么用户可以在程序运行的时候设置语言选项和程序进行交互。gettext用一种非常优秀的方法解决了这个看起来非常困难的问题,这个方法简化了程序员和翻译者的工作,并且更重要的是它允许程序员和翻译者独立的工作。

       这篇文章描述了如何在系统下利用GNUgettext工具来支持本地化语言。本文例子中实际使用的是0.12.1版本,同时,它也适用于其它版本的gettext。另一套系统,在X/Open Portability Guide中描述的叫做called catgets,也正在被使用,但是我们在这里不对其进行讨论。




一个简单的例子

       我们的第一个使用gettext的例子是传统的helloworld程序,它只包含了一个函数用来打印“Hello World”信息到终端上。这个程序的国际化版本保存为hello.c如下:


01    #include

02    #include

03    #include

04    #include

05    int main(void)

06    {

07      setlocale( LC_ALL, "" );

08      bindtextdomain( "hello", "/usr/share/locale" );

09      textdomain( "hello" );

10      printf( gettext( "Hello, world!\n" ) );

11      exit(0);

12    }


       在实际中,我们应该检查函数的返回值以捕获各种错误,但为清晰起见这里忽略了这些处理。编译的时候使用我们常用的命令:gcc -o hello hello.c。这个程序本来是应该链接到GNU libintl库的,但是做为GNU C库的一个组成部分,如果你是在Linux或者其它使用glibc的系统中,这一步会被自动进行。


程序员的角度

       当hello可执行文件在默认的情况下运行的时候,(通常是C 情况?什么意思?),它在终端打印出"Hello,world!"字样。另外,它还会做一些初始的设置工作。程序员唯一需要做的工作就是把所有将要打印的字符替换为gettext(字符串),也就是说,把字符做为一个参数传递给gettext函数。有些时候为了简便,我们可以把函数定义为一个cpp宏,例如,可以在源代码的开头加上一句:

       #define _(STRING) gettext(STRING)

       然后,就可以使用_(字符串)来替代gettext(字符串)了。

       下面,我们来一行一行地对这个程序进行分析。

       locale.h文件定义了用来保存多语言化信息的c数据结构,并且setlocale函数也需要这个文件。

       libintl.h文件声明了GNU text 工具函数,这里的bindtextdomain,gettext,和textdomain函数就需要用到这个文件。

       第7行的setlocale()调用,把LC_ALL做为它的第一个参数,把空字符串做为它的第二个参数,这样把程序当前的所有多语言 相关设置为用户定义的每一个环境变量。也就是说,这个程序的多语言相关设置被初始化为和用户相匹配的环境。关于更多的细节信息,我们可以参见"man setlocale"手册说明。

       第8行的函数bindtextdomain设置了?为给定的消息域的消息类别?的基本目录。消息域是一个翻译的消息的集合,每一个软件包一般都有它自己的域。在这里,我们使用"hello"做为我们程序的消息域。这里的第2个参数(/usr/share/locale)是消息类别的默认系统路径,我们将要把消息类别放在默认的系统路径中。因此我们可以在这里不用调用bindtextdomain函数?,这个函数仅在消息类别没有被安装在标准路径的时候才会有用,例如一个打包的软件发行可能会把类别放在它主目录下面的po/目录中,我们可以参见"man bindtextdomain"来了解更为详细的信息。

       第9行的textdomain函数调用把当前程序的消息域设置为"hello",也就是我们这个程序使用的名字。我们可以参见"man textdomain"来了解更为详细的信息。

最后,我们把第10行的:

       printf( "Hello, world!\n" );

       替换成了:

       printf( gettext( "Hello, world!\n" ) );

       (如果你不熟悉C语言,字符串末尾的\n会在输出的末尾打印一个新行)对于所有可以翻译的字符串,这个简单的修改实现了翻译者和程序员之间的工作相互独立。如果一个包第一次使用GNUgettext或者升级到新版本的gettext,gettextize减轻了减轻了程序员的工作。 


提取可以翻译的字符串

       现在应该从程序的源代码中提取将要翻译的字符串了。这个工作是利用xgettext完成的,如下:

       xgettext -d hello -s -o hello.pot hello.c

       这里,会处理hello.c中的源代码,并且把输出保存到一个hello.pot文件中(这个在选项-o已经指明了的)。-s选项告诉xgettext产生排好序的输出。程序中的消息域使用-d选项的参数来指定,这个消息域应该和textdomain调用中指定的消息域相匹配。(在源代码中的第9行)。其它关于如何使用gettext的细节问题可以参见“man gettext”。

       .pot文件(portable object template)是把程序中相关信息翻译成其它语言得基础。开始翻译之前,可以把hello.pot拷贝一份副本oriya.po(这样保留原来的模板文件便于今后翻译成其它的语言);然而更好的方法是使用msginit程序,这个程序还可以正确地设置一些默认的值。

       msginit -l or_IN -o oriya.po -i hello.pot

       这里,-l 选项定义了语言(这里需要在你的系统中安装Oriya语言),-i 选项和-o选项分别定义了输入和输出文件。这里,oriya.po文件是由msginit来生成的,它的内容如下:

  # Oriya translations for PACKAGE package.

  # Copyright (C) 2004 THE PACKAGE'S COPYRIGHT HOLDER

  # This file is distributed under the same license as the PACKAGE package.

  # Gora Mohanty , 2004.

  #

  msgid ""

  msgstr ""

  "Project-Id-Version: PACKAGE VERSION\n"

  "Report-Msgid-Bugs-To: \n"

  "POT-Creation-Date: 2004-06-22 02:22+0530\n"

  "PO-Revision-Date: 2004-06-22 02:38+0530\n"

  "Last-Translator: Gora Mohanty \n"

  "Language-Team: Oriya\n"

  "MIME-Version: 1.0\n"

  "Content-Type: text/plain; charset=UTF-8\n"

  "Content-Transfer-Encoding: 8bit\n"

 

  #: hello.c:10

  msgid "Hello, world!\n"

  msgstr ""

       msginit从password文件中获取了我们的email地址,名字等。它也会自动填充一些数据,例如版本日期,语言,字符集,这些是从or_IN中获取的。

       一定要遵守.op(portable object)文件中实体的格式。每个实体具有如下的结构:

  WHITE-SPACE

  #  TRANSLATOR-COMMENTS

  #. AUTOMATIC-COMMENTS

  #: REFERENCE...

  #, FLAG...

  msgid UNTRANSLATED-STRING

  msgstr TRANSLATED-STRING

       这里,初始的空白(空格,制表,新行,等)以及所有的注释也有可能不会出现在特定的实体中。注释行以'#'做为开始,分为两种类型:(i)手动添加的翻译注释,经常在'#'的会面紧跟着一个空格。(ii)自动添加的注释,一般通过gettext 工具来进行维护,这样的注释在'#'的后面没有空格。这里的msgid行包含没有翻译的(英文)字符串,如果具有一个实体在po文件中,那么msgstr行就是翻译字符串所在的地方。(更多关于po文件的格式信息参见info里面的gettext::Basics::::PO Files::。)


进行翻译

       编辑oriya.po文件,添加翻译好的Oriya字符串。如果遵照PO文件格式,那么任何编辑器都可以进行这个工作。当然也有一些其他的方便的编辑器,例如Emacs,kbabel,gtranslator,poedit等等。在附录B中都将它们列出来了。

       首先需要做的就是,在开头将注释以及一些头实体的信息填充好,这些信息已经有一部分被msginit给填充好了。头实体中的内容很容易看懂,具体信息可以查看info文档中的gettext::Creating::HeaderEntry::info节点。然后,剩下的工作主要是敲入Oriya文本串作为翻译好的英语字符串。给剩下的每一个实体的msgstr行添加翻译好的Oriya文本串(在一个双引号中),翻译和实体中msgid串中的英语串相对应。例如对于oriya.po中的"Hello world!\n",我们可以填入``ନମସ୍କାର \n''(oriya文本串)。最好的oriya.po的内容类似如下:


  # Oriya translations for hello example package.

  # Copyright (C) 2004 Gora Mohanty

  # This file is distributed under the same license as the hello example package.

  # Gora Mohanty , 2004.

  #

  msgid ""

  msgstr ""

  "Project-Id-Version: oriya\n"

  "Report-Msgid-Bugs-To: \n"

  "POT-Creation-Date: 2004-06-22 02:22+0530\n"

  "PO-Revision-Date: 2004-06-22 10:54+0530\n"

  "Last-Translator: Gora Mohanty \n"

  "Language-Team: Oriya\n"

  "MIME-Version: 1.0\n"

  "Content-Type: text/plain; charset=UTF-8\n"

  "Content-Transfer-Encoding: 8bit\n"

  "X-Generator: KBabel 1.3\n"


  #: hello.c:10

  msgid "Hello, world!\n"

  msgstr "ନମସ୍କାର\n"


       (...然后简单叙述了关于各种编辑器对编写PO文件的易用性,省略...)


消息类别

       翻译完了oriya.po文件中的内容之后,它必须被编译成gettext工具能够迅速加载的二进制格式。如下:

       msgfmt -c -v -o hello.mo oriya.po

       -c选项进行具体PO文件格式得检查,-v给出一些程序得更多信息,输出文件通过-o选项指出。需要注意得是,文件得基本部分需要和消息域相匹配,消息域通过第2节中例子程序第8,9行中的bindtextdomain和textdomain的第一个参数给出。.mo(machine object)文件需要存放在一个特定的位置,这个位置的基础目录通过bindtextdomain的第2个参数给出。文件的最终位置在基础目录的子目录LL/        LC_MESSAGES 或LL_CC/LC_MESSAGES中。这里,LL代表一种语言,CC代表一个国家。例如,像我们选择了标准的路径,/usr/share/locale作为我们的基础目录,对于我们来说,语言和国家的字符串分别是"or"和"IN",我们将要把hello.mo放在/usr/share/locale/or IN。需要注意的是,你需要成为超级用户,这样才能把hello.mo拷贝到这个系统目录中去。如下:

 mkdir -p /usr/share/locale/or_IN/LC_MESSAGES

 cp hello.mo /usr/share/locale/or_IN/LC_MESSAGES


从使用者的角度

       一旦消息的类别被正确地安装了之后,如果Oriya语言是可用的话,任何系统中的用户都能够使用HelloWorld程序的Oriya版本了。首先,改变你的语言,如下:

       echo $LANG

       export LANG=or_IN

       第一个命令显示了你当前的语言设置(这个一般都是en_US,并且你在最后需要它重置为默认的语言)。第二个命令将当前的语言设置改变成了Oriya语言。

       需要一个支持Unicode的模拟终端来直接显示Oriya的输出。新版本的gnome-terminal和konsole(KDE的模拟终端)都是可以识别Unicode的。这里主要考虑gnome-terminal,似乎这个终端对国际化的支持也更好一些。gnome-terminal需要知道到达的字节是UTF-8编码的。这个可以通过:(a)选择Terminal->Character Coding->Unicode(UTF-8)或者(b)键入"/bin/echo -n -e '\033%\@'"或者(c)运行/bin/unicode_stop.现在,可以运行编译好(如第2节说得那样编译)的程序了:

       ./hello

       这样你会得到Oriya的输出。注意有可能终端支持的问题导致字体显示有问题。也有可能所有的终端假设字体的长度都是固定的导致显示不太漂亮。

       可以把字条重定向到一个文件中,然后利用yudit编辑器来打开它,这样字体就会正确地显示了。如下:

       ./hello > junk

       yudit junk

       不要忘记在回到正常的工作的时候重置字体。否则你的英文字符在终端中显示会有问题。

       能够从程序中打印出Oriya输出看起来让人高兴,但是这距离理想的目标还具有很长的举例。希望有一天我们不是为了仅仅从中获取到快乐。


添加复杂点的功能:程序的升级

       前面给出了一个简单的如何利用C程序支持简单的Oriya语言的支持。我们可以添加更强一点的功能,例如,可以再加一个printf语句来打印问候的信息。新的hello.c的源代码如下所示:

1    #include

2    #include

3    #include

4    #include

5    int main(void)

6    {

7      setlocale( LC_ALL, "" );

8      bindtextdomain( "hello", "/usr/share/locale" );

9      textdomain( "hello" );

10      printf( gettext( "Hello, world!\n" ) );

11      printf( gettext( "How are you\n" ) );

12      exit(0);

13    }

       对于一个小小的改动,重复前面的工作就够了。也即导出相关的英文文本,把它翻译成为Oriya,然后准备一个新的消息类别。我们甚至可以把工作简化到剪切和粘贴原来oriya.po文件中的大部分内容到新的里面。然而,实际的程序会有成千上万的这样字符串,我们应该仅仅翻译其中改变的部分,然后利用gettext工具把新的翻译和旧的翻译结合起来。这一点确实很重要。


合并新旧翻译工作

       如前述,使用xgettext从hello.c导出可翻译的字符串到一个可移植的对象模板文件hello-new.pot:

       xgettext -d hello -s -o hello-new.pot hello.c

       现在,我们使用一个新的程序,msgmerge来合并已有的.po文件到新的模板文件中,        即:

       msgmerge -s -U oriya.po hello-new.pot

       这里的-s选项会将输出排序,-U选项更新已经存在的.po文件,oriya.po。我们能够使用"-o "来代替-U,以创建一个新的.po文件。更新的.po文件仍然包含旧的翻译,以及新的具有没有翻译的msgid行的实体。对于我们来说,新的行再oriya.po中看起来是这样的:

  #: hello.c:11

  msgid "How are you?\n"

  msgstr ""

       我们使用``ଆପଣ କିପରି ଅଛନ୍ତି?''(oriya语言)来代替英语语法的"How are you?",翻译好的更新oriya.po文件如下:

  # Oriya translations for hello example package.

  # Copyright (C) 2004 Gora Mohanty

  # This file is distributed under the same license as the hello examplepackage.

  # Gora Mohanty , 2004.

  #

  msgid ""

  msgstr ""

  "Project-Id-Version: oriya\n"

  "Report-Msgid-Bugs-To: \n"

  "POT-Creation-Date: 2004-06-23 14:30+0530\n"

  "PO-Revision-Date: 2004-06-22 10:54+0530\n"

  "Last-Translator: Gora Mohanty \n"

  "Language-Team: Oriya\n"

  "MIME-Version: 1.0\n"

  "Content-Type: text/plain; charset=UTF-8\n"

  "Content-Transfer-Encoding: 8bit\n"

  "X-Generator: KBabel 1.3\n"

  

  #: hello.c:10

  msgid "Hello, world!\n"

  msgstr "ନମସ୍କାର\n"


  #: hello.c:11

  msgid "How are you?\n"

  msgstr "ଆପଣ କିପରି ଅଛନ୍ତି?\n"

       把oriya.po编译成机器目标文件,然后像第2.4节说的那样把它们安装到合适的位置。如下:


  msgfmt -c -v -o hello.mo oriya.po

  mkdir -p /usr/share/locale/or_IN/LC_MESSAGES

  cp hello.mo /usr/share/locale/or_IN/LC_MESSAGES

       再重新编译hello.c并且在Oriya语言环境中运行,你可以像前面看到Oriya的输出了。


后面是更多关于gettext的帮助信息,不再翻译了。

More about gettext 

The GNU gettext info pages provide a well-organized and complete description of the gettext utilities and their usage for enabling Native Language Support. One should, at the very least, read the introductory material at gettext::Introduction::, and the suggested references in gettext::Conclusion::References::. Besides the gettext utilities described in this document, various other programs to manipulate .po files are discussed in gettext:Manipulating::. Finally, support for programming languages other than C/C++ is discussed in gettext::Programming Languages::. 



The work of translation 

Besides the obvious program message strings that have been the sole focus of our discussion here, there are many other things that require translation, including GUI messages, command-line option strings, configuration files, program documentation, etc. Besides these obvious aspects, there are a significant number of programs and/or scripts that are automatically generated by other programs. These generated programs might also themselves require translation. So, in any effort to provide support for a given native language, carrying out the translation and keeping up with program updates becomes a major part of the undertaking, requiring a continuing commitment from the language team. A plan has been outlined for the Oriya localization project [2]. 


Acknowledgments 

Extensive use has obviously been made of the GNU gettext manual in preparing this document. I have also been helped by an article in the Linux Journal [3]. 

This work is part of the project for enabling the use of Oriya under Linux. I thank my uncle, N. M. Pattnaik, for conceiving of the project. We have all benefited from the discussions amidst the group of people working on this project. On the particular issue of translation, the help of H. R. Pansari, A. Nayak, and M. Chand is much appreciated. 


The Emacs info browser 

You can start up Emacs from the command-line by typing ``emacs,'' or ``emacs .'' It can be started from the menu in some desktops, e.g., on my GNOME desktop, it is under Main Menu -> Programming -> Emacs. If you are unfamiliar with Emacs, a tutorial can be started by typing ``C-h t'' in an Emacs window, or from the Help item in the menubar at the top. Emacs makes extensive use of the Control (sometimes labelled as ``CTRL'' or ``CTL'') and Meta (sometimes labelled as ``Edit'' or ``Alt'') keys. In Emacs parlance, a hyphenated sequence, such as ``C-h'' means to press the Control and `h' key simultaneously, while ``C-h t'' would mean to press the Control and `h' key together, release them, and press the `t' key. Similarly, ``M-x'' is used to indicate that the Meta and `x' keys should be pressed at the same time. 

The info browser can be started by typing ``C-h i'' in Emacs. The first time you do this, it will briefly list some commands available inside the info browser, and present you with a menu of major topics. Each menu item, or cross-reference is hyperlinked to the appropriate node, and you can visit that node either by moving the cursor to the item and pressing Enter, or by clicking on it with the middle mouse button. To get to the gettext menu items, you can either scroll down to the line, 


  * gettext: (gettext).                          GNU gettext utilities.

and visit that node. Or, as it is several pages down, you can locate it using ``I-search.'' Type ``C-s'' to enter ``I-search'' which will then prompt you for a string in the mini-buffer at the bottom of the window. This is an incremental search, so that Emacs will keep moving you forward through the buffer as you are entering your search string. If you have reached the last occurrence of the search string in the current buffer, you will get a message saying ``Failing I-search: ...'' on pressing ``C-s.'' At that point, press ``C-s'' again to resume the search at the beginning of the buffer. Likewise, ``C-r'' incrementally searches backwards from the present location. 

Info nodes are listed in this document with a ``::'' separator, so that one can go to the gettext::Creating::Header Entry:: by visiting the ``gettext'' node from the main info menu, navigating to the ``Creating'' node, and following that to the ``Header Entry'' node. 


A stand-alone info browser, independent of Emacs, is also available on many systems. Thus, the gettext info page can also be accessed by typing ``info gettext'' in a terminal. xinfo is an X application serving as an info browser, so that if it is installed, typing ``xinfo gettext'' from the command line will open a new browser window with the gettext info page. 



PO file editors 

While the yudit editor is adequate for our present purposes, and we are planning on using that as it is platform-independent, and currently the best at rendering Oriya. This section describes some features of some editors that are specialized for editing PO files under Linux. This is still work in progress, as I am in the process of trying out different editors before settling on one. The ones considered here are: Emacs in po-mode, poedit, kbabel, and gtranslator. 

Emacs PO mode 

Emacs should automatically enter po-mode when you load a .po file, as indicated by ``PO'' in the modeline at the bottom. The window is made read-only, so that you can edit the .po file only through special commands. A description of Emacs po-mode can be found under the gettext::Basics info node, or type `h' or `?' in a po-mode window for a list of available commands. While I find Emacs po-mode quite restrictive, this is probably due to unfamiliarity with it. Its main advantage is that it imposes rigid conformance to the PO file format, and checks the file format when closing the .po file buffer. Emacs po-mode is not useful for Oriya translation, as I know of no way to directly enter Oriya text under Emacs. 

poedit 

XXX: in preparation. 

KDE: the kbabel editor 

kbabel [4] is a more user-friendly and configurable editor than either of Emacs po-mode or poedit. It is integrated into KDE, and offers extensive contextual help. Besides support for various PO file features, it has a plugin framework for dictionaries, that allows consistency checks and translation suggestions. 

GNOME: the gtranslator editor 

XXX: in preparation. 

Bibliography 


G. Mohanty, 

A practical primer for using Oriya under Linux, v0.3, 

http://oriya.sarovar.org/docs/getting_started/index.html, 2004, 

Sec. 6.2 describes the xkb layouts for Oriya. 


G. Mohanty, 

A plan for Oriya localization, v0.1, 

http://oriya.sarovar.org/docs/translation_plan/index.html, 2004. 


Linux Journal article on internationalization, 

http://www.linuxjournal.com/article.php?sid=3023. 


Features of the kbabel editor, 

http://i18n.kde.org/tools/kbabel/features.html. 

About this document ... 

A tutorial on Native Language Support using GNU gettext

This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70) 


Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds. 

Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney. 


The command line arguments were: 

latex2html -no_math -html_version 4.0,math,unicode,i18n,tables -split 0 memo 


The translation was initiated by Gora Mohanty on 2004-07-24 



简单总结:

为了便于操作,不必花费太多时间去阅读本文,我简单地列出了步骤,如果不想看文章细节,照着做就行。

1.源代码hello.c
01    #include 
02    #include 
03    #include 
04    #include 
05    int main(void)
06    {
07      setlocale( LC_ALL, "" );
08      bindtextdomain( "hello", "/usr/share/locale" );//标准路径,不设也行
09      textdomain( "hello" );
10      printf( gettext( "Hello, world!\n" ) );
11      exit(0);
12    }

2.提取要翻译的字符生成模板:
xgettext -d hello -s -o hello.pot hello.c

3.根据模板生成待翻译成目标语言的相应文件:
msginit -l or_IN -o oriya.po -i hello.pot
这里实际可以直接把模板拷贝一份进行翻译,不过最好用这个工具来做。

4.翻译
将上面生成的oriya.po文件中相应的字符串Hello, world!的翻译版本放填到相应位置(msgstr)。如下:
msgid "Hello, world!\n"
msgstr "*****!\n"

5.将翻译好的文件翻译成二进制让gettext可以加载:
msgfmt -c -v -o hello.mo oriya.po
文件名基部要和textdomain指定的一致。

6.将生成的二进制拷贝到合适的位置:
 mkdir -p /usr/share/locale/or_IN/LC_MESSAGES
 cp hello.mo /usr/share/locale/or_IN/LC_MESSAGES

7.编译使用程序:
$echo $LANG
$export LANG=or_IN
$./hello

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值