Linux下C语言的本地化/国际化实现

在linux系统下,以c语言程序为例来实现程序的国际化,即让程序根据Linux系统不同的语言环境的不同来显示出对应该语言的文字,即先让c程序支持国际化然后再进行本地化翻译。

Linux上实现这个过程需要用到xgettext和msgfmt这两个工具。

Xgettext 是国际化的工具,用来提取程序中的字符串,生成*.po或是*.pot的文件,

msgfmt是本地化的工具,用来编译翻译后的.po文件为.mo文件,这样系统在启动时候会扫描系统环境提取对应名字的.mo文件中的字符串替代原来的英文,实现本地化。

如我们来做一个简单的rpm包,包文件的目录树如下:

hello.c /*我们用来测试的c语言程序*/

po /zh_CN.po /*放在该包根目录下的po目录。对应于该程序进行的中文翻译*/

第一步、支持国际化的C程序(hello.c)如下:

#include 
#include  // gettext 库支持
#include  // 本地化locale的翻译支持

#define _(STRING) gettext(STRING) //为了减少代码量和输入量,用别名_(STRING)替换gettext(STRONG)
#define PACKAGE "hello" // 定义软件包的名字为hello
#define LOCALEDIR “/usr/share/locale/” //定义系统语言环境的目录,该目录下存放各种国际化的语言,不同系统可能有差异。


int main (int argv, char *argc[] )
{
  setlocale (LC_ALL, ""); //设置locale为系统默认语言环境,gtk程序不需要,因为gtk_init()已经帮我们做了
  bindtextdomain (PACKAGE, LOCALEDIR); //关联包名称及其对应的国际化翻译语言所在路径
  textdomain (PACKAGE); //定义.mo文件的名字,省略“.mo”后缀,(可以多个路径下的不同mo文件)

  printf(_("Hello, World\n")); //国际化字符串支持,结合上面的替换,实际应是:printf(gettext("Hello, World\n"));
  printf(_("This is a example of locale&rpm @ Neoshine Desktop 5\n"));

  return 0;
} 
第二步、提取待翻译 po并进行本地化

先用下面的命令提取出c程序中需要进行本地化的语言文件。

[[email protected] hello]$  xgettext  -k––keyword=_  hello.c  -o  hello.pot

再用poedit或是vim翻译hello.pot中对应英文为中文并另存为zh_CN.po,作为中文本地化的翻译文件。

如下面翻译后的zh_CN.po文件的内容:

1 msgid “”2 msgstr “”

3 “Project-Id-Version: n”

4 “Report-Msgid-Bugs-To: n”

5 “POT-Creation-Date: 2010-03-11 11:52+0800n”

6 “PO-Revision-Date: n”

7 “Last-Translator: Leedd n”

8 “Language-Team: n”

9 “MIME-Version: 1.0n”

10 “Content-Type: text/plain; charset=UTF-8n”

11 “Content-Transfer-Encoding: 8bitn”

12

13 #: hello.c:15

14 #, c-format

15 msgid “Hello, World!n”

16 msgstr “你好,世界!n”

17

18 #: hello.c:16

19 #, c-format

20 msgid “This is a example of locale&rpm @ Leedd.com !n”

21 msgstr “这是在沉思小屋下的关于”本地化”和”rpm打包”的测试!n”

第三步、把翻译后的文件打包

制作rpm包相当于给源程序添加一个外衣,让用户很容易的安装、使用,而不用理会源程序的编译过程等细节。如果要进行rpmbuild我们需要先写个SPEC文件,如下:

Neoshine-hello-wrold.spec文件内容

Name: neoshine-hello-world
Version: 5.0
Release: 1
License: GPL+
Group: Applications/System
URL: http://leedd.com

Source0: %{name}-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Summary: A applet for Neoshine Desktop (or the other similar Linux system).
BuildRequires: gettext
BuildRequires: gcc

%description
A hello world examples for the locale&rpm

%prep
%setup -q


##,%build


%install
mkdir -p ${RPM_BUILD_ROOT}/usr/share/locale/zh_CN/LC_MESSAGES
mkdir -p ${RPM_BUILD_ROOT}/var/tmp/hello
xgettext -k –keyword=_ hello.c -o po/hello.pot
pushd ./po
msgmerge zh_CN.po hello.pot
msgfmt zh_CN.po -o hello.mo
cp hello.mo ${RPM_BUILD_ROOT}/usr/share/locale/zh_CN/LC_MESSAGES
popd

