QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 9565|回复: 21

Linux中文编程指南

[复制链接]
发表于 2003-4-30 22:26:29 | 显示全部楼层 |阅读模式
Chinese Programming

现在越来越多的华人开始用 Linux 了.也越来越多的华人开始写我门自己的 Linux 下面的应用程序了. 开发我门自己的程序,中文当然是一个必不可少的东西了. 这里就来说说 Linux 下面的中文程序的开发. 小弟才疏学浅,只是因为看到网络上面这方面的文章不是很多很全面.所以动手写了这些东西.如果有错误,还望大家多多包含,多多指正.
QT
QT 现在被越来越多的人所喜爱.也有了越来越多的人选择 QT 作为开发Xwindow 下面 GUI 环境的语言. 在 Linux 中,绚丽的 KDE 桌面环境就是用 QT 开发出来的. 现在来看看如何在 QT 下面处理中文.
本文中全部在 QT 3 的环境下运作.如果您用的是比较旧的 QT 版本, 请注意:
Tips: 如果您的 Linux 系统中找不到 findtr3 那么请尝试找 findtr2 或者 findtr
Tips: 如果您的 Linux 系统中找不到 msg2qm2 那么请尝试找 msg2qm
Tips: 可以用 which findtr3 或者 whereis msg2qm2 来寻找您的系统
Tips: 如果您希望在自己的计算机中编译本文中的范例, 您需要安装 g++, qt, qt-devel, make 这些 套件.
下面我们来看一个简单的 QT 程序:
[code:1]/* chinese.h */

#include <qapplication.h>
#include <qwidget.h>
#include <qlabel.h>
#include <qlineedit.h>

class Chinese: public QWidget
{
        Q_OBJECT
        public:
                Chinese();
        private:
                QLabel *label;
                QLineEdit *input;
        private slots:
                void display();

};
[/code:1]

这里我们简单的定义了一个基于 QWidget 的 class, 命名为 Chinese 然后我们定义了一个 Label *label 和一个 LineEdit input 并且定义一个 [code:1]void slot display()
/* chinese.cpp */

#include "chinese.moc"
#include <iostream.h>

Chinese::Chinese()
{
        resize(200,100);

        label=new QLabel( "Input Line:", this);
        label->setGeometry(10,10,90,30);

        input=new QLineEdit(this);
        input->setGeometry(10, 40, 180, 30);
        input->setFocus();
        connect(input, SIGNAL(returnPressed()), this, SLOT(display()));

}
void Chinese::display()
{
        QCString string;
        string=input->text();
        cout<<string<<endl;
}

[/code:1]
在 .cpp 文件中. Chinese::Chinese() 中,我们只做了四件最简单的事情 1. 显示一个叫做 Input Line: 的 label 在 10,10 这个位置上. 大小为 90 30. 然后显示一个可以用来输入的 LineEdit 在 10, 40, 大小为 180, 30. 然后告诉程序,当程序打开的时侯,将 focus 用 setFocus() 放在 input ( QLineEdit )上面.也就是说,程序一打开,我们打 keyboard 就可以直接输入到 input 这个 QLineEdit 中. 然后我们告诉程序,当在 input 中接收到 Enter 键的时侯,去呼叫 display() 这个 slot Chinese::display() 中.我们有三个动作. 1. 定义一个 QCString 用作 QT 与 C++ 之间的沟通. string=input-text() 把我们输入在 input 中的 string 抓下来,放到 string 中.然后用 cout 把 string 显示在您的 X 终端模拟中. (rxvt, qterm, xter Konsole 之类的程序)

[code:1]/* main.cpp */

#include <qapplication.h>
#include "chinese.h"

main (int argc, char **argv)
{
        QApplication a(argc, argv);
        Chinese w;
        a.setMainWidget (&w);
        w.show();
        return a.exec();
}
[/code:1]

这个就不多说了.是 chinese 的 main 程序.用来显示 chinese 的.

然后建立一个如下的 Makefile:
[code:1]INCL= -I$(QTDIR)/include -I/usr/include/kde
CFLAGS= -pipe -O2 -fno-strength-reduce
LFLAGS= -L$(QTDIR)/lib -L$(KDEDIR)/lib -L/usr/X11R6/lib
LIBS= -lkdecore -lkdeui -lqt  -lX11 -lXext -ldl
CC=g++
MOC=moc

chinese: chinese.moc chinese.o main.o
        $(CC) $(LFLAGS) -o chinese chinese.o main.o $(LIBS)
chinese.moc: chinese.h
        $(MOC) chinese.h -o chinese.moc
main.o: main.cpp
chinese.o: chinese.cpp

clean:
        rm -f *.o
        rm -f *.bak
        rm -f *.moc
        rm -f chinese

.SUFFIXES: .cpp .h

.cpp.o:
        $(CC) -c $(CFLAGS) $(INCL) -o $@ $<

