您的位置:首页 > 运维架构 > Linux

转载_Linux 下解决中文乱码的问题

2013-02-04 14:39 661 查看
我相信有很多象我一样时常工作在VC环境中的程序员来说,当代码移植到Linux下时都会遇到这一问题。今天我就来谈一下自己是如何解决的吧!在网上你一搜索,多数是说setlocale()、gettxt()这类函数可以来解决乱码问题。的确,这类函数是可以搞定的。如下例子:

//prog.cpp

#include<stdio.h>

#include<locale.h>

#include<libintl.h>

#define_(STRING)gettext(STRING)#definePACKAGE"prog"

main(){

setlocale(LC_MESSAGES,"");
textdomain(PACKAGE);
/*这就是指定用

/usr/share/locale/$LOC/LC_MESSAGES/prog.mo

作为讯息档。其中$LOC是在setlocale中设定的*/

printf(_("Thisisateststring.\n"));
/*使用gettext来抓出讯息,再交给printf来印*/

}

接下来要做的工作:

1、使用xgettext产生.pot档:

xgettext-a-oprog.potprog.cpp

2、生成中文翻译的po文件

msginit--locale=zh_CN.utf-8--output=zh_CN.po--input=prog.pot


3、用vi修改po文件将
msgstr""处修改为想要的中文,保存即可。


4、将po文件编译成mo文件


msgfmtzh_CN.po--output=zh_CN.mo


5、将文件编译运行就可以看见了.


下面给出比较给力的文章:



Gettext在C中的用法

1、原始文件

这里使用一个简单的C程序作为例子,注意这个文件是采用utf-8编码的,我故意选择了一个同时包含中文和英文字符串的代码。?

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>


char
*gs=

"Globalstring\n"


int
main(
void
)

{

printf

(
"Hello,world!\n"
);

printf

(
"中文字符串\n"
);

return

0;

}


2、必要的修改

为了让这个程序能够用gettext进行翻译,需要作一些修改(红字部分)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<stdio.h>

#include<locale.h>

#include<libintl.h>


#define_(STR)gettext(STR)

#defineN_(STR)STR


#defineLOCALEDIR"/usr/share/locale"


#definePACKAGE"fool"


char
*gs=N_(
"Globalstring\n"
);


int
main(
void
)

{

setlocale

(LC_ALL,
""
);

bindtextdomain(PACKAGE,LOCALEDIR);

textdomain(PACKAGE);



printf

(_(
"Hellowrold!\n"
));

printf

(_(
"中文字符串\n"
));

printf

(_(gs));


return

0;

}


LOCALEEDIR指定程序要到什么地方查找mo文件。通常是/usr/share/local/,这个目录下面包含

着诸如:en_US、zh_CN、zh_TW的目录。这些目录以LL_CC的形式命名,其中LL是语言名,CC是国家名。我们需要把mo文件放入LL_CC下的LC_MESSAGES目录下面。PACKAGE指定了程序的包名,假如这个程序叫做fool。当程序在某种语言(LANG=en_US)下运行时,就会在LOCALEDIR/LL_CC/LC_MESSAGES下查找fool.mo。如果找到了,程序的结果就会按照这个mo

的内容输出,否则按照源代码中的字符串输出。定义_()宏是一种惯常用法,减少了打字。

另外一个值得注意的地方是N_()这个宏,它仅仅是扩展为原来的字符串。为什么要定义这个宏,因为gs是一个全局的字符串,需要在编译期初始化,如果同样的用_()宏包括这个字符串,程序甚至不能通过编译(至少C语言是这样,C++或许有点不同,不过我不太了解)。这里N_()宏的作用仅仅是为了做个标记,告诉gettext,这个字符串需要翻译。它与_()宏的区别是,后者既是一个标记,又是一个函数调用。所有静态字符串和全局字符串,都可以用N_()宏翻译,而实际显示这个字符串时,还要再加上_()宏来调用gettext。

3、生成po文件的模板

xgettext--keyword=_--keywork=N_--from-code=utf-8--output=fool.pot--package-name=fool--package-version=1.0fool.c