gcc hello.c -o ${RPM_BUILD_ROOT}/var/tmp/hello/hello.out

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)
/var/tmp/hello/*
/usr/share/locale/zh_CN/LC_MESSAGES

%changelog
* Thu Mar 11 2010 Leedd  5.0-1-Inital
已经有了源文件和 SPEC文件后,需要把他们分别放在 rpmbuild目录下对应的 SOURCESSPECS目录中,然后打包:

[[email protected] SPECS]$ rpmbuild -bs neoshine-hello-world.specWrote: /home/Lee/rpmbuild/SRPMS/neoshine-hello-world-5.0-1.src.rpm

[[email protected] SPECS]$ mock -r neoshine-5-i686––rebuild  /home/Lee/rpmbuild/SRPMS/neoshine-hello-world-5.0-1.src.rpm

本文用到的源程序:
 http://leedd.com/docs/linux/hello-world-c/ 
 SOURCES源文件:neoshine-hello-world-5.0.tar.bz2
 SPEC文件:  neoshine-hello-world.spec
 编译后的源码包:neoshine-hello-world-5.0-1.src.rpm

参考文章:

[1] Hello Worldhttp://zh.wikipedia.org/wiki/Hello_World#C

[2] Linux 国际化编程 http://riuliyu.blog126.fc2.com/blog-entry-20.html

[3] 认识下linux下程序的国际化(C语言实例)

http://swinging-breeze.blogspot.com/2009/05/linuxc.html(已经被墙)

Google的缓存:http://203.208.39.132/search?q=cache:E-Os3g0yHjsJ:swinging-breeze.blogspot.com/2009/05/linuxc.html+hello+world+%E5%9B%BD%E9%99%85%E5%8C%96+c%E8%AF%AD%E8%A8%80&cd=1&hl=zh-CN&ct=clnk&gl=cn&st_usg=ALhdy2_j1VcwccRlk394Xz6n81ZnDCvbuQ

附加:如何把“Hello world!”打印显示为“你好,世界!”?

1)编辑test_helloworld.c

$vim test_helloworld.c
#include   
#include   
#include   

#define PACKAGE_NAME "test_helloworld"  
#define PACKAGE_LOCALEDIR "po"  
#define _(x) gettext(x)  
 
int main (int argc, char *argv[])  
{  
  setlocale(LC_ALL, "");  
  // Comment: printf "1 Hello world!"  
  printf(_("1 Hello world!\n"));  
  setlocale(LC_MESSAGES, "zh_CN.UTF-8");  
  bindtextdomain(PACKAGE_NAME, PACKAGE_LOCALEDIR);  
  textdomain(PACKAGE_NAME);   
  // Comment: printf "2 Hello world!"  
  printf(_("2 Hello world!\n"));     
  return 0;  
}  

2)从.c源文件中提取需要进行国际化/翻译的字串,生成.po(portable object file)

$ xgettext --add-comments --keyword=_ test_helloworld.c -o test_helloworld.pot --from-code=UTF-8
$ cp test_helloworld.pot test_helloworld.po

3)修改并翻译字串文件.po

用xgettext命令提取出来的.pot文件,修改和翻译后的.po文件内容如下:

# SOME DESCRIPTIVE TITLE.  
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER  
# This file is distributed under the same license as the PACKAGE package.  
# FIRST AUTHOR , YEAR.  
#  
#, fuzzy  
msgid ""  
msgstr ""  
"Project-Id-Version: PACKAGE VERSION\n"  
"Report-Msgid-Bugs-To: \n"  
"POT-Creation-Date: 2013-01-14 16:42+0800\n"  
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"  
"Last-Translator: FULL NAME \n"  
"Language-Team: LANGUAGE \n"  
"Language: \n"  
"MIME-Version: 1.0\n"  
"Content-Type: text/plain; charset=UTF-8\n"  
"Content-Transfer-Encoding: 8bit\n"  
  
#. Comment: printf "1 Hello world!"  
#: test_helloworld.c:14  
#, c-format  
msgid "1 Hello world!\n"  
msgstr "1 你好,世界!\n"  
  
#. Comment: printf "2 Hello world!"  
#: test_helloworld.c:22  
#, c-format  
msgid "2 Hello world!\n"  
msgstr "2 你好,世界!\n"

4)把翻译好的.po文件转化为程序运行时可读取的二进制文件.mo(machine object file)

$ msgfmt test_helloworld.po -o test_helloworld.mo
(为了查看,也可将mo文件反向转换成po文件,$msgunfmt test_helloworld.mo -o test_helloworld.po)

5)把转化好的.mo文件放置到相应的LOCALEDIR路径下供程序执行时使用

$ mkdir -p po/zh_CN.UTF-8/LC_MESSAGES/
$ cp test_helloworld.mo po/zh_CN.UTF-8/LC_MESSAGES/

6)最后就可以编译并运行

$ gcc -Wall test_helloworld.c -o test_helloworld
$ ./test_helloworld 
1 Hello world!
2 你好,世界!

GNU locale 

locale被设计用来设定当前系统区域环境相关的信息来满足不同用户对国际化和本地化的需要,相关环境变量信息如下:

    $ locale  
    LANG=en_US.UTF-8  
    LANGUAGE=  
    LC_CTYPE="en_US.UTF-8"  
    LC_NUMERIC="en_US.UTF-8"  
    LC_TIME="en_US.UTF-8"  
    LC_COLLATE="en_US.UTF-8"  
    LC_MONETARY="en_US.UTF-8"  
    LC_MESSAGES=en_US.UTF-8  
    LC_PAPER="en_US.UTF-8"  
    LC_NAME="en_US.UTF-8"  
    LC_ADDRESS="en_US.UTF-8"  
    LC_TELEPHONE="en_US.UTF-8"  
    LC_MEASUREMENT="en_US.UTF-8"  
    LC_IDENTIFICATION="en_US.UTF-8"  
    LC_ALL=  

GNU gettext 

gettext被设计用来实现国际化和本地化,通过gettext可以索引到字串的多国语言翻译,从而为国际化和本地化的字串显示提供基础。

Self-check

要理解test_helloworld.c里面相关locale和国际化相关API最好的方法就是亲自读一读glibc中的locale和intl相关目录下的src code,如setlocale.c、textdomain.c、bindtextdom.c、gettext.c等。多用man命令查一下这些命令或API,配合google和wikipedia。

你可能感兴趣的:(linuxC/C++编程)