[/code:1]
通过 make 指令.我们就可以 build 出一个可执行的文件 chinese 执行 ./chinese 可以看到我们的小程序. 我们在程序中那个可以输入的地方,随便打些东西进去(请输入英文)然后按 enter 键. 我们就会看到我们在程序中输入的那些东西会被显示在我们的 X 终端模拟中.

但是目前这个程序既不能显示中文,同样的也不能接受中文输入. 不相信的话,您可以尝试在那个输入文字的地方打入随便几个中文. 例如 "中文测试" 然后按 Enter 键.您会发现 X 终端模拟中显示的只有 "??" 之类的东西. 原因后面会讲到.

首先,我们第一部需要的是让我门的程序显示中文.相信大家一定都知道, QT 中已经帮我们做好了 i18n 了.只要去用就好. 首先我们需要在 chinese.h 的文件中加入:
#include <qtranslator.h> qtranslator.h 是给 QTranslator 用的 include 文件. 然后在 chinese.cpp 中 Chinese::Chinese 需要改成下面的样子:
[code:1]Chinese::Chinese()
{
        resize(200,100);
        QTranslator translator(this);
        translator.load("chinese", ".");
        qApp->installTranslator(&translator);

        label=new QLabel(tr( "Input Line:"), this);
        label->setGeometry(10,10,90,30);

        input=new QLineEdit(this);
        input->setGeometry(10, 40, 180, 30);
        input->setFocus();
        connect(input, SIGNAL(returnPressed()), this, SLOT(display()));

}

[/code:1]
首先,我们定义了 QTranslator translator 在 this. 然后 translator.load 去载入 chinese.qm 这个文件.这里 .qm 这个全名称我们是不需要的. QT default 就会去找 .qm 了. 所以虽然我们的中文对应的翻译文件全名是 chinese.qm ,但是我们这里只需要打入 chinese 就好了.

qApp->installlTranslator(&translator)就会帮我们把翻译成中文的信息, 加载我们的程序中.

同时, label=new QLabel("Input Line:", this) 要改成下面这样 label=new QLabel(tr( "Input Line:"), this) 这里的 tr 是代表信息需要翻译 (translator)的意思. tr 也是给 findtr 来寻找哪里需要信息翻译的一个标志 (flag)

现在用 make 重新编译我们的程序. 编译完后,我们还需要做出 qm 文件来给 QTranslator 用.

首先,我们需要用到 findtr3 这个程序. findtr3 会帮我们找出程序中所有用到 tr 的地方.同时帮我们自动产生 po 文件.
findtr3 chinese.cpp > chinese.po
现在用您最喜爱的文字编辑器(vim, emacs, joe or other)打开 chinese.po 这个文件,我们会看到:
[code:1]# This is a Qt message file in .po format.  Each msgid starts with
# a scope.  This scope should *NOT* be translated - eg
# from French to English, "Foo::Bar" would be translated to "Pub",
# not "Foo::Pub".
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2002-06-22 20时09分00秒 EDT\n"
"PO-Revision-Date: YYYY-MM-DD\n"
"Last-Translator: FULLNAME <EMAIL@ADDRESS>\n"
"Content-Type: text/plain; charset=iso-8859-1\n"

#: chinese.cpp:15
msgid "Chinese::Input Line:"
msgstr ""
[/code:1]

这里 findtr3 帮我们找到,在 chinese.cpp 这个程序中的第 11 行,有一处用到 tr 的地方. 而 tr 后面的内容是 Input Line: 还记得我们的 label 是怎么写的吗? label=new QLabel(tr( "Input Line:"), this); 没错, 这里找到的就是 "Input Line:" 这几个字. 下面我们开始翻译 po 文件了

我们真正需要动到的只有两个地方,就是:
"Content-Type: text/plain; charset=iso-8859-1\n"
和下面的 msgstr "" 就足够了.

[code:1]
#: chinese.cpp:15
msgid "Chinese::Input Line:"
msgstr ""
[/code:1]

首先我们需要设定我们是用哪种于言编码来翻译 po 的.同时 qt 也会用这种语言编码来显示我们所翻译的信息.所以我们需要改成:
[code:1]"Content-Type: text/plain; charset=big5\n"
[/code:1]或者:
[code:1]"Content-Type: text/plain; charset=gb2312\n"[/code:1]


其中 big5 是繁体中文所以用的编码, gb2312 (GBK) 则是简体中文所使用的.

接下来,我就需要更改 msgstr 了:
msgid "Chinese::Input Line:"
msgstr "请输入中文:"