我们在源代码中把gettext()定义为_(),因此这里要手动指定关键字,也就是一个下划线(–keyword=_)。在默认情况下,xgettext会假设源文件的编码为ASCII,但是我们的文件中包含了一个中文字符串,并且以utf-8编码,这时候需要手动指定文件的编码()。–output=fool.pot指定了输出文件名为fool.pot,在默认的情况下是message.po,采用pot的后缀表示这个一个po的template。–package-name=fool
和–package-version=1.0分别指定包名和版本,它们填充fool.pot中的相应字段。这条命令在当前目录下生成了fool.pot文件。

PS.xgettext后面可以同时写上多个文件。或者是在一个单独的文件中列举所有的文件名,并用–files-from=FILE选项指定该文件。

4、生成语言的po文件

源文件中既包含英文,又包含中文,我们可以同时生成两种语言的翻译。

4.1英文翻译

a)生成po文件

msginit--locale=en_US--output=en_US.po--input=fool.pot


命令执行时会提示你输入自己的邮件地址,最后生成en_US.po文件,你可以对比一下en_US.po跟fool.pot有什么异同。en_US.po的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#EnglishtranslationsforPACKAGEpackage.

#Copyright(C)2011THEPACKAGE'SCOPYRIGHTHOLDER

#ThisfileisdistributedunderthesamelicenseasthePACKAGEpackage.

#lyre<lyre@poetpalace.org>,2011.

#

msgid""

msgstr""

"Project-Id-Version:fool1.0\n"

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

"POT-Creation-Date:2011-01-1913:11+0800\n"

"PO-Revision-Date:2011-01-1913:11+0800\n"

"Last-Translator:lyre<lyre@poetpalace.org>\n"

"Language-Team:English\n"

"MIME-Version:1.0\n"

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

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

"Plural-Forms:nplurals=2;plural=(n!=1);\n"


#:fool.c:12

msgid"Globalstring\n"

msgstr"Globalstring\n"


#:fool.c:20

#,c-format

msgid"Hellowrold!\n"

msgstr"Hellowrold!\n"


#:fool.c:21

#,c-format

msgid"中文字符串\n"

msgstr"中文字符串\n"


留意最后一部分,其中每一行msgstr是我们需要翻译的地方,原始文件是中英文混杂的,而我们目前做的是英文翻译,注意到第一个字符串本身就是英文,我们可以简单的清空对应的msgstr,让程序使用默认的字符串。第二个字符串是需要翻译的。修改后的结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
#:fool.c:12

msgid"Globalstring\n"

msgstr""


#:fool.c:20

#,c-format

msgid"Hellowrold!\n"

msgstr""


#:fool.c:21

#,c-format

msgid"中文字符串\n"

msgstr"chinesestring\n"


PS.有专门的程序可以编辑po文件,如gtranslator

b)把po编译成二进制的mo文件

msgfmten_US.po--output=en_US.mo


c)把mo复制到指定位置

sudo
cp
en_US.mo
/usr/share/locale/en_US/LC_MESSAGES/fool
.mo


注意路径和文件名。

4.2中文翻译

与英文是类似的

a)生成po文件

msginit--locale=zh_CN.utf-8--output=zh_CN.po--input=fool.pot


注意–locale=zh_CN.utf-8,中文除了要指定语言国家以外,还要指定编码。修改这个po:

1
2
3
4
5
6
7
8
9
10
11
12
13
#:fool.c:12

msgid"Globalstring\n"

msgstr"全局字符串"


#:fool.c:20

#,c-format

msgid"Hellowrold!\n"

msgstr"你好世界"


#:fool.c:21

#,c-format

msgid"中文字符串\n"

msgstr""


b)把po编译成二进制的mo文件

msgfmtzh_CN.po–output=zh_CN.mo
c)把mo复制到指定位置

sudo
cp
zh_CN.mo
/usr/share/locale/zh_CN/LC_MESSAGES/fool
.mo


注意路径和文件名。

5.编译程序并测试

1
gccfool.c-Wall-ofool


lyre@linux-4179e1:~/Desktop/sample>LANG=en_US./fool

Hello,world!

chinesestring

#注意,这个地方指定了utf-8的编码

lyre@linux-4179e1:~/Desktop/sample>LANG=zh_CN.utf-8./fool

