随笔 - 96  文章 - 255  trackbacks - 0
<2008年3月>
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345

E-mail:zbln426@163.com QQ:85132383 长期寻找对战略游戏感兴趣的合作伙伴。

常用链接

留言簿(21)

随笔分类

随笔档案

SDL相关网站

我的个人网页

我的小游戏

资源下载

搜索

  •  

积分与排名

  • 积分 - 488146
  • 排名 - 37

最新评论

阅读排行榜

评论排行榜

作者:龙飞

        自从开始研究SDL的文本显示,我就一直在思考在SDL中显示中文的问题。我们知道韦诺之战Battle for Wesnoth)使用SDL开发的,并且支持多语言。所以,我一直相信Wesnoth的源代码里面一定有我所需要的答案。网络上是纵说纷纭啊,有些人干脆说,SDL不支持中文;有些人在困难面前回到了MFC的怀抱。而,既然我的目标是跨平台,并且我也相信一定能找到答案,所以,我坚持寻找。终于,完美解决了在SDL中显示中文,甚至多语言的问题。以下的几节,我将全面,详细的说明这些方法。

1.1:po,mo与gettext

        线索从Wesnoth的发布游戏与源代码中开始,我们知道,在Wesnoth游戏中,有个名为po的文件夹,多国语言翻译都放在了这个文件夹下面。游戏程序中多为*.mo文件,源代码中多为*.po文件。通过搜索,po与mo的背景浮出水面——它们来自GNU项目gettext
        gettext项目是专门为多语言设计的。我们不需要修改源代码和程序的情况下,可以让程序支持多国语言。程序将根据系统所在的国家和区域选择相应的语言,当然,也可以在执行过程中让玩家自由的选择。既然是开放源代码的,自然也很容易的被移植到win32下。win32下的这个项目主页如下:
http://gnuwin32.sourceforge.net/packages/gettext.htm
        为了方便的使用,我还是建议你下载完整的安装包(Complete package)。然后,你可以看英文说明,也可以凭着直觉去试验,找到哪些库和哪些DLL文件是编译和运行时必须的——当然,我也可以直接告诉你答案。
        设置编译环境的问题就不再多说了,不清楚的请看前面的章节。反正都三部分:*.h文件,*.lib文件和*.dll文件,放到相应的文件夹下面并在编译时候指明就可以了。
        我们下面将用到的文件有:
libintl.h:请在写源代码的时候#include进来;
libintl.lib:这是编译时候需要的库文件;
libintl3.dll和libiconv2.dll:这是程序运行时候需要的文件,放到*.exe文件可以找到的地方。

1.2:演示程序以及说明

#include <iostream>
#include 
<string>
#include 
<clocale>
#include 
"GNU/libintl.h"

int main(int argc, char* argv[])
{
    setlocale(LC_ALL, 
"");
    bindtextdomain(
"myText""E:/My Documents/Visual Studio 2008/po");
    textdomain(
"myText");
    std::
string test = gettext("Hello, World!");
    std::cout 
<< test << std::endl;
    
return 0;
}
        我们先说#include进来的<clocale>,我用“<>”表示它是标准C++的一部分。它包含了函数setlocale()。这个函数在这里的两个参数——常量LC_ALL与空字符串""的意思是,在这个程序中的所有语言与区域,都设置为系统默认的语言与区域。
        libintl.h是我们刚才加入的GNU的一部分,这意味着在Linux系统下,这个头文件是系统本身自带的。它包含了后面三个函数:bindtextdomain()将一个文件夹目录绑定到一个域名上,这个域名也是将来*.mo文件的文件名;textdomain()表明我们将使用的域名;gettext()中的字符串将是被多语言翻译替换的部分。
        将这个程序编译,在没有多语言包的时候,程序也能正常的运行,显示“Hello, World!”。

1.3:为源程序制作po文件和mo文件

        如果你已经安装了完整的安装包,找到相关文件夹的bin目录,这里有很多工具软件。你可以通过cmd的方式一步步的转换,也可以,偷点儿懒,因为有更加现成的工具可以用。但是,第一步,从源代码提取gettext()的文本,还得靠命令:xgettext。就跟用g++命令一样,假设我们的源文件名是main.cpp,我们把它先转换成一个模板文件a.pot:
xgettext -o a.pot main.cpp
        你可以用vim之类的文本编辑器看看*.pot文件的内容,你会发现,一些说明,以及提取文本的详细信息被纪录了下来。
# 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 
<EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid 
""
msgstr 
""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-03-30 00:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: main.cpp:
11
msgid 
"Hello, World!"
msgstr 
""
下面,我们使用一个简单的小工具poedit。又一个跨平台的软件,主页在:
http://www.poedit.net/
安装运行后,选择“从POT文件更新类目”,然后打开我们刚才的a.pot,什么都不用修改(当然,你也可以把自己信息都写上去),确保“字符集”是UTF-8就可以了。然后,在英语下面也上替换的文字吧,保存的时候,相应的mo文件也就建立起来了。
msgid ""
msgstr 
""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-03-30 00:24+0800\n"
"PO-Revision-Date: 2008-03-30 00:25+0800\n"
"Last-Translator: lf426 <zbln426@163.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: main.cpp:
11
msgid 
"Hello, World!"
msgstr 
"浜茬埍鐨勪笘鐣岋紝鎴戞潵浜嗭紒"
这是po文件。怎么是乱码?那是因为windows不是用UTF-8保存的文本文件(默认一般是GB2312)。用poedit打开时候是正确显示的。我的文本内容是:“亲爱的世界,我来了!”。
如果你用的是vim,可以通过设置环境变量解决显示乱码的问题,在_vimrc文件中添加这一句:
set fileencodings=gb2312,ucs-bom,utf-8,chinese


1.4:设置mo文件的目录

        下面的工作可能就有些教条了。还记得我们绑定域名的路径吧,我用的是
E:\My Documents\Visual Studio 2008\po
(请注意在C++程序里面把斜杠反过来!)
*.mo文件并不是直接放到这个路径下,而是这个路径下的./LL/LC_MESSAGES或者./LL_CC/LC_MESSAGES。其中LL表示语种,CC表示国家或区域。具体的请参考Wesnoth。就我们的中文来说,这个例子放mo文件的路径是:
E:\My Documents\Visual Studio 2008\po\zh_CN\LC_MESSAGES
        现在运行程序就可以看到文本已经被替换了。如果我们删除mo文件或修改mo文件名(与绑定域名不一致),程序会继续显示原来的英文。如果我们改变系统环境,只要不是中国中文,程序都还是显示英文。如果我们要更新替换内容,直接用poedit更新po和mo文件就可以了。
 
1.5:构建StringData类

        我们希望字符串的数据单独的保存在一个文件里,这样既方便被gettext提取,也方便修改。而且,在程序里面,我们尽量把gettext涉及到的一些特殊的设置隐藏了。所以,我们构建StringDada类,在程序中需要用到的地方,直接调用它的对象就可以了。
//FileName: string_data.h
#ifndef STRING_DATA_H
#define STRING_DATA_H

#include 
<clocale>
#include 
<string>
#include 
<vector>
#include 
"GNU/libintl.h"

class StringData
{
private:
    std::vector
<std::string> data;
public:
    StringData();
    std::
string operator [](const unsigned int& n) const;
};

#endif
我重载了[],这样在调用数据的时候更加直观。我们将数据都写在StringData的构造函数中,将来gettext也只需要提取StringData的实现文件就可以了。
#include "string_data.h"

StringData::StringData()
{
    setlocale(LC_ALL, 
"");
    bindtextdomain(
"StringData""./po");
    textdomain(
"StringData");

    
//0
    data.push_back(gettext("Up was pressed."));
    
//1
    data.push_back(gettext("Down was pressed."));
    
//2
    data.push_back(gettext("Left was pressed."));
    
//3
    data.push_back(gettext("Right was pressed."));
    
//4
    data.push_back(gettext("Other key was pressed."));
}

std::
string StringData::operator [](const unsigned int& n) const
{
    
if ( n >= data.size() )
        return 0;

    
return data[n];
}


1.6:做个gettext的批处理

        如果你按照我全面介绍的,安装了Poedit,也安装了GnuWin32,那么,我们做个批处理文件让从string_data.cpp到StringData.mo的转换更加简单吧。(如果安装路径不一样请做相应的修改)。

@set path=C:\Program Files\GnuWin32\bin;%PATH%;
xgettext 
--force-po -o string_data.pot string_data.cpp
msginit 
-l zh_CN -o StringData.po -i string_data.pot
@set path
=C:\Program Files\Poedit\bin;%PATH%
poedit StringData.po
del string_data.pot
del StringData.po
Poedit打开StringData.po的时候会报错,那是因为文件指明的编码不可用,请在“字符集”中选择UTF-8,另外,在“工程名称以及版本”中写点信息,不要使用默认值就可以了。然后翻译并保存,StringData.mo文件就生成了。
posted on 2008-03-30 02:02 lf426 阅读(4950) 评论(3)  编辑 收藏 引用 所属分类: SDL入门教程跨平台与GNU

FeedBack:
# re: SDL入门教程(十):1、多语言支持,Win32下的GetText[未登录] 2008-06-21 00:26 eye
您辛苦了!
请问一下,xgettext -o a.pot main.cpp这行命令应该放在什么地方呢?
.po文件是怎么生成的?
用gettext生成的吗?
谢谢.  回复  更多评论
  
# re: SDL入门教程(十):1、多语言支持,Win32下的GetText[未登录] 2008-06-21 02:10 lf426
命令可以在“命令与提示符”(Windows)或者在shell(Linux)中直接使用。当然,前提是路径PATH能找到相关命令。在批处理文件(Windows, *.bat)或者shell脚本中可以指定寻找命令的PATH。*.po文件是msginit命令得到的。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理