Tips: 注意,如果您是用 gb2312 简体来输入的那么 您需要在 charset 中选择 gb2312. 同样的, big5 繁体输入,就需要选择 charset=big5 您不能用同一种编码的输入法,同时去写两 个 po 文件. 也就是说,不能用 xcin 的繁 体输入,同时去翻译 gb2312 的 po. 也不 能用 chinput 的简体输入来翻译 big5 的 po. 如果您需要同时翻译繁体,简体的 po 那么建议您用您所熟习的语言环境翻译好 一份出来,然后用 big5<=>gb 的转码工具 转出另外一个编码的po文件来.同时记得更 改所转出来的文件中的 charset
当然,如果您愿意,也可以把
"PO-Revision-Date: YYYY-MM-DD\n"
"Last-Translator: FULLNAME <EMAIL@ADDRESS>\n"
加入适当的信息
PO-Revision-Date: 加入您翻译的信息
Last-Translator: 加入您的个人信息以及 e-mail
"PO-Revision-Date: 2002-06-22\n"
"Last-Translator: Goldencat <[email protected]>\n"


然后存盘,退出.先在我们还需要最后一步,就是把 chinese.po 文件转成 chinese.qm 的格式给程序来读.这时侯我们就用到了一只叫做 msg2qm2 的程序.
msg2qm2 chinese.po chinese.qm


现在我们就算是完成了. 执行我们的文件 ./chinese 看看,那个 "Input Line:" 已经变成中文的 "请输入中文:" 了.

下一步,我们来解决在 QT 中输入中文,然后把中文显示出来的问题.
Tips: QT 中的中文输入也很重要.写一个程序,不光是 表面上看起来信息都变成中文的,就叫做中文化 了.举一个最简单的例子:现在有一个中文的 MySql 数据库.我们的 QT 程序需要正确的输入 查询中文的数据.这就需要 QT 可以正确无误的 接受并且处理我们的中文输入才可以.
在 QT 中,我们有一个 QTextCodec 可以用. QTextCodec 就是专门来为我们处理不同编码的.

为了使用 QTextCodec 我们首先需要在 chinese.cpp 中,把 qtextcodec.h 给 include 进来.
include <qtextcodec.h>
现在来看看我们的 void Chinese::display() 吧.
[code:1]
void Chinese::display()
{
        QString string;
        string=input->text();
        QTextCodec *codec=QTextCodec::codecForName("big5");
        QCString chinese_string=codec->fromUnicode(string);
        cout<<chinese_string<<endl;
}

[/code:1]
这里我们看到,同样的, 把 input 中我们输入的 string 扔给 string. 下面就用到了 QTextCodec 了.我们可以把 QTextCodec *codec=QTextCodec::codecForName("big5"); 写成两行,这样比较方便看:
[code:1]
QTextCodec *codec;
codec=QTextCodec::codecForName("big5");
[/code:1]
第一行是通过 QTextCodec 建立 codec 第二行呢,告诉 QT 我们的 codec 是用 big5 编码的. (简体中文就要用 gb2312)

最后,我们通过 codec->formUnicode(string) 把 Unicode 转换成我们需要用的 big5 码. ( QT default 全部是用 Unicode,这也就是为什么开始的时侯,如果我们没有通过 QTextCodec 转码,看到的中文都是 ?? 了.因为 Unicode 的标准就是不认得的 code 一律用 ?? 来表示)最后我们定义一个 QCString chinese_string,
[code:1]
QCString chinese_string=codec->fromUnicode(string);
[/code:1]
而 chinese_string就是已经被 codec 转码过的 big5 code 的了. codec->fromUnicode(string) 这里是做转码的动作.

现在重新 make 所 build 出来的 chinese 就已经具备了最基本的中文程序的要求了.现在在输入中文到程序中,您就可以在 X 终端模拟中看到中文了.

那么这个程序已经具备了中文处理能力,为什么还被叫做最基本的呢中文处理呢?因为我们的程序目前并不会判断 locale, 而自动设定相应的编码.现在我们把这个 chinese 的程序作成一个相对完整的中文程序.

QTextCodec 中还给我们提供了一个读取当然于言环境的 function 叫做 locale. QTextCodec::locale() 就会返回当前使用者的 locale. 现在让我们来看看一个相对完整的中文程序:
Tips: 这个程序中对 locale 名称的定义 是按照 RedHat 7.3 中对中文 locale 的定义为标准的,也就是说: 繁体中文 : zh_TW.Big5 简体中文 : zh_CN.GB2312
[code:1]
/* chinese.h */

#include <qapplication.h>
#include <qwidget.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qtranslator.h>

class Chinese: public QWidget
{
        Q_OBJECT
        public:
                Chinese();
        private:
                QLabel *label;
                QLineEdit *input;
                QString locale;
        private slots:
                void display();

};

[/code:1]
我们这里加入了一个新的 QString locale, 用来做 locale 的判断.
[code:1]
/* chinese.cpp */

#include "chinese.moc"
#include <iostream.h>
#include <qtextcodec.h>

Chinese::Chinese()
{
        resize(200,100);
        QTranslator translator(this);
        locale=QTextCodec::locale();
        translator.load("chinese."+locale, ".");
        qApp->installTranslator(&translator);

        label=new QLabel(tr( "Input Line:"), this);
        label->setGeometry(10,10,90,30);

        input=new QLineEdit(this);
        input->setGeometry(10, 40, 180, 30);
        input->setFocus();
        connect(input, SIGNAL(returnPressed()), this, SLOT(display()));

}

void Chinese::display()
{
        QString string;
        string=input->text();
        QTextCodec *codec;
        if (locale == "zh_TW.Big5")
        codec=QTextCodec::codecForName("big5");
        if (locale == "zh_CN.GB2312")
        codec=QTextCodec::codecForName("gb2312");

        QCString encoded_string=codec->fromUnicode(string);
        cout<<encoded_string<<endl;
}
[/code:1]
在 chinese.cpp 里面, Chinese::Chinese 我们加入 locale 的判断. 从而自动加载相应的 .qm 文件.

locale=QTextCodec::locale() 就把 QTextCodec 返回的值,也就是当前使用的 locale 传给了 locale. (locale 是一个 QString)然后我们用 translator.load("chinese."+locale, "."); 实际上就是说 chinese.+当然 locale (zh_TW.Big5 或者 zh_CN.GB2312)也就是说,最后我们加载的 locale 是 chinese.zh_TW.Big5 或者是 chinese.zh_CN.GB2312. 这样只要对应不同的 locale 环境翻译不同的 .po 然后制作成基于 locale 名称的 qm, QT 就可以帮我们自动判断在哪个locale 要加载那个文件了. (chinese.zh_TW.Big5.qm 或者 chinese.zh_CN.GB2312.qm)
Tips: QT 在载入 .qm 文件的时侯,如果该文件不存在.那么 就会用程序中的语言来显示.所以不需要为了程序是否 可以找到该 .qm 文件而担心.(不用去写 这方面的 error handle)
而在 void Chinese::display() 中,我们也做一个自动的判断,用来判断使用者是在哪个中文环境中输入中文的. 如果是 zh_TW.Big5 我们就把转码设定为 big5, 如果是 GB2312 就设定为 gb2312. 而如果是其它的,就忽略不管了.

同时在修改一下我们的 Makefile
[code:1]
INCL= -I$(QTDIR)/include -I/usr/include/kde
CFLAGS= -pipe -O2 -fno-strength-reduce
LFLAGS= -L$(QTDIR)/lib -L$(KDEDIR)/lib -L/usr/X11R6/lib
LIBS= -lkdecore -lkdeui -lqt  -lX11 -lXext -ldl
CC=g++
MOC=moc

chinese: chinese.moc chinese.o main.o
        $(CC) $(LFLAGS) -o chinese chinese.o main.o $(LIBS)
chinese.moc: chinese.h
        $(MOC) chinese.h -o chinese.moc
main.o: main.cpp
chinese.o: chinese.cpp

po:
        findtr3 chinese.cpp > chinese.zh_TW.Big5.po
        findtr3 chinese.cpp > chinese.zh_CN.GB2312.po
qm:
        msg2qm2 chinese.zh_TW.Big5.po chinese.zh_TW.Big5.qm
        msg2qm2 chinese.zh_CN.GB2312.po chinese.zh_CN.GB2312.qm
clean:
        rm -f *.o
        rm -f *.bak
        rm -f *.moc
        rm -f chinese

.SUFFIXES: .cpp .h

.cpp.o:
        $(CC) -c $(CFLAGS) $(INCL) -o $@ $<

[/code:1]
在 Makefile 中加入 po 和 qm. 这样我们用 make po 就会产生 chinese.zh_TW.Big5.po 和 chinese.zh_CN.GB2312.po 了.把这两个 po 翻译好以后.用 make qm 就会做出 chinese.zh_TW.Big5.qm 和 chinese.zh_CN.GB2312.qm 这两个 qm 文件.现在试试看.在简体或者繁体的 X 终端模拟环境下.我们的程序会自动的显示相应的翻译信息.也会自动接受相应 locale 下面的中文输入法了.
GTK+
在 Linux 下面,还有一个常用的 GUI 环境开发语言.就是GTK+ 了. 大家都知道, gnome 这套桌面环境就是由 GTK+ 开发出来的. 现在我们来看看 GTK+ 如何处理中文.
Tips: 这里我们以 GTK 1.x 为例. (因为 GTK 2.x 目前 还不算是非常的稳定)
Tips: 如果您希望在自己的计算机中编译本文中的范例, 您需要安装 gcc, gtk+, gtk+-devel, make , gettext, 这些套件.
首先我们先来用 GTK 写一个跟前面 QT 中的那个程序功能完全一样的程序.
[code:1]

/* chinese.c */