你好,世界!

中文字符串

#我们没有翻译法语,所以是按照原样输出的。

lyre@linux-4179e1:~/Desktop/sample>LANG=fr_FR./fool

Hello,world!

中文字符串

6、更新po

有时候,我们会修改源文件中的字符串,这个时候需要更新po和mo。比如,我们把源代码中的”Hello,world!”修改为”We’verepalacedthisstring\n””。

重新生成po模板:

xgettext--keyword=_--from-code=utf-8--output=fool.pot--package-name=fool

--package-version=1.0fool.c


更新po文件

1
2
msgmerge--updateen_US.pofool.pot

msgmerge--updatezh_CN.pofool.pot


剩下的内容就是重新翻译,生成mo并安装。

7、现实使用

gettext一般是配合autotool一起使用的,使用起来没有那么繁琐。但是学习autotool需要一定的时

间,目前网上很多的教程都是过时的,而autotool的manual并不适合用于学习。我能找到的最好的autotool的教程来自http://www.lrde.epita.fr/~adl/autotools.html,介绍autoconf、automake、libtool以及gettext的使用,其内容比较新,上一次的更新时间在2010年5月,

这一点很重要,保证了按照里面的例子可以得到正确的结果。

看完之后,是不是觉得很不错。不过,这种方法只适合固定的文字翻译,对于我们游戏中类似玩家名字这种变化的文字,这是不可取的。后来我又做了修改环境变量的尝试:

汉字编码:

GB2312字集是简体字集,全称为GB2312(80)字集,共包括国标简体汉字6763个。
BIG5字集是台湾繁体字集,共包括国标繁体汉字13053个。
GBK字集是简繁字集,包括了GB字集、BIG5字集和一些符号,共包括21003个字符。
GB18030是国家制定的一个强制性大字集标准,全称为GB18030-2000,它的推出使汉字集有了一个“大一统”的标准。

ASCII:
AmericanStandardCodeforInformationInterchange,美国信息交换标准码。目前计算机中用得最广泛的字符集及其编码,由美国国家标准局(ANSI)制定。它已被国际标准化组织(ISO)定为国际标准,称为ISO646标准。ASCII字符集由控制字符和图形字符组成。在计算机的存储单元中,一个ASCII码值占一个字节(8个二进制位),其最高位(b7)用作奇偶校验位。

UTF:

Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的,但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(UnicodeTranslationFormat,简称为UTF)。

*UTF-8:8bit变长编码,对于大多数常用字符集(ASCII中0~127字符)它只使用单字节,而对其它常用字符(特别是朝鲜和汉语会意文字),它使用3字节。

*UTF-16:16bit编码,是变长码,大致相当于20位编码,值在0到0x10FFFF之间,基本上就是unicode编码的实现,与CPU字序有关。

注意:兼容性最好的编码就是UTF-8!毕竟GBK/GB2312是国内的标准,当我们大量使用国外的开源软件时,UTF-8才是编码界最通用的语言。

具体解决办法如下:

1、console终端乱码

  在/etc/profile文件的最后一行添加如下内容:

  exportLC_ALL="zh_CN.GB18030"

2、xwindow终端乱码

  在/etc/sysconfig/i18n文件的最后一行添加如下内容:

  exportLC_ALL="zh_CN.GB18030"

3.修改i18n文件

vi/etc/sysconfig/i18n

将内容改为

LANG="zh_CN.GB18030"

LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"

SUPPORTED="zh_CN.GB18030:zh_CN:zh:en_US.UTF-8:en_US:en"

SYSFONT="lat0-sun16"

之后重启机器,这样中文在SSH,telnet终端就可以正常显示了。

做完这些后,感觉都不是自己理想的效果,最后,我想Linux既然是UTF-8编码的,而Windows是GB2312或者说是GBK编码。这就是问题的关键了。所以我干脆将文件转成UTF-8试试,没料真的成功了。呵呵,后来我直接将虚拟终端的编码设置成GB18030就行了。当然这是一种偷懒的方法,若想让程序成UTF-8可以用iconv命令将文件转为UTF-8或将指定的地方用iconv函数转即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: