Chinese Programming

Goldencat ruili@worldnet.att.net

現在越來越多的華人開始用 Linux 了.也越來越多的華人開始寫我門自己的 Linux 下面的應用程式了. 開發我門自己的程式,中文當然是一個必不可少的東西了. 這裡就來說說 Linux 下面的中文程式的開發. 小弟才疏學淺,只是因為看到網路上面這方面的文章不是很多很全面.所以動手寫了這些東西.如果有錯誤,還望大家多多包含,多多指正.

QT

QT 現在被越來越多的人所喜愛.也有了越來越多的人選擇 QT 作為開發Xwindow 下面 GUI 環境的語言. 在 Linux 中,絢麗的 KDE 桌面環境就是用 QT 開發出來的. 現在來看看如何在 QT 下面處理中文.

本文中全部在 QT 3 的環境下運作.如果您用的是比較舊的 QT 版本, 請注意:

下面我們來看一個簡單的 QT 程式:

/* 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();

};


這裡我們簡單的定義了一個基於 QWidget 的 class, 命名為 Chinese 然後我們定義了一個 Label *label 和一個 LineEdit input 並且定義一個 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;
}


在 .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 之類的程式)


/* 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();
}


這個就不多說了.是 chinese 的 main 程式.用來顯示 chinese 的.

然後建立一個如下的 Makefile:

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 $@ $<


通過 make 指令.我們就可以 build 出一個可執行的文件 chinese 執行 ./chinese 可以看到我們的小程式. 我們在程式中那個可以 輸入的地方,隨便打些東西進去(請輸入英文)然後按 enter 鍵. 我們就會看到我們在程式中輸入的那些東西會被顯示在我們的 X 終端模擬中.

但是目前這個程式既不能顯示中文,同樣的也不能接受中文輸入. 不相信的話,您可以嘗試在那個輸入文字的地方打入隨便幾個中文. 例如 "中文測試" 然後按 Enter 鍵.您會發現 X 終端模擬中顯示 的只有 "??" 之類的東西. 原因後面會講到.

首先,我們第一部需要的是讓我門的程式顯示中文.相信大家一定都 知道, QT 中已經幫我們做好了 i18n 了.只要去用就好. 首先我們需要在 chinese.h 的文件中加入:

#include <qtranslator.h> qtranslator.h 是給 QTranslator 用的 include 文件. 然後在 chinese.cpp 中 Chinese::Chinese 需要改成下面的 樣子:

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()));

}


首先,我們定義了 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 這個文件,我們會看到:

# 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 ""

這裡 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 "" 就足夠了.
#: chinese.cpp:15
msgid "Chinese::Input Line:"
msgstr ""


首先我們需要設定我們是用哪種於言編碼來翻譯 po 的.同時 qt 也會 用這種語言編碼來顯示我們所翻譯的信息.所以我們需要改成:

"Content-Type: text/plain; charset=big5\n"
或者:
"Content-Type: text/plain; charset=gb2312\n"


其中 big5 是繁體中文所以用的編碼, gb2312 (GBK) 則是簡體中文所 使用的.

接下來,我就需要更改 msgstr 了:
msgid "Chinese::Input Line:"
msgstr "請輸入中文:"


當然,如果您願意,也可以把
"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 <ruili@worldnet.att.net>\n"


然後存檔,退出.先在我們還需要最後一步,就是把 chinese.po 文件轉成 chinese.qm 的格式給程式來讀.這時侯我們就用到了 一隻叫做 msg2qm2 的程式.
msg2qm2 chinese.po chinese.qm


現在我們就算是完成了. 執行我們的文件 ./chinese 看看,那個 "Input Line:" 已經變成中文的 "請輸入中文:" 了.

下一步,我們來解決在 QT 中輸入中文,然後把中文顯示出來的 問題. 在 QT 中,我們有一個 QTextCodec 可以用. QTextCodec 就是 專門來為我們處理不同編碼的.

為了使用 QTextCodec 我們首先需要在 chinese.cpp 中,把 qtextcodec.h 給 include 進來.
include <qtextcodec.h>
現在來看看我們的 void Chinese::display() 吧.

void Chinese::display()
{
        QString string;
        string=input->text();
        QTextCodec *codec=QTextCodec::codecForName("big5");
        QCString chinese_string=codec->fromUnicode(string);
        cout<<chinese_string<<endl;
}


這裡我們看到,同樣的, 把 input 中我們輸入的 string 扔給 string. 下面就用到了 QTextCodec 了.我們可以把 QTextCodec *codec=QTextCodec::codecForName("big5"); 寫成兩行,這樣比較方便看:

QTextCodec *codec;
codec=QTextCodec::codecForName("big5");

第一行是通過 QTextCodec 建立 codec 第二行呢,告訴 QT 我們的 codec 是用 big5 編碼的. (簡體中文就要用 gb2312)

最後,我們通過 codec->formUnicode(string) 把 Unicode 轉換成我們需要用的 big5 碼. ( QT default 全部是用 Unicode,這也就是為什麼開始的時侯,如果我們沒有通過 QTextCodec 轉碼,看到的中文都是 ?? 了.因為 Unicode 的標準就是不認得的 code 一律用 ?? 來表示) 最後我們定義一個 QCString chinese_string,

QCString chinese_string=codec->fromUnicode(string);

而 chinese_string就是已經被 codec 轉碼過的 big5 code 的了. codec->fromUnicode(string) 這裡是做轉碼的動作.

現在重新 make 所 build 出來的 chinese 就已經具備了最 基本的中文程式的要求了.現在在輸入中文到程式中,您就可以 在 X 終端模擬中看到中文了.

那麼這個程式已經具備了中文處理能力,為什麼還被叫做最基 本的呢中文處理呢?因為我們的程式目前並不會判斷 locale, 而自動設定相應的編碼.現在我們把這個 chinese 的程式作成一個 相對完整的中文程式.

QTextCodec 中還給我們提供了一個讀取當然於言環境的 function 叫做 locale. QTextCodec::locale() 就會返回當前使用者的 locale. 現在讓我們來看看一個相對完整的中文程式:

/* 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();

};

我們這裡加入了一個新的 QString locale, 用來做 locale 的判斷.

/* 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;
}

在 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)

而在 void Chinese::display() 中,我們也做一個自動的判斷,用來判斷使用者 是在哪個中文環境中輸入中文的. 如果是 zh_TW.Big5 我們就把轉碼設定為 big5, 如果是 GB2312 就設定為 gb2312. 而如果是其它的,就忽略不管了.

同時在修改一下我們的 Makefile


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 $@ $<

在 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+ 如何處理中文.

首先我們先來用 GTK 寫一個跟前面 QT 中的那個程式功能完全一樣 的程式.

/* 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;
}

裡面的 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. 現在來看一下我們的 Makefile

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 $@ $<

寫好 Makefile 以後. make 一下,就可以編譯出 GTK+ 版的 chinese 了.執行以後, 我們可以發現,在可以輸入文字的地方,直接輸入中文,就可以在 X 終端模擬中正確 的顯示出來中文. 所以我們需要的,就只有把介面中文化一下就好了. 中文化 gtk+ 程式的時侯,會比 QT 稍稍麻煩些.


#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;
}


首先我們需要 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") 就可以了. 在 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 看看:

# 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 <LL@li.org>\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 ""


跟翻譯 QT 的 po 一樣,我們真正需要動到的只有 charset 和 msgstr "

" 而已.當然,您也可以加入相應的翻譯信息,

PO-Revision-Date: 翻譯時間
"Last-Translator: 翻譯人 翻譯人_E-Mail地址
Language-Team:	  翻譯團隊 翻譯團隊_E-Mail_地址

下面是翻譯好的 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 <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;ruili@worldnet.att.net>\n"
"Language-Team: LANGUAGE &l;ruili@worldnet.att.net>\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 "中文輸入"

存檔以後.我們用:
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 並且放到相應的語言目錄下.

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 $@ $<

C/C++

其是在 C/C++ 的一般程式中,也是可以中文化的. 在 C/C++ 程式中.中文化的步驟跟 GTK+ 己乎是一樣的. 這裡簡單介紹一下:

/* chinese.c */
#include <stdio.h>
main ()
{
        printf(_("This is a test.\n"));
}


現在我們來中文化這隻程式:

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

#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"));
}


然後跟 GTK+ 中的製作 mo 的方法一樣.用 xgettext 製作出 po 文件.
翻譯好以後,用 msgfmt 製作成 mo 文件. 把這個 mo 文件 
copy 到 /usr/locale/你的_locale/LC_MESSAGES下面. 
textdoamin 會去那裡找 mo 文件.現在再重新跑您的程式. 
"This is English" 就變成您改的中文了.