#include <gtk/gtk.h>
#include <stdio.h>
void display (GtkWidget *widget, GtkWidget * entry)
{
        const gchar *entry_text;
        entry_text=gtk_entry_get_text( GTK_ENTRY (entry));
        printf ("%s\n", entry_text);
}
void destroy(GtkWidget *widget, gpointer *data)
{
        gtk_main_quit();
}
int main(int argc, char *argv[])
{
        GtkWidget *window;
        GtkWidget *vbox;
        GtkWidget *entry;
        GtkWidget *label;

        gtk_init(&argc, &argv);

        window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy),NULL);
        gtk_container_border_width(GTK_CONTAINER(window),10);
        vbox = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (window), vbox);
        gtk_widget_show(vbox);

        label= gtk_label_new("Input Line");
        gtk_box_pack_start(GTK_BOX (vbox), label, TRUE, TRUE, 0);
        gtk_widget_show(label);

        entry= gtk_entry_new();
        gtk_signal_connect(GTK_OBJECT(entry), "activate",
                        GTK_SIGNAL_FUNC(display), entry);

        gtk_box_pack_start(GTK_BOX (vbox), entry, TRUE, TRUE, 0);
        gtk_widget_show(entry);
        gtk_widget_show(window);
        gtk_main();
        return 0;
}
[/code:1]
里面的 void display() 就相当于前面 QT 中的 void Chinese::display() 是用来把我们输入的东西显示在您的 X 终端模拟上面的. 我们首先通过 gchar 来定义一个 entry_text, 然后用 gtk_entry_get_text(GTK_ENTRY())来把我们输入的东西抓出来,放到 entry_text 中去.再用 C 语言中的 printf 把它显示在我们的 X 终端模拟上面.

void destory() 告诉程序,如果收到程序结束的信号,(例如你按了窗口中的那个小 X )那么就代表程序结束. 退出 gtk 的 main loop.

我们的 main 程序中. 定先义了 window, vbox, entry, label 这己个物件.这里面, window, 就是我们看到的主窗口. vbox 是 gtk+ 中一种用来排列对象的东西. entry 是给我们输入用的(QT 中的 QLineEdit)lable 是作为显示一个信息而用的. (QT 中的 QLabel)

gtk_init() 帮我们初始化 gtk.

window=gtk_window_new 就通过 gtk_window_new 把 window 定一为一个新的窗口.也就是我们的主窗口了. GTK_WINDOW_TOPLEVEL 则告诉程序,我们的这个主窗口显示的时侯,是在最上面的. ( TOPLEVEL)

gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy),NULL); 这一行,首先我们连接一个 gtk 的信号, 对象是在 window 上面. 信号的内容为 destroy, 当收到这个信号以后,用 GTK_SIGNAL_FUNC() 去呼叫 destroy() 这个 function . 传给 function 的值为 NULL. 也就是说,当我们的主窗口 window 接收到遗个 destroy 的信号的时侯, 去呼叫 destroy() 结束 gtk 程序. 这里的 destroy 信号就是代表你关闭主窗口的意思. 在 gtk 中,当你关闭了一个窗口, gtk 就会帮你送出一个 destroy 的信号.

gtk_container_border_width(GTK_CONTAINER(window),10); 定义了我们的主窗口的 border(边缘)宽度为 10.

vbox = gtk_vbox_new (FALSE, 0); 定义了一个新的 gtk_vbox 出来.用来排列我们放在主窗口中的对象.

gtk_container_add (GTK_CONTAINER (window), vbox); 就把 vbox 通过 gtk_container_add 加入到我们的主窗口 window 中.

gtk_widget_show(vbox); 通过呼叫 gtk_widget_show 在主窗口中显示我们的 vbox

label= gtk_label_new("Input Line"); 通过 gtk_label_new 定义一个 label,而这个 label 显示的内容为 "Input Line"

gtk_box_pack_start(GTK_BOX (vbox), label, TRUE, TRUE, 0); 是通过 gtk_box_pack_start 把 label 放到我们前面生成的 vbox 中.

gtk_widget_show(label); 再次利用 gtk_widget_show, 这次是在 vbox 中显示我们新加入的 label.

entry = gtk_entry_new(); 用 gtk_entry_new 定义了一个 entry. 这里就是指产生一个可以接受文字输入的对象,就好像 QT 中的 LineEdit 一样.

gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(display), entry); 这里,我们连接一个 gtk 信号,信号作用于 entry,也就是我们前面产生的那个文字输入对象.当我程序接收到 "activate" 这个信号的时侯,就去呼叫 display() 这个 function, 同时把 entry 传给 display(). 这里的 activate 信号,就是当我们按下键盘的 enter 键时所产生的. gtk 当适用者按下 enter 键的时侯,就会送初一个 activate 的信号.所以当我们输入一些文字以后,按下 enter 键,这些文字就会被送到 display() 去处理了.

gtk_box_pack_start(GTK_BOX (vbox), entry, TRUE, TRUE, 0); 再次用 gtk_box_pack_start 把对象 entry 加入到 vbox 中.

