介绍
GetText 是 GNU 的一个项目,GetText 主页有最丰富的信息。
GetText 官网:
http://www.gnu.org/software/gettext/
下面大致翻译一下 GetText 主页的介绍 :
通常,程序及其文档信息都是用英语写的,程序运行时同用户交互的信息也是英语。这是一个事实,不仅仅 GNU 的软件是这样,其他大部分私有软件或自由软件也是这样。一方面,对于来自所有国家的开发者、维护者和用户来说,相互沟通中使用一种通用的语言非常的方便。另一方面,相对于母语来说大多数人并不适应使用英语,而且他们的日常工作都是尽可能的使用他们自己的母语。多数人都会喜欢他们的计算机屏幕显示更少的英语,显示的母语更多。
GNU gettext 对于 GNU 翻译项目而言,是非常重要的一步。我们需要依赖于它,以便于做更多其他的事情。这个软件包给程序员、翻译者,或者用户提供了一套集成工具和文档。详细地说,GNU gettext 提供了一套工具,能让其他 GNU 软件创建多种语言信息… …”
翻译真累!直接说汉语吧!
gettext的工作流程是这样的:比如我们写一个 C 程序,通常 printf 等输出信息都是 English 的。如果我们在程序中加入 gettext 支持,在需要交互的字符串上用 gettext 函数,程序运行是就可以先调用 gettext 函数处理字符串,替换当前的字符串(注意是运行时替换的哟)。
简单使用的例子
一个简单的例子源码如下:
#include <stdio.h>
#include <locale.h>
#include <libintl.h>
/* 使用 gettext 通常使用类似下面的一个带函数的宏定义
* 你完全可以不用,直接使用 gettext(字符串)
*/
#define _(S) gettext(S)
/* PACKAGE是本程序最终的名字(运行时输入的命令)*/
#define PACKAGE "jian_gettext"
int main(int argc, char **argv)
{
/* 下面几个函数都是使用 gettext 时候需要使用的
* setlocale(LC_ALL,"");
* bindtextdomain(PACKAGE, LOCALEDIR);
* bind_textdomain_codeset(PACKAGE, "UTF-8");
* textdomain(PACKAGE);
*/
setlocale(LC_ALL,"");
bindtextdomain(PACKAGE, "locale");
textdomain(PACKAGE);
printf(_("Hello,GetText!\n"));
return 0;
}
使用 gettext 的一般流程
1. 头文件包含:
#include <locale.h>
#include <libintl.h>
2. 定义宏定义(完全是为了方便,而且几乎所有程序都这样)
#define _(S) gettext(S)
3. 设置 locale 和 mo 文件将要存放的路径,关联程序
setlocale(LC_ALL,"");
bindtextdomain(PACKAGE, "locale");
textdomain(PACKAGE);
4. 制作 po 文件(示例程序是:jian_gettext.c)
xgettext -a jian_gettext.c -o jian_gettext.po --from-code=utf-8
5. 修改生成的 jian_gettext.po 文件,将下面行中的 CHARSET 换成 UTF-8
"Content-Type: text/plain; charset=CHARSET\n"
再翻译其中的string(其他的string不要改):
#: jian_gettext.c:14
#, c-format
msgid "Hello,GetText!\n"
msgstr "你好,GetText!\n"
6. 生成 mo 文件,
msgfmt jian_gettext.po -o jian_gettext.mo
7. 创建存放 mo 文件的目录 (例子: 使用 ./locale )
mkdir -pv locale/zh_CN/LC_MESSAGES/
cp -v jian_gettext.mo locale/zh_CN/LC_MESSAGES/
8. 运行程序,你的程序应该是这样编译好的:
gcc -Wall -o jian_gettext jian_gettext.c
没有错误,上面的程序运行结果如下:
# ./jian_gettext
你好,GetText!
细节
setlocale()
setlocale(LC_ALL,””) 的意思是使用环境变量(系统当前的值)
xgettext
-k
该参数可以只搜索某些字符串,没有用的信息省略。xgettext jian_gettext.c -k_ -o jian_gettext.po
SofTool.CN Note:
-k后面的 _ 是什么意思?-l
该参数 可以针对某一语言生成 po 文件,中文为例:msginit -l zh_CN -i jian_gettext.po
上面命令会生成 zh_CN.po 文件,可由此修改生成mo文件
多文件的 PO
一个开源项目中,通常有很多源文件,我们都需要翻译其中的字符串。通常步骤如下:
进入 PO 目录执行(一定要是 PO 目录):
# intltool-update -m
得到一个文件列表告诉我应该把它们加入 POTFILES.in 文件.
跳到 PO 目录的父目录,使用新的 POTFILES.in 生成 POT 文件:
xgettext -a -f po/POTFILES.in -o po/xxx.pot
我使用的一个示例:
# xgettext -k_ -f po/POTFILES.in -o po/mutter-moblin.pot --from-code="utf-8"
intltool-update —pot 生成 pot 文件intltool-update —maintain
示例:
创建po文件
创建并进入/po目录,创建 POTFILES.in ,内容为你需要提取源码中有需要翻译的字符串的源文件列表,每行一个文件,然后执行 intltool-update —pot 产生 pot 文件,如果将来你在维护代码中代码有变化或者在 POTFILES.in 中新增了源文件,可用 intltool-update —maintain 更新。
有了 pot 文件,现在需要产生各种语言的 po 文件,如 msginit —locale=zh_CN 产生中文 po 文件,如将来源文件有变化可以 intltool-update zh_CN 更新 zh_CN.po ,接下来翻译的工作就是你自己来做了。
要生成特定语言的 mo 文件,需要维护 configure.ac 中的 ALL_LINGUAS ,以空格分隔就可以了
GTK 程序中使用 gettext
首先,您必须声明一些允许 gettext 库为您的应用程序找到正确消息的名称。请注意,在现实的场景中,构建系统将为您处理这些名称,但是出于我们的需要,可以使用下面的名称:
GETTEXT_PACKAGE "foo-app"LOCALEDIR "mo"
在此之后,您必须正确地包含 gettext Header。完成该任务的最简单的方法是使用 GLib 提供的可用 Header(在 Versions 2.4 及更高版本中可用):
#include <glib/gi18n.h>
这个 Header 为您提供了 _()
和 N()
宏,用来标记可翻译的字符串,稍后您将看到。
现在,您必须以一种 gettext 可以识别并在运行时进行翻译的方式来标记用户可见的字符串。出于这两个目的,您可以使用 _() 宏,它是 gettext() 函数完整调用的简短别名。
gettext() 函数查找消息目录中提供的字符串,以判断对于当前语言,是否存在一个经过翻译的合适的版本。如果存在,它会返回翻译结果;否则,它会返回原始字符串。当您为源代码进行翻译准备工作时,在扫描过程中将单词 gettext 作为标记,以便提取要翻译的消息并将其放置到单独的文件中。
认识到了这一点,您就可以开始以不同的语言进行表达了。在程序开始执行之前,您必须初始化 gettext:
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
现在,您可以使用 gettext() 调用来替换所出现的每个可以翻译的字符串。因此,下面的代码行形式:
gtk_label_set_markup(GTK_LABEL (label1), "<b>Normal mode Foo, translated:</b>");
变为:
gtk_label_set_markup(GTK_LABEL (label1), _("<b>Normal mode Foo, translated:</b>"));
这就是与此有关的全部内容,其中有两点例外之处。一个是不能在静态字符串中使用 gettext(),因为它是一个函数。在这种情况下,可以使用 N_() 宏,它不进行任何扩展,但会被作为对翻译内容进行标记的关键词。稍后在需要使用的地方,可以和前面一样使用 () 。
const char *msg = N_("Important message");
do_important_stuff(_(msg));
另一个例外是包含某个数值变量的情况,如检索的文件的数目。在这种情况下,可以使用 ngettext() 函数,它能够理解复数。GNU gettext 库手册中包含了该函数的细节信息,以及关于 gettext() 函数的使用和操作的细节信息。
最后,请正确地选择哪些内容应该翻译,哪些内容不应该翻译。通常,所有用户可见的字符串都是翻译工作的候选对象。然而,对于调试信息和其他面向开发人员的信息,可以保留其中的部分或所有内容不进行翻译,以使得您自己和其他开发人员能够理解并在源代码中对其进行查找。FooWidget 便是这种方式的一个示例(请参见下面的“自定义小部件”),它包含了一个模拟的调试模式,在该模式中 RTL 或者 LTR 标记都没有进行翻译。
与此类似,避免翻译那些不是真正的单词的内容。例如,不要翻译 TCP/IP 状态标记,即使它们最初来自于英语单词。这类错误几乎随处可见,例如,在Microsoft Windows 的网络工具中,类似 SYN_ACK 的内容被翻译 为毫无意义的波兰语 ZGODN_POTW。其结果是所有的人,包括说本族语的人,都被弄糊涂了,感到无法理解,甚至在 Internet 上查阅这样的信息。
使用 <glib/gi18n.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
{
gtk_set_locale();
//bindtextdomain (PACKAGE, LOCALEDIR);
//textdomain (PACKAGE);
bindtextdomain ("test", "/usr/share/locale");
textdomain ("test");
gtk_init (&argc, &argv);
...
gtk_window_set_title(GTK_WINDOW(dialog), _("hi"));
...
}
使用 <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#define GETTEXT_PACKAGE "test"
#include <glib/gi18n-lib.h>
{
gtk_init (&argc, &argv);
...
gtk_window_set_title(GTK_WINDOW(dialog), _("hi"));
...
}
上述两种方法是一样的。第一种中 gi18n.h将为我们引用
#include <libintl.h>
同时定义
#define _(String) gettext (String)
而第二中方法中gi18n-lib.h必须在我们定义了 GETTEXT_PACKAGE 宏之后才为我们定义
#define _(String) dgettext (GETTEXT_PACKAGE, String)
由于
gtk_init(); -->gtk_init_check() -->gtk_parse_args() -->gettext_initialization ()
gettext_initialization的代码实现:
static void
gettext_initialization (void)
{
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
bindtextdomain (GETTEXT_PACKAGE "-properties", GTK_LOCALEDIR);
# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
bind_textdomain_codeset (GETTEXT_PACKAGE "-properties", "UTF-8");
# endif
#endif
}
mutter-moblin 项目 i18n 实现
mutter-moblin 已经有 i18n 架构了,只需要生成 po 文件,设置 lang,翻译 po 就可以了。
设置 LINGUAS 环境变量
直接修改 configure 也可以,但是不推荐。
# Substitute ALL_LINGUAS so we can use it in po/Makefile
ALL_LINGUAS="zh_CN"
通常在 po/ 目录下创建一个文件 LINGUAS
echo zh_CN > po/LINGUAS
创建 zh_CN.po 文件
下面操作都在 po 目录下:
1. 更新 POTFILES.in 文件
[root@localhost po]# intltool-update --pot # 生成 mutter-moblin-netbook-plugin.pot
[root@localhost po]# ll
总计 28
-rw-r--r-- 1 root root 6 06-18 01:03 LINGUAS
-rw-r--r-- 1 1000 1000 6899 06-05 21:54 Makefile.in.in
-rw-r--r-- 1 root root 8998 06-18 01:04 mutter-moblin-netbook-plugin.pot
-rw-r--r-- 1 1000 1000 978 06-05 20:46 POTFILES.in
2. zh_CN.po
[root@localhost po]# msginit --locale=zh_CN
新的翻译库应该包含您的电子邮件地址,这样用户就可以对您的翻译提出意见,
同时软件维护者也可以在出现技术问题时联络您。
Is the following your email address?
root@localhost.localdomain
Please confirm by pressing Return, or enter your email address.
jian.li@turbolinux.com.cn
A translation team for your language (zh) does not exist yet.
If you want to create a new translation team for zh or zh_CN, please visit
http://www.iro.umontreal.ca/contrib/po/HTML/teams.html
http://www.iro.umontreal.ca/contrib/po/HTML/leaders.html
http://www.iro.umontreal.ca/contrib/po/HTML/index.html
已创建 zh_CN.po。
注意,上面创建的 zh_CN.po 文件是 gbk 编码,现在多用 UTF-8 编码。所以需要用下面命令创建(其实就是把 locale 参数的值 zh_CN 换成 zh_CN.utf8):
# msginit -l zh_CN.utf8
接下来翻译 po 文件。
3. 检查翻译正误
翻译过程中通常有一些粗心的错误。需要检查:
msgfmt -c mutter-moblin-0.27.6/po/zh_CN.po