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

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

// prog.cpp

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

#define _(STRING) gettext(STRING)
#define PACKAGE "prog"

main()
{
    setlocale(LC_MESSAGES, "");
    textdomain(PACKAGE);
        /* 这就是指定用
            /usr/share/locale/$LOC/LC_MESSAGES/prog.mo
           作为讯息档。其中 $LOC 是在 setlocale 中设定的 */
    printf(_("This is a test string.\n"));
        /* 使用 gettext 来抓出讯息,再交给 printf 来印 */
}

接下来要做的工作:

1、使用 xgettext 产生 .pot 档: 
        xgettext -a -o prog.pot prog.cpp

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

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

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

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

msgfmt zh_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 = "Global string\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)
#define N_(STR) STR
  
#define LOCALEDIR "/usr/share/locale"
  
#define PACKAGE "fool"
  
char *gs = N_("Global string\n");
  
int main (void)
{
    setlocale (LC_ALL, "");
    bindtextdomain (PACKAGE, LOCALEDIR);
    textdomain (PACKAGE);
  
    printf (_("Hello wrold!\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.0 fool.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
# English translations for PACKAGE package.
# Copyright (C) 2011 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# lyre <lyre@poetpalace.org>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: fool 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-19 13:11+0800\n"
"PO-Revision-Date: 2011-01-19 13: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 "Global string\n"
msgstr "Global string\n"
  
#: fool.c:20
#, c-format
msgid "Hello wrold!\n"
msgstr "Hello wrold!\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 "Global string\n"
msgstr ""
  
#: fool.c:20
#, c-format
msgid "Hello wrold!\n"
msgstr ""
  
#: fool.c:21
#, c-format
msgid "中文字符串\n"
msgstr "chinese string\n"

PS. 有专门的程序可以编辑 po 文件,如 gtranslator
b) 把 po 编译成二进制的 mo 文件

msgfmt en_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 "Global string\n"
msgstr "全局字符串"
  
#: fool.c:20
#, c-format
msgid "Hello wrold!\n"
msgstr "你好世界"
  
#: fool.c:21
#, c-format
msgid "中文字符串\n"
msgstr ""

b) 把 po 编译成二进制的 mo 文件
msgfmt zh_CN.po –output=zh_CN.mo
c)把 mo 复制到指定位置

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

注意路径和文件名。

5.编译程序并测试

1
gcc fool.c -Wall -o fool

lyre@linux-4179e1:~/Desktop/sample> LANG=en_US ./fool
Hello, world!
chinese string

#注意,这个地方指定了 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’ve repalaced this string\n””。

重新生成 po 模板:

xgettext --keyword=_ --from-code=utf-8 --output=fool.pot --package-name=fool
 
--package-version=1.0 fool.c
 
更新 po 文件
1
2
msgmerge --update en_US.po fool.pot
msgmerge --update zh_CN.po fool.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:

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

UTF:

Unicode 的实现方式不同于编码方式。 一个字符的Unicode编码是确定的,但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。 Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 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文件的最后一行添加如下内容:
  export LC_ALL="zh_CN.GB18030"
2、xwindow终端乱码
  在/etc/sysconfig/i18n文件的最后一行添加如下内容:
  export LC_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 函数转即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值