gtk_widget_show(entry); 显示 vbox 中的对象 entry
gtk_widget_show(window);
gtk_main();
最后我们显示我们的主窗口.并且进入 gtk 的 main loop.
Tips: 这里我们利用 gtk_widget_show() 先显示了 window 中的 vbox, label, entry, 最后才去显示主窗口 window 是因为这样的话,当主窗口 window 显示出来的时侯. vbox, label, entry 已经比 window 先显示好了.这样我们看到的 就是一个整体的 window. 有主窗口,有 label 和 entry. vbox, label, entry 都是在主窗口显示以前就已经画好的了. 所以主窗口一出来,不用等待 label, entry 的出现. 如果先去显示 window, 再去显示 vbox, label, entry, 那么在比较慢的计算机上面(或者你的程序比较复杂的情况下) 你将会先看到一个空白的主窗口出现,然后再慢慢的画出 label 和 entry 来.
现在来看一下我们的 Makefile
[code:1]
INCL= -I/usr/lib/glib/include/ -I/usr/include/gtk-1.2/gdk/ -I/usr/include/gtk-1.2/ \
         -I/usr/include/glib-1.2/
LIBS= -L/usr/X11R6/lib
LFLAGS= -lglib -lgdk -lgtk -lX11 -lXext -lm
CC=gcc

chinese: chinese.c
        $(CC) $(INCL) -o chinese chinese.c $(LIBS) $(LFLAGS)
clean:
        rm -f chinese
        rm -f *.bak

.SUFFIXES: .c

.c.o:
        $(CC) -c $(CFLAGS) $(INCL) -o $@ $<
[/code:1]
写好 Makefile 以后. make 一下,就可以编译出 GTK+ 版的 chinese 了.执行以后, 我们可以发现,在可以输入文字的地方,直接输入中文,就可以在 X 终端模拟中正确的显示出来中文. 所以我们需要的,就只有把接口中文化一下就好了. 中文化 gtk+ 程序的时侯,会比 QT 稍稍麻烦些.
[code:1]
#include <gtk/gtk.h>
#include <locale.h>
#include <libintl.h>
#include <stdio.h>
#define PACKAGE "chinese"
#define LOCALEDIR "/home/goldencat/program/gtk/chinese/final"
#define _(STRING) gettext(STRING)
void display(GtkWidget *widget, GtkWidget * entry)
{
        const gchar *entry_text;
        entry_text=gtk_entry_get_text( GTK_ENTRY (entry));
        printf ("%s\n", entry_text);
}
void destroy(GtkWidget *widget, gpointer *data)
{
        gtk_main_quit();
}
int main(int argc, char *argv[])
{
        GtkWidget *window;
        GtkWidget *vbox;
        GtkWidget *entry;
        GtkWidget *label;

        gtk_set_locale();
        bindtextdomain (PACKAGE, LOCALEDIR);
        textdomain (PACKAGE);

        gtk_init(&argc, &argv);

        window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy),NULL);
        gtk_container_border_width(GTK_CONTAINER(window),10);
        vbox = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (window), vbox);
        gtk_widget_show(vbox);

        label= gtk_label_new(_("Input Line"));
        gtk_box_pack_start(GTK_BOX (vbox), label, TRUE, TRUE, 0);
        gtk_widget_show(label);

        entry= gtk_entry_new();
        gtk_signal_connect(GTK_OBJECT(entry), "activate",
                        GTK_SIGNAL_FUNC(display), entry);

        gtk_box_pack_start(GTK_BOX (vbox), entry, TRUE, TRUE, 0);
        gtk_widget_show(entry);
        gtk_widget_show(window);
        gtk_main();
        return 0;
}
[/code:1]

首先我们需要 locale.h 和 libintl.h 然后还要定义 PACKAGE, LOCALEDIR 这里的 PACKAGE 就是我们翻译后的 mo 文件的名称. 这里把它定义为 chinese 因为我们的程序就叫做 chinese, GTK+ 会在固定的地方寻找 mo 文件.在 RedHat 7.3 中,会在 /usr/share/locale/你的locale/LC_MESSAGES 下面寻找 mo 文件. 对于中文来说,也就是 /usr/share/locale/ 下面的 zh_TW zh_TW.Big5 zh_CN zh_CN.GB2312 这几个目录中的 LC_MESSAGES 下面.去寻找 mo 文件.

这里我们去定义 LOCALEDIR 就是告诉 gtk+ 我们希望 gtk+ 到哪里去寻找 mo 文件. /home/goldencat/program/gtk/chinese/final 就是 chinese.c 的位置.也就是我们的当前工作目录.这样 gtk+ 就不会再去 /usr/share/locale/ 下面找相应语言环境中的 LC_MESSAGES 中寻找 mo 文件了. 而是在您 LOCALEDIR 中定义的位置.(配合 bindtextdomain 使用)

而后面的 #define _(STRING) gettext(STRING) 只是说把 gettext(STRING)换成 _(STRING) gettext 就好像前面 QT 中的那个 tr 一样.在产生 po 文件的时侯,就是根据 gettext 来决定哪些信息是需要翻译的.只不过每个需要翻译的信息都把 ("English String") 改成 (gettext("English String")) 未免有些麻烦.所以 GTK+ 中,大家都把 gettext(STRING) 换成 _(STRING), 这样我们就只需要 (_("English String") 就可以了.
Tips: 如果您在您的计算机中编译这些东西,请记得把 #define LOCALEDIR "/home/goldencat/program/gtk/chinese/final" 改换成您自己的工作目录
在 gtk_init() 之前,我们需要告诉 gtk 我们有翻译的信息.所以要有下面三个动作:
gtk_set_locale();
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
get_set_locale() 告诉 gtk 去找我们现在用的 locale (QT 中的 TextCodec::locale())这样 gtk+ 就会自动判断出当前 locale 的环境,然后去相应的环境下面找 mo 文件.

bindtextdomain() 则是告诉 gtk 去哪里找 mo 文件. 这里我们要它去我们定义的 LOCALEDIR 下面找 PACKAGE (chinese)

textdomain() 就是载入翻译的信息啦.

最后就是把 label= gtk_label_new("Input Line"); 改成 label= gtk_label_new(_("Input Line")); 这样 xgettext 就可以找到需要翻译的信息了.

现在我们就算是完成了程序这边对中文化的支持了. gtk+ 需要用 xgettext 来产生 po 文件的.所以我们用:

xgettext -k_ chinese.c -o chinese.po

这里的 -k_ 就是说 keywork 是 _ ,也就是说 xgettext 会去找程序中的 _(STRING) 出来. 现在打开 chinese.po 看看:
[code:1]
# 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"
"POT-Creation-Date: 2002-06-23 17:44-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: chinese.c:38
msgid "Input Line"
msgstr ""

[/code:1]
跟翻译 QT 的 po 一样,我们真正需要动到的只有 charset 和 msgstr "

" 而已.当然,您也可以加入相应的翻译信息,

PO-Revision-Date: 翻译时间
"Last-Translator: 翻译人 翻译人_E-Mail地址
Language-Team:          翻译团队 翻译团队_E-Mail_地址

下面是翻译好的 po
[code:1]
# 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@ADDRES>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2002-06-23 17:44-0400\n"
"PO-Revision-Date: 2002-06-23 14:45-0400\n"
"Last-Translator: Goldencat &l;[email protected]>\n"
"Language-Team: LANGUAGE &l;[email protected]>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=Big5\n"
"Content-Transfer-Encoding: 8bit\n"

#: chinese.c:38
msgid "Input Line"
msgstr "中文输入"
[/code:1]

存档以后.我们用:
msgfmt -o chinese.mo chinese.po
生成 mo 文件.

跟 QT 不同的是, GTK+ 不是通过判断 mo 名子来载入不同的的 mo 的. 在 GTK+ 下面.任何 locale (语言环境)的 mo 名子都叫做 chinese.mo 那么 GTK+ 是如何分辨不同的 locale 需要加载相对语言的 mo 呢? GTK+ 是通过把不同的 mo 放在不同的目录下,然后通过判断目录名称来找到相应的 mo 文件的. 所以存放 GTK+ mo 文件的目录名,需要跟相应的 locale 一致. GTK+ 就会在这个目录下的 LC_MESSAGES 中找到所需要的 mo 文件了. 所以我们首先需要在当前目录下做出相应的存放 mo 文件的目录.
$mkdir zh_TW
$mkdir zh_TW/LC_MESSAGES
$cp -r zh_TW zh_TW.Big5
$cp -r zh_TW zh_CN
$cp -r zh_TW zh_CN.GB2312


这样我们就有了中文所需要的四个最基本的目录了. 现在我们把 chinese.mo 放到相应的目录下面

$cp chinese.mo zh_TW/LC_MESSAGES
$cp chinese.mo zh_TW.Big5/LC_MESSAGES

然后在做一份 GB2312 的 mo 放在 zh_CN 和 zh_CN.GB2312

实际上,在 RedHat7.3 中, export LANG=zh_TW.Big5 情况下(LC_MESSAGES=zh_TW.Big5)GTK+ 会在 zh_TW/LC_MESSAGES 下面寻找 mo 文件. 而 export LANG=zh_CN.GB2312 的情况下(LC_MESSAGES=zh_CN.GB2312)GTK+ 则是在 zh_CN.GB2312/ LC_MESSAGES 下面寻找 mo 文件.

现在试试在不同的 locale 运行一下 ./chinese 您会看到, 程序已经被中文化了.那个 Input Line 已经换成中文的 "中文输入" 了.

最后,我们再来改一下我们的 Makefile, 帮助我们产生 po, mo 并且放到相应的语言目录下.
[code:1]
INCL= -I/usr/lib/glib/include/ -I/usr/include/gtk-1.2/gdk/ -I/usr/include/gtk-1.2/ \
         -I/usr/include/glib-1.2/
LIBS= -L/usr/X11R6/lib
LFLAGS= -lglib -lgdk -lgtk -lX11 -lXext -lm
CC=gcc

chinese: chinese.c
        $(CC) $(INCL) -o chinese chinese.c $(LIBS) $(LFLAGS)
clean:
        rm -f chinese
        rm -f *.bak
po:
        xgettext -k_ chinese.c -o chinese_big5.po
        xgettext -k_ chinese.c -o chinese_gb2312.po
mo:
        msgfmt -o chinese_big5.mo chinese_big5.po
        msgfmt -o chinese_gb2312.mo chinese_gb2312.po

mo_install:
        cp chinese_big5.mo zh_TW/LC_MESSAGES
        cp chinese_big5.mo zh_TW.Big5/LC_MESSAGES
        cp chinese_gb2312.mo zh_CN/LC_MESSAGES
        cp chinese_gb2312.mo zh_CN.GB2312/LC_MESSAGES

.SUFFIXES: .c

.c.o:
        $(CC) -c $(CFLAGS) $(INCL) -o $@ $<
[/code:1]
C/C++

其是在 C/C++ 的一般程序中,也是可以中文化的. 在 C/C++ 程序中.中文化的步骤跟 GTK+ 己乎是一样的. 这里简单介绍一下:
[code:1]
/* chinese.c */
#include <stdio.h>
main ()
{
        printf(_("This is a test.\n"));
}
[/code:1]

现在我们来中文化这只程序:


首先我们需要 libintl.h 和 locale.h 再就是
#define PACKAGE "chinese" #define _(STRING) gettext(STRING)
然后用 setlocale() 和 textdomain() 来完成中文化

[code:1]
#include <stdio.h>
#include <libintl.h>
#include <locale.h>
#define _(STRING) gettext(STRING)
#define PACKAGE "chinese"
main ()
{
        setlocale(LC_MESSAGES, "");
        textdomain(PACKAGE);
        printf(_("This is English.\n"));
}

[/code:1]
然后跟 GTK+ 中的制作 mo 的方法一样.用 xgettext 制作出 po 文件.
翻译好以后,用 msgfmt 制作成 mo 文件. 把这个 mo 文件
copy 到 /usr/locale/你的_locale/LC_MESSAGES下面.
textdoamin 会去那里找 mo 文件.现在再重新跑您的程序.
"This is English" 就变成您改的中文了.

转载自 LIY学园 (liy.slat.org)
发表于 2003-4-30 23:14:33 | 显示全部楼层
佩服,写了这么多!
回复

使用道具 举报

发表于 2003-4-30 23:16:29 | 显示全部楼层
GTK2程序好象需要将中文转成utf-8才能正确显示,否则直接将GB编码当作utf8对待,导致显示不了
回复

使用道具 举报

发表于 2003-5-1 12:06:32 | 显示全部楼层
好好好好好
回复

使用道具 举报

发表于 2003-5-12 15:53:16 | 显示全部楼层
为什么我在编译原始QT代码的时候出错了(使用了上述的makefile)
出错信息如下:
Makefile:9: *** missing separator. Stop
回复

使用道具 举报

发表于 2003-5-12 19:36:40 | 显示全部楼层
liy.slat.org
为什么去不了呀?
thx
回复

使用道具 举报

发表于 2003-5-20 20:27:47 | 显示全部楼层
佩服,佩服!
回复

使用道具 举报

发表于 2003-9-3 13:42:03 | 显示全部楼层
有人依照文章中写的去做了吗?
我用的是redhat8.0,我还没有加中文那,执行 make文件,就有好多错误,说没有类是qapplication的头文件,我想是不是linux的版本不太一样,我想知道QTDIR  和 KDEDIR是什么意思
能否具体解释一下make文件各行的意思呀,不是特别明白,
还有为什么不能用qmake -project  ,qmake ,make 来编译那
回复

使用道具 举报

发表于 2003-9-7 21:12:05 | 显示全部楼层
收藏先
回复

使用道具 举报

发表于 2003-9-8 08:23:09 | 显示全部楼层
什么时候能打包,提供下载
回复

使用道具 举报

发表于 2003-9-8 15:19:58 | 显示全部楼层
我用的是redhat8.0
好像没有msg2qm2命令,msg2qm也没有
回复

使用道具 举报

发表于 2003-9-23 23:48:46 | 显示全部楼层
厉害!厉害!
回复

使用道具 举报

发表于 2003-9-24 12:04:39 | 显示全部楼层
up
回复

使用道具 举报

发表于 2004-6-14 14:47:32 | 显示全部楼层
这些对我来说太深奥了
回复

使用道具 举报

发表于 2004-6-15 12:45:36 | 显示全部楼层
支持,,虽然我现在不用C++..但支持..
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-6 21:43 , Processed in 0.081298 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表