Tag: 开发

小宁和你聊编程之四: 语句篇

Posted by – September 26, 2017

上一篇《小宁和你聊编程之三: 类型篇》,我们主要和大家聊了聊什么是变量的类型。大家还有印象么?什么是类型,我们将它应用在什么地方?我们又是如何在编程实践中使用它的呢? 你有什么想法需要和我交流么?你遇到了什么困难么?有什么我可以帮助你的?

让我们再次回顾下类型的内容,一起来复习一下:
什么是类型:

类型就是变量的一个分类(归类)。

比如我们把红富士、黄元帅都叫做苹果, 我们把海南香蕉、进口香蕉都要叫香蕉.
我们把和田大枣、西安洋县枣都叫大枣. 或者我们把男孩子,女孩子都叫孩子。
这是对TA们的一个分类。在编程中这个分类就是其类型。
我们也举例进行了说明。比如:

int iFive = 5; //int(整型)变量iFive的值为5.
string strJ="jin"; //字符串strJ的值为"jin"

我们前三节说明了编程的本质 、编程的核心概念: 变量编程的核心概念:类型 . 那么变量和类型他们是如何组合起来发挥作用的呢?
我们今天来看编程的另一个核心概念: 语句

那么什么是语句呢?其它我们早就见过它了。
1.什么是语句?


简单来说, 语句就是表达完整意思的一句话。

这里面有两个要点:
.表达完整意思: 就像你说话得说完整,要不然大家会听不明白。
比如你说,我现在要… 如果你这么说,大家就在等着你说完,要不不知道你要做什么。编程也是一样。
生活中我们是为了交流,沟通,编写代码时,则是为了清楚地告诉计算机我们想让它做什么。

.一句话: 这句话通常以;(绝大多数情况) 或者右大括号}, 或者>, 或者以) 结束.
现实中,我们说话的时候,说完了会有一个句号,或者一个省略号,或者一到多个!, 或者?号等等。
总之大家知道这句话结束了。那么在编程中,好多程序都有语句结束标志。
比如C++中,绝大多数情况下以;或者}为语句结束的标志。

2.语句的应用?(举个例子)


那么语句在编程中是如何使用的呢?其实它和变量、类型一样,随处可见,应用及其广泛.
基本可以说是变量和类型组成了语句。比如:

int i23=23; //语句,以int开始, 以;结束.
int iSix=6; //一条语句,以int始, 以;终.

再如:

string strJ="j"; //语句:以string始,以;终
string strJor="Mi"; //语句:以string开始,以;终

又如:

char cCh='c'; //语句:以char始,以;终.
char * pHi="Hi"; //语句:以chra始,以;终.

最后来个空语句:

; //以;结束的语句。什么也不做.

3.语句的实践?


我们上面和大家举例说明了语句在程序中的应用, 那现在来实践一下.写段代码来使用它们。
请打开VS2013或者打开http://cpp.sh来写上如下代码:

#include <iostream> //一条语句, 以>结束
#include <string> //一条语句
using namespace std; //一条语句
int main()  //以)结束
{
    int i23 = 23; // 一条完整的语句
    int iSix = 6; // 

    float fFivePotFive = 5.5; // 一条完整的语句。告诉计算机保存变量的值为5.5
    float fNine = 9;        // 

    string strJin = "jin"; // 告诉计算机, 在strJin中保存"jin"这个字符串。
    string strJordan = "Michael Jordan"; //

    char cChar = 'c';  //告诉计算机在cChar中保存字符'c'
    char * pHello = "Hello"; //

    std::cout << i23 << "," << iSix << std::endl; 
    //一条语句:打印出 i23的值, 一个,号, 和iSix的值,并且换行
    std::cout << fFivePotFive << "," << fNine << std::endl;
    //一条语句:打印出strJin的值,一个逗号, strJordan的值,然后换行
    std::cout << strJin << "," << strJordan << std::endl;
    std::cout << cChar << "," << pHello << std::endl;
    return 0;
}

如果使用的是VS2013, 那么我们按F7, 如果没有错误的话, 就会编译生成我们的程序,并且会有下面的输出:
vs2013语句: 程序输出

如果使用http://cpp.sh也会产生下面的输出.
cpp.sh语句: 程序输出

这样我们就定义了很多条“语句”, 程序就是由很多条语句组成完整的意思.就像我们说了好多句话,表达了我们的想法。然后我们看到了程序运行后输出的结果.

恭喜你, 今天你和我一起学习了又一基本概念: 语句
编程核心概念之四: 语句 
语句:

语句是表达完整意思的一句话。

语句的应用
语句的实践

怎么样?有没有理解语句的概念?你是如何理解C++中语句的概念的?能不能举几个例子?

请大家多多动手,让编程也成为你的一种爱好和生产力。
戳右上角 小宁静致远 或扫码关注我, 欢迎、感激传播! ^_^
期待和你多交流、共成长。谢谢!
扫描关注小宁静致远

鼓励我的创作?
您的鼓励我的动力!

视觉计算开发人员社区,提高游戏开发效率

Posted by – May 28, 2010

Refers to:
http://software.intel.com/zh-cn/articles/TickerTape/?cid=sw:prccsdn1096
http://freeimage.sourceforge.net/

What is FreeImage?

FreeImage is an Open Source library project for developers who would like to support popular graphics image formats like PNG, BMP, JPEG, TIFF and others as needed by today’s multimedia applications. FreeImage is easy to use, fast, multithreading safe, compatible with all 32-bit versions of Windows, and cross-platform (works both with Linux and Mac OS X).

Thanks to it’s ANSI C interface, FreeImage is usable in many languages including C, C++, VB, C#, Delphi, Java and also in common scripting languages such as Perl, Python, PHP, TCL or Ruby.

The library comes in two versions: a binary DLL distribution that can be linked against any WIN32 C/C++ compiler and a source distribution. Workspace files for Microsoft VS.Net 2003 and VS.Net 2005 are provided, as well as makefiles for Linux and Mac OS X.

From january 2000 to july 2002, FreeImage was designed and mainly developed by Floris van den Berg. FreeImage is now maintained by Hervé Drolon.

Qt事件

Posted by – May 18, 2010

Qt事件:

Refers to:
http://www.w3china.org/blog/more.asp?name=oceanblue&id=41941

Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发. Qt事件的类型很多, 常见的qt的事件如下:
键盘事件: 按键按下和松开.
鼠标事件: 鼠标移动,鼠标按键的按下和松开.
拖放事件: 用鼠标进行拖放.
滚轮事件: 鼠标滚轮滚动.
绘屏事件: 重绘屏幕的某些部分.
定时事件: 定时器到时.
焦点事件: 键盘焦点移动.
进入和离开事件: 鼠标移入widget之内,或是移出.
移动事件: widget的位置改变.
大小改变事件: widget的大小改变.
显示和隐藏事件: widget显示和隐藏.
窗口事件: 窗口是否为当前窗口.

还有一些非常见的qt事件,比如socket事件,剪贴板事件,字体改变,布局改变等等.

Qt 的事件和Qt中的signal不一样. 后者通常用来”使用”widget, 而前者用来”实现” widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的. 但是如果我们要重载一个按钮的时候,我们就要面对event了. 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.

2. 事件产生和处理流程

2.1 事件的产生
事件的两种来源:

一种是系统产生的;通常是window system把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中. Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理.

一种是由Qt应用程序程序自身产生的.程序产生事件有两种方式, 一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理. 另一种方式是调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是这种方式.

// 自定义事件的时候讲述: 需要注意的时, 这两个函数的使用方法不大一样, 一个是new, 一个是….

2.2 事件的调度
两种调度方式,一种是同步的, 一种是异步.

Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:

while ( !app_exit_loop )
{
while( !postedEvents ) { processPostedEvents() }
while( !qwsEvnts ){ qwsProcessEvents();   }
while( !postedEvents ) { processPostedEvents() }

}

先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.

调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.

2.3 事件的派发和处理
首先说明Qt中事件过滤器的概念. 事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便. 通过它, 可以让一个对象侦听拦截另外一个对象的事件. 事件过滤器是这样实现的: 在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObjec (qobjA)给另一个QObject (qobjB)安装了事件过滤器之后, qobjB会把qobjA的指针保存在eventFilters中. 在qobjB处理事件之前,会先去检查eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数. 一个对象可以给多个对象安装过滤器. 同样, 一个对象能同时被安装多个过滤器, 在事件到达之后, 这些过滤器以安装次序的反序被调用. 事件过滤器函数( eventFilter() ) 返回值是bool型, 如果返回true, 则表示该事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理; 如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理.

Qt中,事件的派发是从 QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用这些事件过滤器. 接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并). 之后,事件被送到reciver::event() 处理.

同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之. 接下来,根据QEvent的类型, 调用相应的特定事件处理函数. 一些常见的事件都有特定事件处理函数, 比如:mousePressEvent(), focusOutEvent(), resizeEvent(), paintEvent(), resizeEvent()等等. 在实际应用中, 经常需要重载这些特定事件处理函数在处理事件. 但对于那些不常见的事件, 是没有相对应的特定事件处理函数的. 如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器.

事件派发和处理的流程图如下:

2.4 事件的转发

对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口. 如图所示, 事件最先发送给QCheckBox, 如果QCheckBox没有处理, 那么由QGroupBox接着处理, 如果QGroupBox没有处理, 再送到QDialog, 因为QDialog已经是最顶层widget, 所以如果QDialog不处理, QEvent将停止转发.       如何判断一个事件是否被处理了呢? Qt中和事件相关的函数通过两种方式相互通信. QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递. 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识. 这种方式只用于event() 函数和特定事件处理函数之间的沟通. 而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件.
3. 实际运用

根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下:

3.1 重载特定事件处理函数.
最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下:

void imageView::keyPressEvent(QKeyEvent * event)
{
switch (event->key()) {
case Key_Plus:
zoomIn();
break;
case Key_Minus:
zoomOut();
break;
case Key_Left:
// …
default:
QWidget::keyPressEvent(event);
}
}

3.2重载event()函数.
通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件.

下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )

bool CodeEditor::event(QEvent * event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = (QKeyEvent *) event;
if (keyEvent->key() == Key_Tab)
{
insertAtCurrentPosition(‘\t’);
return true;
}
}
return QWidget::event(event);
}

3.3 在Qt对象上安装事件过滤器.

安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)
首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.
然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.
用这种方法改写上面的例子: (假设我们将CodeEditor 放在MainWidget中)

MainWidget::MainWidget()
{
CodeEditor * ce = new CodeEditor( this, “code editor”);
ce->installEventFilter( this );
}

bool MainWidget::eventFilter( QOject * target , QEvent * event )
{
if( target == ce )
{
if( event->type() == QEvent::KeyPress )
{
QKeyEvent *ke = (QKeyEvent *) event;
if( ke->key() == Key_Tab )
{
ce->insertAtCurrentPosition(‘\t’);
return true;
}
}
}
return false;
}

3.4 给QAppliction对象安装事件过滤器.
一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个 eventFilter(). 在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉. ( 在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)

3.5 继承QApplication类,并重载notify()函数.

Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件过滤器, 相比之下, notify()函数只有一个.

Calling Qt Functions From Unix Signal Handlers

Posted by – May 18, 2010

Home · All Classes · All Functions · Overviews

Calling Qt Functions From Unix Signal Handlers

Refers to:
http://doc.qt.nokia.com/4.6/unix-signals.html
http://www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01
http://doc.qt.nokia.com/4.6/qsocketnotifier.html
http://doc.qt.nokia.com/4.6/platform-specific.html
You can’t call Qt functions from Unix signal handlers. The standard POSIX rule applies: You can only call async-signal-safe functions from signal handlers. See Signal Actions for the complete list of functions you can call from Unix signal handlers.

But don’t despair, there is a way to use Unix signal handlers with Qt. The strategy is to have your Unix signal handler do something that will eventually cause a Qt signal to be emitted, and then you simply return from your Unix signal handler. Back in your Qt program, that Qt signal gets emitted and then received by your Qt slot function, where you can safely do whatever Qt stuff you weren’t allowed to do in the Unix signal handler.

One simple way to make this happen is to declare a socket pair in your class for each Unix signal you want to handle. The socket pairs are declared as static data members. You also create a QSocketNotifier to monitor the read end of each socket pair, declare your Unix signal handlers to be static class methods, and declare a slot function corresponding to each of your Unix signal handlers. In this example, we intend to handle both the SIGHUP and SIGTERM signals. Note: You should read the socketpair(2) and the sigaction(2) man pages before plowing through the following code snippets.

[cpp]
class MyDaemon : public QObject
{
Q_OBJECT

public:
MyDaemon(QObject *parent = 0, const char *name = 0);
~MyDaemon();

// Unix signal handlers.
static void hupSignalHandler(int unused);
static void termSignalHandler(int unused);

public slots:
// Qt signal handlers.
void handleSigHup();
void handleSigTerm();

private:
static int sighupFd[2];
static int sigtermFd[2];

QSocketNotifier *snHup;
QSocketNotifier *snTerm;
};
[/cpp]

In the MyDaemon constructor, use the socketpair(2) function to initialize each file descriptor pair, and then create the QSocketNotifier to monitor the read end of each pair. The activated() signal of each QSocketNotifier is connected to the appropriate slot function, which effectively converts the Unix signal to the QSocketNotifier::activated() signal.

[cpp]
MyDaemon::MyDaemon(QObject *parent, const char *name)
: QObject(parent,name)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighupFd))
qFatal("Couldn’t create HUP socketpair");

if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd))
qFatal("Couldn’t create TERM socketpair");
snHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this);
connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
snTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this);
connect(snTerm, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));


}
[/cpp]

Somewhere else in your startup code, you install your Unix signal handlers with sigaction(2)

[cpp]
static int setup_unix_signal_handlers()
{
struct sigaction hup, term;

hup.sa_handler = MyDaemon::hupSignalHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;

if (sigaction(SIGHUP, &hup, 0) > 0)
return 1;

term.sa_handler = MyDaemon::termSignalHandler;
sigemptyset(&term.sa_mask);
term.sa_flags |= SA_RESTART;

if (sigaction(SIGTERM, &term, 0) > 0)
return 2;

return 0;
}
[/cpp]

In your Unix signal handlers, you write a byte to the write end of a socket pair and return. This will cause the corresponding QSocketNotifier to emit its activated() signal, which will in turn cause the appropriate Qt slot function to run.

[cpp]
void MyDaemon::hupSignalHandler(int)
{
char a = 1;
::write(sighupFd[0], &a, sizeof(a));
}

void MyDaemon::termSignalHandler(int)
{
char a = 1;
::write(sigtermFd[0], &a, sizeof(a));
}
[/cpp]

In the slot functions connected to the QSocketNotifier::activated() signals, you read the byte. Now you are safely back in Qt with your signal, and you can do all the Qt stuff you weren’tr allowed to do in the Unix signal handler.

[cpp]
void MyDaemon::handleSigTerm()
{
snTerm->setEnabled(false);
char tmp;
::read(sigtermFd[1], &tmp, sizeof(tmp));

// do Qt stuff

snTerm->setEnabled(true);
}

void MyDaemon::handleSigHup()
{
snHup->setEnabled(false);
char tmp;
::read(sighupFd[1], &tmp, sizeof(tmp));

// do Qt stuff

snHup->setEnabled(true);
}
[/cpp]

Linux系统调用列表

Posted by – April 16, 2010

Linux系统调用列表:

Refers to:
http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html#2

2002 年 3 月 01 日

本文列出了大部分常见的Linux系统调用,并附有简要中文说明。

以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数。这可能是你在互联网上所能看到的唯一一篇中文注释的 Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的。

按照惯例,这个列表以man pages第2节,即系统调用节为蓝本。按照笔者的理解,对其作了大致的分类,同时也作了一些小小的修改,删去了几个仅供内核使用,不允许用户调用的系统 调用,对个别本人稍觉不妥的地方作了一些小的修改,并对所有列出的系统调用附上简要注释。

其中有一些函数的作用完全相同,只是参数不同。(可能很多熟悉C++朋友马上就能联想起函数重载,但是别忘了Linux核心是用C语言写的,所以只能取成 不同的函数名)。还有一些函数已经过时,被新的更好的函数所代替了(gcc在链接这些函数时会发出警告),但因为兼容的原因还保留着,这些函数我会在前面 标上“*”号以示区别。

一、进程控制:

fork 创建一个新进程
clone 按指定条件创建子 进程
execve 运行可执行文件
exit 中 止进程
_exit 立即中止当前进程
getdtablesize 进 程所能打开的最大文件数
getpgid 获取指定进程组标识号
setpgid 设 置指定进程组标志号
getpgrp 获取当前进程组标识号
setpgrp 设 置当前进程组标志号
getpid 获取进程标识号
getppid 获 取父进程标识号
getpriority 获取调度优先级
setpriority 设 置调度优先级
modify_ldt 读写进程的本地描述表
nanosleep 使 进程睡眠指定的时间
nice 改变分时进程的优先级
pause 挂 起进程,等待信号
personality 设置进程运行域
prctl 对 进程进行特定操作
ptrace 进程跟踪
sched_get_priority_max 取 得静态优先级的上限
sched_get_priority_min 取得静态优先级的下限
sched_getparam 取 得进程的调度参数
sched_getscheduler 取得指定进程的调度策略
sched_rr_get_interval 取 得按RR算法调度的实时进程的时间片长度
sched_setparam 设置进程的调度参数
sched_setscheduler 设 置指定进程的调度策略和参数
sched_yield 进程主动让出处理器,并将自己等候调度队列队 尾
vfork 创建一个子进程,以供执行新程序,常与execve等同时使用
wait 等 待子进程终止
wait3 参见wait
waitpid 等 待指定子进程终止
wait4 参见waitpid
capget 获 取进程权限
capset 设置进程权限
getsid 获 取会晤标识号
setsid 设置会晤标识号

回页首

二、文件系统控制

1、文件读写操作

fcntl 文件控制
open 打开文件
creat 创 建新文件
close 关闭文件描述字
read 读 文件
write 写文件
readv 从文件 读入数据到缓冲数组中
writev 将缓冲数组里的数据写入文件
pread 对 文件随机读
pwrite 对文件随机写
lseek 移 动文件指针
_llseek 在64位地址空间里移动文件指针
dup 复 制已打开的文件描述字
dup2 按指定条件复制文件描述字
flock 文 件加/解锁
poll I/O多路转换
truncate 截 断文件
ftruncate 参见truncate
umask 设 置文件权限掩码
fsync 把文件在内存中的部分写回磁盘

2、文件系统操作

access 确定文件的可存取性
chdir 改变当前 工作目录
fchdir 参见chdir
chmod 改 变文件方式
fchmod 参见chmod
chown 改 变文件的属主或用户组
fchown 参见chown
lchown 参 见chown
chroot 改变根目录
stat 取 文件状态信息
lstat 参见stat
fstat 参 见stat
statfs 取文件系统信息
fstatfs 参 见statfs
readdir 读取目录项
getdents 读 取目录项
mkdir 创建目录
mknod 创 建索引节点
rmdir 删除目录
rename 文 件改名
link 创建链接
symlink 创 建符号链接
unlink 删除链接
readlink 读 符号链接的值
mount 安装文件系统
umount 卸 下文件系统
ustat 取文件系统信息
utime 改 变文件的访问修改时间
utimes 参见utime
quotactl 控 制磁盘配额

回页首

三、系统控制

ioctl I/O总控制函数
_sysctl 读/写系 统参数
acct 启用或禁止进程记账
getrlimit 获 取系统资源上限
setrlimit 设置系统资源上限
getrusage 获 取系统资源使用情况
uselib 选择要使用的二进制函数库
ioperm 设 置端口I/O权限
iopl 改变进程I/O权限级别
outb 低 级端口操作
reboot 重新启动
swapon 打 开交换文件和设备
swapoff 关闭交换文件和设备
bdflush 控 制bdflush守护进程
sysfs 取核心支持的文件系统类型
sysinfo 取 得系统信息
adjtimex 调整系统时钟
alarm 设 置进程的闹钟
getitimer 获取计时器值
setitimer 设 置计时器值
gettimeofday 取时间和时区
settimeofday 设 置时间和时区
stime 设置系统日期和时间
time 取 得系统时间
times 取进程运行时间
uname 获 取当前UNIX系统的名称、版本和主机等信息
vhangup 挂起当前终端
nfsservctl 对 NFS守护进程进行控制
vm86 进入模拟8086模式
create_module 创 建可装载的模块项
delete_module 删除可装载的模块项
init_module 初 始化模块
query_module 查询模块信息
*get_kernel_syms 取 得核心符号,已被query_module代替

回页首

四、内存管理

brk 改变数据段空间的分配
sbrk 参见brk
mlock 内 存页面加锁
munlock 内存页面解锁
mlockall 调 用进程所有内存页面加锁
munlockall 调用进程所有内存页面解锁
mmap 映 射虚拟内存页
munmap 去除内存页映射
mremap 重 新映射虚拟内存地址
msync 将映射内存中的数据写回磁盘
mprotect 设 置内存映像保护
getpagesize 获取页面大小
sync 将 内存缓冲区数据写回硬盘
cacheflush 将指定缓冲区中的内容写回磁盘

回页首

五、网络管理

getdomainname 取域名
setdomainname 设 置域名
gethostid 获取主机标识号
sethostid 设 置主机标识号
gethostname 获取本主机名称
sethostname 设 置主机名称

回页首

六、socket控制

socketcall socket系统调用
socket 建 立socket
bind 绑定socket到端口
connect 连 接远程主机
accept 响应socket连接请求
send 通 过socket发送信息
sendto 发送UDP信息
sendmsg 参 见send
recv 通过socket接收信息
recvfrom 接 收UDP信息
recvmsg 参见recv
listen 监 听socket端口
select 对多路同步I/O进行轮询
shutdown 关 闭socket上的连接
getsockname 取得本地socket名字
getpeername 获 取通信对方的socket名字
getsockopt 取端口设置
setsockopt 设 置端口参数
sendfile 在文件或端口间传输数据
socketpair 创 建一对已联接的无名socket

回页首

七、用户管理

getuid 获取用户标识号
setuid 设置用户标 志号
getgid 获取组标识号
setgid 设 置组标志号
getegid 获取有效组标识号
setegid 设 置有效组标识号
geteuid 获取有效用户标识号
seteuid 设 置有效用户标识号
setregid 分别设置真实和有效的的组标识号
setreuid 分 别设置真实和有效的用户标识号
getresgid 分别获取真实的,有效的和保存过的组标识号
setresgid 分 别设置真实的,有效的和保存过的组标识号
getresuid 分别获取真实的,有效的和保存过的用 户标识号
setresuid 分别设置真实的,有效的和保存过的用户标识号
setfsgid 设 置文件系统检查时使用的组标识号
setfsuid 设置文件系统检查时使用的用户标识号
getgroups 获 取后补组标志清单
setgroups 设置后补组标志清单

回页首

八、进程间通信

ipc 进程间通信总控制调用

1、信号

sigaction 设置对指定信号的处理方法
sigprocmask 根 据参数对信号集中的信号执行阻塞/解除阻塞等操作
sigpending 为指定的被阻塞信号设置队 列
sigsuspend 挂起进程等待特定信号
signal 参 见signal
kill 向进程或进程组发信号
*sigblock 向 被阻塞信号掩码中添加信号,已被sigprocmask代替
*siggetmask 取得现有阻塞 信号掩码,已被sigprocmask代替
*sigsetmask 用给定信号掩码替换现有阻塞信 号掩码,已被sigprocmask代替
*sigmask 将给定的信号转化为掩码,已被 sigprocmask代替
*sigpause 作用同sigsuspend,已被 sigsuspend代替
sigvec 为兼容BSD而设的信号处理函数,作用类似 sigaction
ssetmask ANSI C的信号处理函数,作用类似sigaction

2、消息

msgctl 消息控制操作
msgget 获取消息队列
msgsnd 发 消息
msgrcv 取消息

3、管道

pipe 创建管道

4、信号量

semctl 信号量控制
semget 获取一组信号量
semop 信 号量操作

5、共享内存

shmctl 控制共享内存
shmget 获取共享内存
shmat 连 接共享内存
shmdt 拆卸共享内存

参考资料

  • Linux man pages
  • Advanced Programming in the UNIX Environment, W. Richard Stevens, 1993

解决QT静态编译和debug库问题

Posted by – March 13, 2010

解决QT静态编译和debug库问题(mingwm10.dll丢失、collect2:ld returned 1 exit status)
通过网上到处找资料,终于搞定了遇到的问题,相信其它人也会遇到,我就把解决方法非写出来了:

平台:windows7   软件:qt4.6+qt creator 1.3     使用的安装包是  qt-sdk-win-opensource-2009.05.exe

问题描述:
(1)使用 qt creator 生成工程,写好程序进行编译运行,在qt creator点击运行,程序能跑起来,没问题,可是我在工程目录下找到编译生成的程序双击运行时,提示缺少mingwm10.dll,无法运行。
(2) 按照 http://wiki.qtcentre.org/index.php?title=Building_static_Qt_on_Windows#MinGW 这个方法完成操作以后,使用qt creator 不能用debug,只能使用release选项,使用debug选项产生的错误提示是:collect2:ld returned 1 exit status    通过查找资料发现是缺少debug库

——————————————————
解决方案:
(1)编辑 C:\Qt\2009.05\qt\mkspecs\win32-g++\qmake.conf 文件 把
QMAKE_LFLAGS =         -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
改为: QMAKE_LFLAGS = -static -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
(2)在开始菜单里找到Qt Command Prompt并运行,输入命令:   configure -static -debug-and-release -no-exceptions
(3)上一条命令运行成功后再输入命令:   mingw32-make sub-src
等1 ~3 小时可能就运行完了   完成后就ok了。。。
已经在我的机子上验证,祝大家好运!

Q: 首先得谢谢楼主让我解决了不能debug的问题。。。但是又产生了新的问题,现在debug生成的可执行文件太大了。。。一百多兆啊,release生成的才八九M。。。这是怎么回事儿呢?

A: Debug时当然用debug了,大一点没关系。Debug 完之后正式发布软件再用release编译一遍。然后还可以strip(Linux 程序) 一下exe、在upx(http://upx.sourceforge.net/#download)一下exe和dll,体积会非常小。

QT手册& 汉化Chinesize

Posted by – January 5, 2010

QT手册& 汉化Chinesize:
http://blog.chinaunix.net/u/26313/showart_213711.html

嵌入式工具Qt的安装与使用
——————————————————————————–

摘要
Qt是Trolltech公司的一个产品。Trolltech是挪威的一家软件公司,主要开发两种产品:一种是跨平台应用程序界面框架;另外一种就是提供给做嵌入式Linux开发的应用程序平台,能够应用到PDA和各种移动设备上。Qt和 Qtopia分别是其中具有代表性的两个。

By lanf, 出处:http://tech.ccidnet.com/pub/article/c310_a71173_p1.html

作者:胡利民 本文选自:开放系统世界

Qt是Trolltech公司的一个产品。Trolltech是挪威的一家软件公司,主要开发两种产品:一种是跨平台应用程序界面框架;另外一种就是提供给做嵌入式Linux开发的应用程序平台,能够应用到PDA和各种移动设备上。Qt和Qtopia分别是其中具有代表性的两个。

Qt是一个多平台的C++图形用户界面应用程序框架,它能给用户提供精美的图形用户界面所需要的所有元素,而且它是基于一种面向对象的思想,所以用户对其对象的扩展是相当容易的,并且它还支持真正的组件编程。

Qt是Linux桌面环境KDE的基础。笔者认为,可以说Qt与Windows下的Mfc的实质是一样的,所以Qt最大的优点在于其跨平台性,可以支持现有的多种操作系统平台,主要有:

◆ MS/Windows 95、Windows 98、WindowsNT 4.0、Windows 2000、Windows XP;

◆ Unix/X11 Linux、Sun Solaris、HP-UX、Compaq True64Unix、IBM AIX、SGI IRIX和很多其它X11平台;

◆ Macintoshi Mac OSX;

◆ Embedded—带FramBuffer的Linux平台。

下面简单介绍一下Qt/Embedded和Qtopia在Linux上的安装和使用,还有在开发过程中可能碰到的一些问题。

Qt 和Qtopia的安装

如果需要安装一个带FramBuffer的Qtopia平台,需要有以下软件(所列举软件以笔者使用的为例):

◆ Qtopia 1.6.0;

◆ Tmake 1.11;

◆ Qt/Embedded 2.3.4(Qtopia 1.6.0是基于该开发平台上开发的);

◆ Qt/Embedded 2.3.2 for X11;

◆ Qt 3.1.2 for X11。

在Trolltech公司的网站上可以下载该公司所提供的Qt/Embedded的免费版本。

Qtopia平台安装分为以下几个步骤:

1. 解包Qtopia

在Linux命令模式下运行以下命令:

tar xfz qtopia-source-1.6.0 (解包)
cd qtopia-source-1.6.0
export QPEDIR=$PWD (设置环境变量)
cd..

2. 安装Tmake

在Linux命令模式下运行以下命令:

tar xfz tmake-1.11.tar.gz
export TMAKEDIR=$PWD/tmake-1.11
export TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
export PATH=$TMAKEDIR/bin:$PATH

3. 安装Qt/Embedded2.3.4

在Linux命令模式下运行以下命令:

tar xfz qt-embedded-2.3.4-commercial.tar.gz
cd qt-2.3.4
export QTDIR=$PWD
export QTEDIR=$QTDIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
cp $QPEDIR/src/qt/qconfig-qpe.h src/tools/
. /configure -qconfig qpe -qvfb -depths 4,8,16,32
make sub-src
cd ..

也可以在configure的参数中添加-system-jpeg和gif,使Qtopia平台能支持jpeg、gif格式的图形。

4. 安装Qt/X11 2.3.2

在Linux命令模式下运行以下命令:

tar xfz qt-x11-2.3.2-commercial.tar.gz
cd qt-2.3.2
export QTDIR=$PWD
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
. /configure -no-opengl
make
make -C tools/qvfb
mv tools/qvfb/qvfb bin
cp bin/uic $QTEDIR/bin
cd ..

根据开发者本身的开发环境,也可以在configure的参数中添加别的参数,比如-no-opengl或-no-xfs,可以键入./configure -help来获得一些帮助信息。

5. 安装Qt/X11 3.1.2

在Linux命令模式下运行以下命令:

tar xfz qt-x11-commercial-3.1.x.tar.gz
cd qt-x11-commercial-3.1.x
export QTDIR=$PWD
export QT3DIR=$QTDIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -thread
make
cd ..

6. 安装Qtopia

在Linux命令模式下运行以下命令:

cd qtopia-source-1.6.x
export QTDIR=$QTEDIR
export QPEDIR=$PWD
export PATH=$QPEDIR/bin:$PATH
cd src
./configure
make
cd ../..

7. 安装Qtopia桌面

cd qtopia-source-1.6.x/src
export QTDIR=$QT3DIR
./configure -qtopiadesktop
make
mv qtopiadesktop/bin/qtopiadesktop ../bin
cd ..

Qt和Qt Designer的使用
——————————————————————————–
根据上面的步骤安装完成了Qt/Embedded和Qtopia之后,就可以运行这些程序了。

运行Qt的虚拟仿真窗口:在Linux的图形模式下运行命令qvfb&;Qtopia只是一个用Qt/Embedded开发的程序,运行Qtopia,在图形模式下运行命令:

export QTDIR=$QTEDIR,
qpe &;

这样Qtopia的程序就运行在QVFB上,即Qt的虚拟仿真窗口。

Qt/Embedded是针对嵌入式Linux而开发的一种开发工具,Qt封装了一些常用的类,而且这些类的名字都以Q字开头命名,如QString、QDialog等。这里主要介绍一下如何利用Qt Designer来设计组件,并生成相应的代码。

在Qt中,把组件分为复合体、原始体和配件。而在Qt中,组件是由一些抽象类、复杂的组件类、管理组件几何特性的类等组成。

Qt中有三个主要的基类:QObject、Qapplication和QWidget。

在Qt 中编程,利用Signal和Slot进行对象之间的通信是Qt的主要特征。它与Windows中的消息机制非常类似,但是Signal和Slot机制真正实现了一种消息的封装。当对象的状态改变时,发出Signal,通知所有的Slot接受Signal,尽管它不知道哪些函数是Slot,Slot一开始也不知道哪些Signal可以接收。Signal和Slot之间不是一一对应的关系,一个Signal可以发给多个Slot, Slot也可以接收多个Signal。Slot除了可以接收Signal以外,与其它的成员函数没有区别。这种机制比使用回调函数要灵活,但是会减慢程序的运行速度。不过在现在高速CPU的面前,这种损失是无足轻重的,而且它还能保证程序的简明性和灵活性,非常便利。

在Qt的组件中,不仅定义了常用的成员变量和成员函数,还定义了所有与该组件相关的Signal和Slot。

要将组件组合起来,最简单的方法就是使用Qt Designer。首先要启动Qt Designer,在Linux命令模式下,键入以下命令(假设Qt安装在/usr/local下):

cd qt-2.3.2/bin
./designer

这样就可以启动一个与Windows下的Delphi相类似的界面。

然后新建一个QFrame,将自己需要的组件直接拖拉到这个Frame中,相信很多人都有过这样的经历,此处就不再详细描述了。完成之后存盘时,会将这个新的组件保存为一个扩展名为.ui的文件。假设所存的文件名为test.ui,用vi test.ui来查看这个文件,发现这是一个用xml语言写的一个文本。下面用这个test.ui生成相应的test.h和test.cpp。同样还是在这个目录下,可以看到一个uic的工具,这个是Qt专门用来将ui文件生成.h和.cpp文件的,在终端模式下键入以下命令:

./uic -o test.h test.ui
./uic -o test.h -i test.cpp test.ui

此时就能看到生成了相应test.h和test.cpp,这是一个类。当然这只是一些表面的东西,还需要在这些代码中添加相应的Signal和Slot,完成所需要的操作。值得注意的是,相应版本生成的ui最好用相应版本的uic来生成代码。如果用Qt 3.1.2的Designer生成的ui,用Qt 2.3.2的uic来生成代码,生成的代码都会是一些空函数。

在一般的开发过程中,首先通过这个ui生成的一个类,在Qt中通常叫做 Base,如上面的例子,叫做testBase;然后再新建一个类,来继承这个Base。通常叫做实现类Impl,如testImpl。在这个实现类里面定义所需要的成员函数、Signal和Slot,因为ui可能是经常需要改动的。如果这样做,每次只需要在Designer中修改ui,而不用去理会这些成员函数、Signal和Slot了。

编译一个Qt程序必然需要Makefile,在Qt中提供了一个专门生成Makefile的工具,就是tmake。用tmake需要根据编写的程序写一个.pro文件。.pro文件非常简单,有固定的格式,下面是一个例子:

TEMPLATE = app
CONFIG = qtopia warn_on release
MOC_DIR =tmp
OBJECTS_DIR =tmp
HEADERS =fcrs.h\
structs.h \
globalfunc.h \
globalvars.h \
testimpl.h
SOURCES = main.cpp \
globalfunc.cpp\
globalvars.cpp \
testimpl.cpp
INTERFACES = test.ui \
TARGET = fcrs

生成这个.pro文件之后,在终端中键入下面的命令:

tmake -o Makefile test.pro

就自动生成了一个Makefile,使用这个Makefile编译所编写的程序就可以了。

Qt/Embedded开发环境建立的过程
——————————————————————————–

Qt/Embedded开发环境建立的过程:
(这些软件可以免费从trolltech的WEB或FTP服务器上下载)
◆ tmake 1.11 或更高版本; (生成Qt/Embedded应用工程的Makefile文件)
◆ Qt/Embedded 2.3.7 (Qt/Embedded 安装包)
◆ Qt 2.3.2 for X11; (Qt的X11版的安装包, 它将产生x11开发环境所需要的两个工具)
1、安装tmake
在Linux 命令模式下运行以下命令:
tar xfz tmake-1.11.tar.gz
export TMAKEDIR=$PWD/tmake-1.11
export TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
export PATH=$TMAKEDIR/bin:$PATH
2. 安装Qt/Embedded 2.3.7
在Linux 命令模式下运行以下命令:
tar xfz qt-embedded-2.3.7.tar.gz
cd qt-2.3.7
export QTDIR=$PWD
export QTEDIR=$QTDIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -qconfig -qvfb -depths 4,8,16,32
make sub-src
cd ..
上述命令 ./configure -qconfig -qvfb -depths 4,8,16,32 指定Qt 嵌入式开发包生
成虚拟缓冲帧工具qvfb,并支持4,8,16,32 位的显示颜色深度。另外我们也可以在
configure 的参数中添加-system-jpeg 和gif,使Qt/Embedded 平台能支持jpeg、gif
格式的图形。
上述命令 make sub-src 指定按精简方式编译开发包,也就是说有些Qt 类未被编
译。Qt 嵌入式开发包有5 种编译范围的选项,使用这些选项,可控制Qt 生成的库文件的大
小,但是您的应用所使用到的一些Qt 类将可能因此在Qt 的库中找不到链接。编译选项的具
体用法可运行./configure -help 命令查看。
3. 安装Qt/X11 2.3.2
在Linux 命令模式下运行以下命令:
tar xfz qt-x11-2.3.2.tar.gz
cd qt-2.3.2
export QTDIR=$PWD
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -no-opengl
make
make -C tools/qvfb
mv tools/qvfb/qvfb bin
cp bin/uic $QTEDIR/bin
cd ..
根据开发者本身的开发环境,也可以在configure 的参数中添加别的参数,比如
-no-opengl 或-no-xfs,可以键入./configure -help 来获得一些帮助信息。
如果Qt/Embedded 的应用是在UNIX 平台下开发的话,那么它就可以在开发的机器
上以一个独立的控制台或者虚拟缓冲帧的方式来运行,对于后者来说,其实是有一个X11
的应用程序虚拟了一个缓冲帧。通过指定显示设备的宽度,高度和颜色深度,虚拟出来
的缓冲帧将和物理的显示设备在每个像素上保持一致。这样每次调试应用时开发人员就
不用总是刷新嵌入式设备的FLASH 存储空间,从而加速了应用的编译、链接和运行周期。
运行Qt 的虚拟缓冲帧工具的方法是:在Linux 的图形模式下运行命令:
qvfb (回车)
当Qt 嵌入式的应用程序要把显示结果输出到虚拟缓冲帧时,我们在命令行运行这
个程序时,在程序名后加上-qws 的选项。例如: $> hello -qws

一,QT/E的安装。
——————————————————————————–
在本机中安装了包括QT4.0.1(WINDOWS版本)以及QT/E2。3。7(LINUX版本)。
QT/E我安装在我的虚拟机中。因为QT/E2.3.7的版本问题,其适宜在REDHAT9。0版本(或更低版本)下安装,否则安装不成功。
QT/E安装过程复杂,具体细节可以参考下面这篇文章。《Qt/Embedded开发环境建立的过程》
此文在网上可搜索到。

本机下虚拟机中QT/E安装路径为:/home/wangxl/QTE/qt-2.3.7
QT/X11安装路径为:/home/wangxl/QTE/qt-2.3.2
Tmake安装路径为:/home/wangxl/QTE/Tmake-1.8

QT/E下载地址为:ftp://ftp.rediris.es/mirror/Qt/source/
Tmake下载地址为:ftp://ftp.trolltech.com/freebies/tmake/

二.QT与QT/E以及QT3与QT4之间的区别
——————————————————————————–
相对来说QT与QT/E的语法一样,所不同之处在库类大小或者库类函数大小不同而已。QT/E相对于QT来说,不具有少数类或者少数函数的支持。具体QT/E是否包含某个类或者包含某个类中的函数,我的方法是在QT/E安装目录下的include文件夹中去查找。
QT3和QT4有很多不同点,主要不同也是在于库类以及支持函数有所变化,比如,有些QT3中的函数,在QT4中被其他函数名所代替,因此很多QT4程序在QT3环境下无法执行。QT/E2.3.7与QT3基本相同,除了我前面提到的QT与QT/E的差别。

三.QT/E编译与执行。
——————————————————————————–
1.在QT/E编译与执行前要先设置TMAKE与QT/E LIB环境,具体方法如下:
[root@localhost tmake-1.8]# export TMAKEDIR=$PWD
[root@localhost tmake-1.8]# export TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
[root@localhost tmake-1.8]# export PATH=$TMAKEDIR/bin:$PATH
[root@localhost qt-2.3.7]# export QTDIR=$PWD
[root@localhost qt-2.3.7]#export QTEDIR=$QTDIR
[root@localhost qt-2.3.7]#export PATH=$QTDIR/bin:$PATH
[root@localhost qt-2.3.7]#export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH

2.如果你是用DESIGNER工具设计的界面(后面有讲),则要将*.ui文件转换成*.h文件和*.cpp文件。转换方法如下:
uic –o test.h test.ui
uic –o test.cpp –i test.h test.ui

3.编写一个*.pro文件(用来生成Makefile文件用),该文件格式比较固定。
如test.pro文件基本格式如下(以test.cpp ,test.h main.cpp为例子):
EMPLATE = app
CONFIG += qt warn_on release
HEADERS = test.h
SOURCES = test.cpp \
main.cpp
TARGET = hello
DEPENDPATH=/home/wangxl/QTE/qt-2.3.7/include
REQUIRES=

4.生成Makefile文件
方法为:tmake –o Makefile test.pro
5 编译生成可执行文件
make

6 打开QVFB
进入安装QT/X11所在目录, 在BIN目录下执行程序qvfb。
有时候需要修改qvfb执行时的deptb参数才能够执行QT/E程序。可以直接在QVFB打开窗口的Configure彩单项中选择,也可以用如下命令执行QVFB。
./qvfb –width ** -height ** -depth **
7.执行QT/E程序
如 。/TEST
在QVFB程序打开的窗口中将出现TEST程序的显示 。

四.Qt/e与QT/X11
——————————————————————————–
安装QT/E的同时还需要安装QT/X11与Tmake,Tmake 是用来帮助生成Makefile文件的。安装QT/X11主要是向QT/E提供designer工具和qvfb工具的。
Designer可以用来设计图形界面,最后生成.ui文件,可通过UIC命令转换为相应的C++文件。
QVFB模拟帧缓冲,提供QT/E程序的显示平台。

五.QT/E 程序ARM 板上执行
——————————————————————————–
在我虚拟机上可以执行的QT/E程序不能在ARM板上执行,需要对QT/E进行重新编译,并需要设置响对于ARM板系统的编译环境,具体方法可以参考我另外的一文《QT/E开发记录》

六.QT/E支持中文显示问题
——————————————————————————–
QT/E需要字体转换才能显示中文。具体方法可以参考我另外的一文《QT/E开发记录》
但是由于缺少UNICODE的QPF文件的字体,中文字大小不均匀问题尚没解决。

七 QT/E的一些参考资料:
——————————————————————————–
http://www.qtcn.org/bbs/index.php QT中文论坛
http://www.qiliang.net/qt/ (关于QT3的类,以及类函数可以在这寻找)
提供QT3编程最好书籍的电子版本《C++ GUI Programming with QT 3》(本机)
关于QT4可以参考QT ASSISTNAT(本机中),另外QT ASSISTNAT中也可以查找QT3的类及库等。

设置Qtopia的build环境
——————————————————————————–
1、环境变量
在目标系统上build Qtopia必须设定必要的环境变量,如QTDIR, 如果依赖多个版本的Qt,则需要用环境变量指向用到的库配置。
一种方法是将环境变量的设定写入文件,通过运行source命令应用文件的内容。

举例说明:
Linux/bash下建立环境变量设定文件qtopia.sh, 内容如下:
export QPEDIR=/opt/Qtopia
export QTDIR=/opt/Qtopia
export PATH=$QTDIR/bin:$PATH
export TMAKEPATH=/opt/Qtopia/tmake/lib/qws/linux-generic-g++
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH

需要时运行 source qtopia.sh即可应用以上环境变量。

另外也可以通过在.bashrc文件中设定别名来应用这些环境变量。 假设环境变量设置文件存放在home目录的bin下, 则可在.bashrc中加入:
alias qtopia=’source ~/bin/qtopia.sh’

2、创建自定义的配置文件

整个Qtopia系统在build过程中需要三个独立的配置系统:
1) Qt/Embedded 配置文件
2) tmake – 用于build Qtopia 1.x和一些第三方软件
3) qmake – 用于build Qtopia 2及以上版本

在build Qtopia以前,先要完成以上三项的配置。

下面是创建一个“myarm”配置的例子:
cp -r $TMAKEDIR/lib/qws/linux-arm-g++ $TMAKEDIR/lib/qws/linux-myarm-g++
cp -r $QTEDIR/configs/linux-arm-g++-shared $QTEDIR/configs/linux-myarm-g++-shared
cp -r $QPEDIR/mkspecs/qws/linux-arm-g++ $QPEDIR/mkspecs/qws/linux-myarm-g++
Qt/Embedded
编辑$QTEDIR/configs/linux-myarm-g++-shared, 修改其中的utilities和flags成适合系统的内容, 如:
可能需要修改SYSCONF_CXX, SYSCONF_CC 和SYSCONF_LINK, 指定正确的编译器. 可能需要增加编译选项-DMYARM,在代码中增加宏#ifdef MYARM(用以增加设备相关的代码)
tmake
编辑$TMAKEDIR/lib/qws/linux-myarm-g++/tmake.conf,修改用到的utilites和flags:
可能需要修改TMAKE_CC, TMAKE_CXX 和TMAKE_LINK
可能需要增加-DMYARM选项
qmake

修改$QPEDIR/mkspecs/qws/linux-myarm-g++/qmake.conf:

QMAKE_CC, QMAKE_CXX 和QMAKE_LINK
-DMYARM
注意:qmake.conf的最后一行必须是:
exists($$(QPEDIR)/src/config.pri):include($$(QPEDIR)/src/config.pri)

可用于目录名的字符

正则表达式字符

Qtopia 1.9.x和Qtopia 2.0.0 – 2.1.0对字符有以下的要求:

Qtopia build系统访问到的任何目录都不能包含正则表达式字符
任何能被QRegExp识别的特殊字符和字符序列都可能引起问题
‘.’字符只匹配自身
不要将正则表达式字符用于目录名,包括(但不仅限于): + . ? * \ () [] ^ $ {}
注:Qtopia 2.1.0和2.1.1及以上版本可以通过应用patch解除以上的限制

空格

Qtopia build系统不支持目录名中包含空格。

必须保证:

build系统访问到的目录不包含空格
访问目录的各层上级目录也不包含空格

Qtopia的依赖和必要条件
——————————————————————————–
简介
为了使Qtopia正确运行, 必须满足以下的必要条件:
安装适当的编译/交叉编译工具
Qt/Embedded-2.3.11
Linux kernel提供共享内存、mmap和socket支持
Linux支持frame buffer
Linux支持OSS声音或支持与OSS兼容的ALSA声音。 进一步的信息参考 The Qtopia A/V & Appearance FAQ
还需要以下的库:

Video4Linux
zlib
libuuid(aka luuid)
libjpeg
xorg 或X11
注:Freetype不是必须的但建议支持 http://freetype.sourceforge.net/index2.html

支持的编译器和交叉编译器
gcc-2.95.2
gcc 3.2.4
gcc-3.3.0, gcc-3.3.3, gcc-3.3.4
gcc-3.4.1

声音

Qtopia需要/dev/dsp可写,并支持以下的ioctl操作:

SNDCTL_DSP_SETFRAGMENT – Qtopia将这个值设置为0x4000c.
SNDCTL_DSP_SETFM – Qtopia设置为AFMT_S16_LE
SNDCTL_DSP_STEREO – Qtopia 设置为 1/true.
SNDCTL_DSP_SPEED – Qtopia设置为44100.
SNDCTL_DSP_GETOSPACE
Qtopia 还需要/dev/dsp可以以阻塞方式和非阻塞方式打开。以上的设定是Qtopia需要的设定,但可以容许少许的偏差,但如果不设定成Qtopia需要的值则不能保证音频能平滑播放。假如您的设备只支持22500的播放速率,则在调用SNDCTL_DSP_SPEED时要报告此速率,以免造成音频质量的缺损。不过,如果系统不支持GETOSPACE,非阻塞写入或 SNDCTL_DSP_SET_FRAGMENT, 几乎可以肯定必然会损失音频质量。

Video4Linux

Camera应用程序需要支持Video4Linux v1的内核, Qtopia没有提供该支持。

请参考官方的 Video for Linux 资源站点,参看API的详细内容。

zlib

zlib可以从 http://www.gzip.org/zlib/ 下载

libuuid(aka luuid)

Qtopia不提供uuid支持。 这个库可以从 http://e2fsprogs.sourceforge.net/ 站点下载

另外, 如果你有SuSE Linux的发行版, e2fsprogs-devel这个包可以提供uuid支持; 在Debian中对应的包是uuid-dev. 这个库应该放在标准路经下/usr/lib/libuuid.so

请用包管理工具来查看该包是否已经安装:

rpm -qa | grep e2fsprogs

如果尚未安装,请从安装盘或镜像站安装这个包。

需要注意的是,如果没有安装e2fsprogs-devel,在链接时会发生下面的错误:

/usr/lib/gcc-lib/i586-suse-Linux/3.3.3/../../../../i586-suse-linux/bin/ld:
cannot find -luuid
如果安装了该库仍遇到上述错误,则检查Qtopia的configure脚本的-L和-R参数, 以确定脚本包含了库的实际路径。 关于交叉编译的信息可参考 System Integrator’s Guide.

libjpeg

Qtopia不提供jpeg库。 Qtopia需要Qt/Embedded配置为支持jpeg。

libjpeg库可从 http://www.ijg.org/ 下载

此外,如果你有SuSE发行版, libjpeg这个包可提供该库。 这个库应该安装在标准路经/usr/lib/libjpeg.so

检查libjpeg包是否已经安装:

rpm -qa | grep libjpeg

如果未安装,请从安装盘或镜像站安装。

关于libjpeg交叉编译的信息, 可参考System Integrator’s Guide.

xorg或X11

开发包括有从以下站点下载:

http://www.xfree86.org/
http://xorg.freedesktop.org
相关讨论

决定开始Qtopia需要的步骤, 参考: Getting started

将Qtopia集成到特定设备上的重点步骤, 参考: System Integrator’s guide

Qtopia的FAQs,参看: Qtopia index page

系统是完全安装RedHat9.0(里面带QT3.1),板子是X-Hyper250B的,Toolchain用的是开发板带的hybus-arm-linux-R1.1

交叉编译所用到的文件:
qt-embedded-2.3.10-free.tar.gz
qt-x11-2.3.2.tar.gz
qtopia-free-source-2.1.1.tar.gz
tmake-1.13.tar.gz
e2fsprogs-1.35.tar.gz

主机x86的编译步骤:
tar xfz qt-embedded-2.3.10-free.tar.gz(解压后qt-2.3.10改名为qt-2.3.10-host)
export QTEDIR=$PWD/qt-2.3.10-host
tar xfz qt-x11-2.3.2.tar.gz(解压后qt-2.3.2)
export QT2DIR=$PWD/qt-2.3.2
tar xfz qtopia-free-source-2.1.1.tar.gz(解压后qtopia-free-2.1.1改名为qtopia-2.1.1-host)
export QPEDIR=$PWD/qtopia-2.1.1-host
tar xfz tmake-1.13.tar.gz(解压后tmake-1.13)
export TMAKEDIR=$PWD/tmake-1.13
export TMAKEPATH=$PWD/tmake-1.13/lib/qws/linux-x86-g++
export PATH=$TMAKEDIR/bin:$PATH

cd qt-2.3.2
export QTDIR=$QT2DIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -no-xft
make
make -C tools/qvfb
cd ..

cd qt-2.3.10-host
export QTDIR=$PWD
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
mkdir bin (因为解压后的qt-2.3.10没有bin文件夹)
cp $QT2DIR/bin/uic bin
cp $QT2DIR/tools/qvfb/qvfb bin
cp $QPEDIR/src/qt/qconfig-qpe.h src/tools/
./configure -qconfig qpe -qvfb -thread -system-jpeg -gif -depths 4,8,16,32
make
cd ..

tar xzf e2fsprogs-1.35.tar.gz
cd e2fsprogs-1.35
./configure -enable-elf-shlibs
make install lib/uuid/
注:这是编译x86的libuuid库
cd ..

cd qtopia-2.1.1-host
export PATH=$QPEDIR/bin:$PATH
export LD_LIBRARY_PATH=$QPEDIR/lib:$LD_LIBRARY_PATH
./configure
make

编译例子:
$qmake -project ==>>我都是这样创建.pro文件的
$tmake -o Makefile hello.pro
$make
$qvfb &
$./hello -qws 或者$qpe
还没怎么仔细研究过。

目标机arm-linux的编译步骤:
tar xfz qt-embedded-2.3.10-free.tar.gz(解压后qt-2.3.10改名为qt-2.3.10-target)
export QTEDIR=$PWD/qt-2.3.10-target
tar xfz qt-x11-2.3.2.tar.gz
export QT2DIR=$PWD/qt-2.3.2
tar xfz qtopia-free-source-2.1.1.tar.gz(解压后qtopia-free-2.1.1改名为qtopia-2.1.1-target)
export QPEDIR=$PWD/qtopia-2.1.1-target
tar xfz tmake-1.13.tar.gz
export TMAKEDIR=$PWD/tmake-1.13
export TMAKEPATH=$PWD/tmake-1.13/lib/qws/linux-arm-g++
export PATH=$TMAKEDIR/bin:$PATH

cd qt-2.3.2
export QTDIR=$QT2DIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -no-xft
make
make -C tools/qvfb
cd ..

cd qt-2.3.10-target
export QTDIR=$PWD
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
mkdir bin
cp $QT2DIR/bin/uic bin
cp $QT2DIR/tools/qvfb/qvfb bin
cp $QPEDIR/src/qt/qconfig-qpe.h src/tools/
./configure -xplatform linux-arm-g++ -qconfig qpe -qvfb -thread -system-jpeg -gif -depths 4,8,16,32
make
cd ..
注:这里需要arm版本的libjpeg.so.62,hybus-arm-linux-R1.1里面包含这个库;如没有可以上网下载。

tar xzf e2fsprogs-1.35.tar.gz
cd e2fsprogs-1.35
./configure -host=arm-linux -with-cc=arm-linux-gcc -with-linker=arm-linux-ld -enable-elf-shlibs -prefix=/usr/local/hybus-arm-linux-R1.1/arm-linux
make install lib/uuid/ ===>>>这步安装到hybus-arm-linux-R1.1/lib上的libuuid.so.1.2版本不对,
要cp lib/libuuid.so.1.2 ../hybus-arm-linux-R1.1/lib
注:这步是交叉编译arm的libuuid库,配置详情见./configure –help
(还要多谢 http://panjet.wleda.com/?p=20 这里面的大哥呀,好不容易才在网上找到的)
cd ..

cd qtopia-2.1.1-host
export PATH=$QPEDIR/bin:$PATH
export LD_LIBRARY_PATH=$QPEDIR/lib:$LD_LIBRARY_PATH
cp src/libraries/qtopia/custom-linux-ipaq-g++.cpp src/libraries/qtopia/custom-linux-arm-g++.cpp
cp src/libraries/qtopia/custom-linux-ipaq-g++.h src/libraries/qtopia/custom-linux-arm-g++.h
./configure -xplatform linux-arm-g++
make
注:这里需要libstdc++.so和libgcc_s.so库

develop环境下qt中文化程序设计
——————————————————————————–
原文出处:Linux公社

kdevelop是一款在linux平台下可以同windows环境下的vc相媲美的集成开发环境,qt则是一款支持包括windows和linux平台
的GUI库,可以说它是linux下的MFC.在显示上,qt使用Unicode作为内部编码,可以支持多种编码.如何使用qt进行国际化编程
在网上可以找到很多资料的,但都是针对较早版本的qt进行介绍的.qt3.0.5中对这些作了些改动,这些方法就相应的要做些改动.
而且在kdevelop中开发qt应用程序,将会事半功倍.我的开发环境为redhat8.0(需安装kde开发工具包).
首先在linux中打开kdevelop集成开发环境,用它的应用程序向导新建一个qt的SDI的应用程序框架.这个同windows下vc很类似.它
将会为你自动生成版本号,作者,e-mail等信息的单文档对话框的应用程序框架.我们首先对它自动生成的程序进行汉化(qt1是
我的项目名称).

1.汉化自动生成的程序

添加翻译文件
在”项目”菜单中选择”添加新的翻译文件”,语言选择”zh_CN.Gb2312″.将会创建一个zh_CN.GB2312字符编码的翻译文件.扩展名
为”.ts”.在qt3.0.5环境下,打开”*.ts”翻译文件的工具是linguist.你可以在”工具”菜单中选择”QT
linguist”来打开,在linguist菜单中选择”file”->”open”打开所要翻译的翻译文件.此时可以在linguist窗口中的source text中
的文本就是你所要翻译的文本,选择所要翻译的文本,在下方有一个类似一页纸一样的地方,在translate下输入翻译后的文本.所示.
翻译完这些文件后,编译运行,在我们的程序里并不能显示中文,还是英文,我们还需要做的就是用lrelease命令将翻译后的文件转换成”.qm”文件才可以使用.在控制台下进入你用kdevelop所生成的应用程序目录.
>lrelease Makefile.am
qt3.0.5用这个两个程序取代了以前版本的findtr和msg2qm命令.在kdevelop集成环境中打开main.cpp主函数,
……
QApplication a(argc, argv);
a.setFont(QFont(“helvetica”, 10));
QTranslator tor( 0 );
tor.load( QString(“qt1.”) + QTextCodec::locale(), “.” );
// tor.load( QString(“qt1.zh_CN.GB2312”), “.” );
a.installTranslator( &tor );
/* uncomment the following line, if you want a Windows 95
look*/
// a.setStyle(WindowsStyle);

Qt1App *qt1=new Qt1App();
//Form1 *qt1=new Form1();
a.setMainWidget(qt1);
……
此处:
tor.load( QString(“qt1.”) + QTextCodec::locale(), “.” );
是根据客户环境的locale来载入当前目录下相应的翻译文件的.redhat中文环境默认的locale为gb18030,
此处要么把翻译文件名由qt1.zh_CN.GB2312.qm改为qt1.zh_CN.GB18030.qm,要么将这句改为
tor.load( QString(“qt1.zh_CN.GB2312”), “.” );
不过为了国际化编程的需要,建议采用第一种方法.更改后编译运行,你会发现你的程序已经是中文界面的了.

2.汉化自己的对话框

大多数情况下我们都需要自己来设计对话框,qt为我们提供了非常好的对话框编辑器QtDesigner,可以很方便的设计我们的对话框,qt
的信号和槽等,关于QtDesigner的使用,限于篇幅,不再赘述.以前面的程序为基础.在kdevelop中选择”文件”->”新建”,选择Qt
Designer文件(*.ui).在文件名一栏中填写”mydialog”,最后点击确定按钮,即会启动QtDesigner,此处只拖了一个Label,写了一
些英文字符.对话框的name属性为Form1.将对话框mydialog.ui保存.
在控制台下进入你用kdevelop所生成的应用程序目录.
>lupdate Makefile.am
同样用”QT
linguist”来翻译qt1.zh_CN.GB2312.tr文件.此时linguist的context中会多出Form1的选项,这里面就是我们新建的对话框要翻译的
选项.依据前面的方法进行汉化.
>lrelease Makefile.am
生成qt1.zh_CN.GB2312.qm文件.
编译将会生成mydialog.cpp mydialog.h mydialog.moc文件.然后将main.cpp中的
Qt1App *qt1=new Qt1App();改为
Form1 *qt1=new Form1();
并将”mydialog.h” #include
进去.依据前面的方法更改tor.load中加载的翻译文件.编译运行,你的对话框也是中文的了.

3.其它的一些说明

由于qt返回的是Unicode编码,譬如你在LineEdit中直接输入中文,返回的就是??,qt中可以直接使用QTextCodec来转换字符串的编码.
QString string;
string=LineEdit1->text(); //取得LineEdit1返回的文字
QTextCodec *codec=QTextCodec::codecForName(“GBK”);
//转换编码
QCString
chinese_string=codec->fromUnicode(string);//用QCString来存储返回的多字节编码
当然,你在头文件中就必需加入
#include <qtextcodec.h>
#include <qstring.h>
#include <qcstring.h>

4.QTextStream对中文的支持

QTextCodec* codec = QTextCodec::codecForName(“GBK”); /* 当前编码为”GBK” */
QTextStream mystream(&file);
mystream.setCodec(codec);

5. QCString&QString
QCString中不以unicode编码
QString以unicode为编码.
qt内部使用unicode为编码,所以如果要在qt的部件如multilineedit中显示中文,则需要将非unicode的字符转换成unicode字符.
QCString locallyEncoded = “中国人不是东亚病夫”; // text to convert
QTextCodec *codec = QTextCodec::codecForName(“GBK”); // get the codec for GBK
QString unicodeString = codec->toUnicode( locallyEncoded );

http://www-128.ibm.com/developerworks/cn/l…oolkit/qt/i18n/

Qt 国际化编程
——————————————————————————–
内容:

1. Qt 的文本显示

2. Qt 的文本输入

3. Qt 的打印

于明俭

2002 年 1 月 09 日

本篇讨论 Qt 库对国际化的支持,将介绍 Qt 对文本显示,输入和打印的支持,和如何 使用Qt 开发国际化的软件。
Qt 目前的版本(2.2.4)对国际化的支持已经相当完善。 在文本显示上,Qt 使用了Unicode 作为内部编码,可以同时支持多种编码。 为 Qt 增加一种编码的支持也比较方便,只要 增加该编码和Unicode的转换编码便可以了。 Qt 目前支持ISO标准编码ISO 8859-1, ISO 8859-2,ISO 8859-3,ISO 8859-4,ISO 8859-5,ISO 8859-7,ISO 8859-9,和 ISO 8859-15(对于阿拉伯语和希伯来语的支持正在开发之中),中文GBK/Big5,日文 eucJP/JIS/ShiftJIS,韩文eucKR,俄文KOI8-R。 当然也可以直接使用UTF8编码。

Qt 使用了自己定义的Locale机制,在编码支持和信息文件(Message File)的翻译上弥补了目前Unix上所普遍采用Locale和gettext的不足之处。 Qt 的这种机制可以使 Qt 的同一组件(QWidget)上同时显示不同编码的文本。 比如,Qt 的标签上可以同时使用中文简体 和中文繁体文本。

在文本输入上,Qt 采用了XIM(X Input Method)标准协议,可以直接使用XIM输入服务器。由于目前的绝大多数输入服务器都是针对单一语言的,所以在 Qt 的标准输入组件( QLineEdit,QMultiLineEdit)中的输入受到单一编码的限制,Qt 还不支持动态切换编码输入的支持,这是它的不足之处。

1. Qt 的文本显示
——————————————————————————–
像普通的国际化过程一样,Qt 使用了类似GNU gettext一样的函数 QObject::tr(),它 用于从Qt的信息文件 .qm 中取出信息,这些信息是经过 Qt 的工具处理的。 Qt在处理 编码时还使用了 QTranslator 类,可用于指定整个应用软件的 的信息文件。

使用 Qt 编写国际化的程序,最好不要在程序中直接使用特殊编码的文本。 比如要 编写一中文界面的 Qt 程序,应该在程序中使用英文,程序编写完成后,把文本提取 出来翻译。 这样,程序还可以根据Locale的不同,支持多种语言。 下面介绍如何在 Qt 程序中标注字符串,如何提取并翻译文本。
下面是一段使用了 QObject::tr()的代码,它建立了一个弹出菜单,菜单项是”Quit”, 它被放置在菜单条上,在菜单条上显示的是标签”File”。

QPopupMenu* popup;
popup = new QPopupMenu( this );
popup->insertItem( tr(“&Quit”),qApp,SLOT(quit()) );
menubar->insertItem( tr(“&File”),popup );

对于绝大多数情况,可以用上述方法处理。不过有时在定义某些变量中使用的字符串,不能使用上述方法,但是为了让Qt提取并翻译该字符串,必须用某种方法标志出 来。Qt 定义了 QT_TR_NOOP() 和 QT_TRANSLATE_NOOP() 来标志它们。前者用于单个字符串,后者用于多个字符串。比如,

static const char* strings[] = {
QT_TR_NOOP( “Hello” ),
QT_TR_NOOP( “World” )
};

有时需要使用printf/sprintf之类的函数动态生成字符串,比如,

QStings s;
s.sprintf( “Button %d”,i );
but->setText( s );

对这种使用方式的国际化是使用 arg() 函数。

QString s = tr( “Button %1” ).arg(i);
but->setText( s );

提取上述信息的方法是使用 Qt 提供的工具 findtr 命令:

findtr [filename].cpp > i18n.po

它类似于GNU的 xgettext,上述文件的提取信息文件内包含,

….
“Content-Type: text/plain; charset=iso-8859-1\n”

#: i18n.cpp:34
msgid “ExampleWidget::&File”
msgstr “”

接下来是文本翻译过程。 在Qt中翻译信息文件时应该注意以下事项: (1) 提取的信息文件的编码是iso-8859-1,在翻译成某种语言(编码)时应该 注意改动它的字符集,比如对中文GB2312和Big5编码,应该是, “Content-Type: text/plain; charset=gb2312\n”或者”Content-Type: text/plain; charset=big5\n”。 (2) 提取的信息有一个范围,比如上面的文件指定的范围是 ExampleWidget, 在翻译 前应该把它去掉,变成 msgid “::&File”。(3) 被翻译的字符串可能含有加速键符号,如 “&File”中的”F”,如果翻译成中文最好保留该信息,它可以翻译成 “文件(&F)”。

对于翻译后的文件(比如上面的翻译文件存为 i18n_gb.po),必须使用 Qt 提供的 工具 msg2qm 把它转换为 .qm 文件才能使用,

> msg2qm i18n_gb.po i18n_gb.qm

它类似于GNU的 msgfmt 命令。翻译后的文件可以用Qt程序直接调用。

QTranslator *translator = new QTranslator(0);
translator->load(“i18n_gb.qm”,”.”);
qApp->installTranslator(translator);

此外,Qt 还提供了类似于 msgmerge 的工具 mergetr,它用于把新提取的信息 文件和已经翻译过的信息文件融合起来,在此不再赘述。

在 Qt 中也可以直接使用 QTextCodec 来转换字符串的编码,这为在Qt下开发纯 中文软件带来了便利条件,不过这种方法不符和国际化/本地化的习惯,

char *string = “中文和English混和字符串!”
QTextCodec* gbk_codec = QTextCodec::codecByName(“GBK”);
QString gbk_string = codec->toUnicode(string);
QLabel *label = new QLabel(gbk_string);

如果使程序只支持一种编码,也可以直接把整个应用程序的编码设置为GBK编码, 然后在字符串之前 加tr(QObject::tr),

qApp->setDefaultCodec( QTextCodec::codecForName(“GBK”) );
QLabel *label = new QLabel( tr(“中文标签”) );

如果使Qt根据Locale的环境变量取得字符集,可以使用 QString::fromLocal8Bit(str)。

本节的例子请参见 qt-i18n-example.tar.gz

2. Qt 的文本输入
——————————————————————————–
在输入方面,Qt 的输入条(QLineEdit)和编辑区(QMultiLineEdit)都支持 XIM,只要配合相应的输入服务器,便可以输入中文/日文/韩文。目前有许多支持XIM的软件,比如 中文: Chinput/xcin/rfinput/q9,日文: kinput2/skkinput,韩文: ami/hanIM。

Qt程序的缺省输入风格是OverTheSpot风格,它也支持 OffTheSpot风格和 Root风格。 用户可以在起动程序时在命令行指定输入风格,比如对程序app,

./app -inputstyle overthespot #缺省风格,光标跟随
./app -inputstyle offthespot
./app -inputstyle root

经过 MiziLinux 补丁的Qt-2.2.0 支持 OnTheSpot 输入风格,并且把它作为 缺省的输 入风格。请参见 http://www.mizi.com/ko/kde/doc/onthespot/onthespot.html。

Qt 中的任何一个 Widget 都可以接受输入,只要它可以有键盘聚焦(Keyboard Focus)。所以对特殊 Widget 的输入处理只需要截获键盘输入,获取从XIM服务器 来的字符串。 对于OverTheSport风格的支持,刷新XIM输入服务器的位置即可。

3. Qt 的打印
——————————————————————————–
在打印方面,XWindow下的 Qt 生成PostScript并使用lpr打印。 它含有QPrinter类, 可以方便地支持输出页面的控制。 对于中文打印,必须修正PostScript文件的输出 部分。

TOpia中文化
——————————————————————————–
一:字符集介绍

我国已经颁布了多种中文信息编码标准,常用的有:GB2312-1980、GB12345、GB13000(GBK)以及最新标准GB18030,其中 GB13000是对GB2312的扩展,又常被成为GBK,GB18030向下兼容GB2312和GBK,中文WINDOW98、中文WIN2000操作系统采用的中文字符集是GB2312。

GB2312字库仅覆盖双字节部分,存储位置索引是编码中每字节的第8bit置0得来的,如A1A1编码汉字在字库中的索引是2121,而非A1A1。以下是它的编码规则:

单字节:00~7F

双字节:A1~F7 A1~FE

GBK的编码规则是:

单字节:00~7F

双字节:81~FE 40~7E

80~FE

GB18030是最新的汉字编码标准,其编码为一、二、四变长编码:

单字节:00~7F

双字节:81~FE 40~7E

80~FE

四字节:81~FE 30~39 81~FE 30~39

Unicode编码采用等长编码,二个字节表示一个字符编码,对于ASCII码也采用双字节来表示,unicode使用二维空间来描述编码空间,平面分为256行、256列,对应于编码的高低字节。

二:Qt 国际化编程

在文本显示上,Qt 使用了Unicode 作为内部编码,为了程序的国际化,通常我们在文本显示的地方不直接输入本地字符,用英文代替,比如要编写一中文界面的 Qt 程序,应该在程序中使用英文,程序编写完成后,把文本提取出来翻译。对于需要翻译的地方,首先是在该文本处用tr()函数标识,同时制作出.qm信息文件,并在程序中加入QTranslator即可。比如我们在某一程序中有如下语句:

setCaption(tr(“main window”));

为了能显示中文,有两种方法:

方法一:
——————————————————————————–
修改工程文件,加上TRANSLATIONS = xxx.ts

lupdate 工程文件名

用linguist编辑刚生成的xxx.ts文件并保存

lrelease 工程文件名 xxx.qm

在main.cpp中加入QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);

qApp->setFont(font1);

QTranslator *translator = new QTranslator(0);
translator->load(“xxx.qm”,”.”);
qApp->installTranslator(translator);

方法二:
——————————————————————————–
findtr 文件名(通常为CPP文件) > xxx.po

编辑po文件,其中charset需由iso-8859-1改为GB2312,然后将“main window”翻译成“主窗口”

msg2qm –scope zh_CN.GB2312 xxx.po xxx.qm

在main.cpp中加入QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);

qApp->setFont(font1);

QTranslator *translator = new QTranslator(0);
translator->load(“xxx.qm”,”.”);
qApp->installTranslator(translator);

方法三:
——————————————————————————–
有时我们只是提供给本地用户使用,无需国际化,QT提供这一支持,在QT中有许多本地字符集同unicode的转换引擎,他们皆为QTextCodec的派生类,如QGbkCodec、QJisCodec, QHebrewCodec等。如:

QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);

qApp->setFont(font1);

QString caption=“主窗口“;

QTextCodec *gk_codec=QTextCodec::codecForName(“GBK”);

setCaption(gk_codec->toUnicode(caption));

从上面可以看出,使用转换引擎可以轻松实现中文显示,简要步骤如下:

1:修改main.cpp文件,将字体改为unifont

QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);

qApp->setFont(font1);

2:在想汉化的内的头文件中加入QTextCodec指针变量和转换函数QString mytr(char *)

#include <qtextcodec.h>

QTextCodec* gbk;

QString mytr(const char *);

3:在想汉化的类的实现文件中,修改类构造函数,加入:

gbk=QTextCodec::codecForName(“GBK”);

4:在想汉化的类的实现文件中,添加mytr函数代码

QString Form1::mytr(const char* chars)

{

return gbk->toUnicode(chars,strlen(chars));

}

5:在想汉化的类的实现文件中,用“mytr”替换“tr”

2004-12-2 测试了上面的方法三,编译通过,如果将codec成员变量改成QTextCodec派生类变量,编译将通不过,比如将QTextCodec* gbk;改成QGbkCodec* gbk;编译将报告此处有语法错误。下面是相似的用法:

1:修改***.cpp文件,在顶部加入codec头文件

#include <qgbkcodec.h>

2:在***.h文件中,加入mytr()函数声明

QString mytr(char* buffer,int size);

3:在***.cpp文件中,加入mytr()定义

QString mytr(char* buffer,int size)

{

QGbkCodec* gbk=QTextCodec::codeForName(“GBK”);

return gbk->toUnicode(buffer,size);

}

4:在需要显示中文的地方,使用mytr函数即可

5:修改main.cpp文件,将字体改为unifont

QFont font1(“unifont”,16,50,FALSE,QFont::Unicode);

qApp->setFont(font1);

备注:在翻译或转换之前必须将Unicode字体调入,否则显示不出中文,网上相关文章并未提及这一点,如果不显式装载该字体,系统默认的是Latin1,于是汉字显不出来。

备注2:在编译qt/embedded之前,必须修改qconfig-qpe.h配置文件的内容,将与TextCodec相关的宏定义给去掉,否则QTextCodec::codecForName(“GBK”)将返回NULL指针。

备注3:使用findtr命令时可同时查找多个文件的tr(),并将查找结果都放入一个文件内,源文件以空格隔开即可,另外,生成的.po和.qm文件的文件名最好与工程文件名相同!

备注4:如果要显示繁体中文,则需要使用QTextCodec::codecForName(“big5”)。获取本地的使用语言,用 QTextCodec::locale(),它返回Qstring变量,通常如果是中文本地的话,通常其值为zh_CN.GB2312和 zh_TW.Big5,根据这个返回字符串,可以加载相应的codec。如果程序只支持一种编码,也可以直接把整个应用程序的编码设置为一个默认的编码标准,比如系统只需要显示中文和英文,则可以直接设置应用程序的默认编码标准是GBK,如下使用方法:

qApp->setDefaultCodec( QTextCodec::codecForName(“GBK”) );

QLabel *label = new QLabel( tr(“中文标签”) );

备注5:如果使用本地的字符转换器,可以使用Qstring的静态函数Qstring::fromLocal8Bit(char* buffer,int size),将本地字符串转换成UNICODE字符串,不过要设置好LANGUAGE环境变量。

三:QTOpia中文化
——————————————————————————–
findtr 文件名 > xxx.po

编辑xxx.po文件

msg2qm –scope zh_CN.GB2312 xxx.po xxx.qm

拷贝可执行文件到QPEDIR/bin目录

拷贝xxx.po和xxx.qm文件到QPEDIR/i18n/zh_CN目录

进入QPEDIR/apps/Applications目录创建一新.desktop文件

iconv –f utf8 –t GB18030 xxx.desktop > xxx1.desktop

编辑xxx1.desktop文件,主要是修改Exec、Icon、Name和Name[zh_CN]四项

iconv –f GB18030 –t utf8 xxx1.desktop > xxx.desktop

rm –f xxx1.desktop

qvfb –depth 16 &

cd $QPEDIR/bin

./qpe

备注:如果你的系统中有多个qtopia版本,要特别注意QTDIR、QPEDIR、LD_LIBRARY_PATH环境变量

备注2:可按照此方法汉化qtopia自带的应用程序

备注3:po文件是中间文件,程序真正需要的是qm文件。iconv是系统自带的内码转换工具,它能将utf8编码的文件转换成gb18030编码的文件,反之也能,转换这一步必不可少,因为desktop文件缺省是utf8编码的,而我们的redhat linux 7。3中文操作系统用的却是gb18030,所以在编辑器打开前需转换。

四:汉化qtopia-1.5.0 for Arm
——————————————————————————–
首先按照“三”的方法对要汉化的程序提取*.qm文件和*.desktop桌面配置文件,在做完这一步后,将*.qm文件down到目标机的 /opt/qtopia-free-1.5.0.arm/i18n/zh_CN目录下,同时将*.desktop文件覆盖/opt/qtopia- free-1.5.0.arm/apps目录下的相应文件,目标机的环境变量配置如下:

export QTDIR=/opt/qt-2.3.3.arm

export QPEDIR=/opt/qtopia-free-1.5.0.arm

export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH

export PATH=$QPEDIR/bin:$PATH

export LANG=zh_CN

开机启动,由于设置的语言为简体中文,qpe会自动为每个应用加载Unifont字体,以及简体中文目录下的翻译文件,因而显示出中文,翻译文件是一个方面,但更关键的是要显示中文必须有中文字体来支持,Unifont字体包含中文字,因而能显示中文,没有相应字体的支持,光有翻译文件是显示不出中文的!

在经过以上的步骤后,有的应用在显示上依然会显示方框,这多半是由于该窗体相关的字体可能不是支持中文的字体,这需要直接修改源代码,以下是在汉化qtopia-free-1.5.0.arm的过程中的相关记录:

1. 在编译qtopia的过程中,可能会报告找不到SetButtonGroupID()函数,编译通不过。这是由于库中并没有该函数,一般情况下将该语句隐藏掉,原代码的原意是设置Button按钮在ButtonGroup组的序号,隐藏该语句对应用无影响。出现这个问题,主要在 /netsetup/dialup/dialupbase.cpp文件和/taskbar/shutdown.cpp文件,shutdown.cpp文件的相应行号是:96、149、201、253行。

2. 修改mpegplayer/playlistwidget.cpp文件的143行和166行,将字体设置改为Unifont,如下:Qfont(“unifont”,16,50,FALSE,QFont::Unicode)或者去掉该语句

3. 修改snake/interface.cpp的87行和186行,将字体设置改为Unifont,如下:Qfont(“unifont”,16,50,FALSE,QFont::Unicode)或者去掉该语句

4. 修改qasteroids/view.cpp的104行和qasteroids/toplevel.cpp的109行和165行,将字体设置改为 Unifont,如下:Qfont(“unifont”,16,50,FALSE,QFont::Unicode)或者去掉该语句

5. 修改sysinfo/versioninfo.cpp文件,将61行的builder改为作者本人,将50行的v改为tr(“corpname”),重新建立po文件,并翻译corpname为 您希望的名字,使用msg2qm生成qm文件

6. 修改桌面,位于taskbar目录下,生成libqpe.po和libqpe.qm文件,并将libqpe.qm文件拷贝到i18n/zh_CN目录下

五:qtopia目录结构
——————————————————————————–
apps/Applications:应用程序桌面配置文件

apps/Games:游戏桌面配置文件

apps/Settings:系统设置桌面配置文件

bin:二进制可执行文件

configs:编译配置文件目录

doc和docs:qtopia的参考文档

etc:应用配置文件目录

i18n:国际化目录

i18n/zh_CN:简体中文目录

include/qpe:与qtopia相关的头文件目录

inputmethods:输入法

library:qtopia部分源代码目录

pics:与应用相关的图片存放目录

plugins:各种插件目录,如mpeg3解码插件、输入法插件等

sounds:音频文件存放目录

taskbar:桌面程序的源代码(qpe的源代码)

GnuWin by gnuwin32

Posted by – January 3, 2010

http://sourceforge.net/projects/gnuwin32

GnuWin provides Win32-versions of GNU tools, or tools with a similar open source licence. The ports are native ports, that is they rely only on libraries provided with any 32-bits MS-Windows operating system, such as MS-Windows 95 / 98 / 2000 / NT / XP /

亲手打造 GNU/Linux 中文环境 (五) – 讯息国际化的解决方案 (gettext 简介)

Posted by – December 27, 2009

亲手打造 GNU/Linux 中文环境 (五) – 讯息国际化的解决方案 (gettext 简介)

参照: http://jinlab.com/blog/?attachment_id=97

转载自: http://cle.linux.org.tw/xcin/i18n/pc2000/p5/
感谢 http://babelfish.yahoo.com/ 提供翻译.
谢东翰 <thhsieh@linux.org.tw>,小虫 <platin@ms31.hinet.net>

1 前言

在前几期的文章里面,我们花了不少篇幅跟大家介绍了 GLIBC 提供的国际化支援以及 locale 的基本结构,我们也提到过 “LC_MESSAGES” 这个环境类别会决定程序用何种语言来显示讯息,在这一期里面,就让我们来更深入的研究一下讯息多国语言的解决方案,看看在一个多国语言环境之中,要如何让同一个程序在不同 locale 环境下可以自动显示不同语言的讯息。

1.1 讯息国际化的问题

有写过程序的人都知道,一般情况下程序的讯息都是写死在程序码里面的,例如说:

printf(“Hello World!!”); 亲手打造 GNU/Linux 中文环境 (五) – 讯息国际化的解决方案 (gettext 简介)

谢东翰 <thhsieh@linux.org.tw>,小虫 <platin@ms31.hinet.net>

1 前言

在前几期的文章里面,我们花了不少篇幅跟大家介绍了 GLIBC 提供的国际化支援以及 locale 的基本结构,我们也提到过 “LC_MESSAGES” 这个环境类别会决定程序用何种语言来显示讯息,在这一期里面,就让我们来更深入的研究一下讯息多国语言的解决方案,看看在一个多国语言环境之中,要如何让同一个程序在不同 locale 环境下可以自动显示不同语言的讯息。

1.1 讯息国际化的问题

有写过程序的人都知道,一般情况下程序的讯息都是写死在程序码里面的,例如说:

printf(“Hello World!!”);

这一段 C 程序码会印出 “Hello World!!” 这个字串,假如要把程序的讯息改成其他的语言的话,必须要修改原始码,然后再重新把程序编译一遍才行,这样的作法在国际化的系统里面是行不通的,因为一个国际化的系统应该要有能力随时依照使用者设定的语言环境 (就是 LC_MESSAGES 的内容) 来选取要输出讯息的版本,也就是说,用同一份程序码就可以显示不同语言的讯息,才能称之为国际化的系统。GLIBC 虽然提供了 setlocale() 的界面以及 LC_MESSAGES 的环境类别,不过却没有定义讯息多国语言的实作方式,所以要做到讯息的国际化,必须得要使用 GLIBC 以外的函式套件才行。

由于在 C 函式库中并没有标准的定义,所以实际上在 GNU/Linux 里面进行讯息国际化的解决方案并不只一种,常见方式可以归类成下面三种:

1. 利用 X 的资源定义资料库 (X resource database) 来作讯息国际化

这是 X 视窗系统提供的标准方法,许多 X 应用程式都是用这种方法来作讯息的国际化的,例如 Netscape 或 xedit 等程序就是属于这一类;X 允许您将程序使用到的讯息存放在 X 的资源资料库 (resource database) 里面,由于 X 应用程式在执行时会依照 locale 设定到 /usr/X11R6/lib/X11/{locale}/app-defaults/ 目录下去寻找对应到该 locale 的资源定义档案,所以只要把讯息字串定义在资源定义档案里面,把这些字串翻译好,然后再放到该 locale 的 app-defaults 目录底下,就可以依照 locale 设定显示不同语言的讯息了,读者可以在 /usr/X11R6/lib/X11/app-defaults/ 目录下找到一些未翻译的资料档案。假如您有加装一些中文套件的话,很可能还可以在 /usr/X11R6/lib/X11/zh_TW.Big5/app-defaults/ 目录下找到 Netscape 的繁体中文定义档,这里面以 labelString 结尾的资源项目就是 Netscape 程序用到的讯息,读者可以自己参考看看。

2. 利用 GNU gettext 套件

gettext 套件是 GNU 对讯息国际化提出的一个统一的解决方案,只要程序写作时注意几点小事情,对程序码做好适当的修改并链接到 gettext 程序库,编译一次以后,不须重新编译就能显示多国的讯息,让国际化 (i18n) 的工作可以更简单的达成;因为使用简单,支援的程序工具也多,gettext 已经渐渐成为 GNU/Linux 上面讯息国际化的标准作法,像 GNOME 程序、KDE 程序等等都是用 gettext 来作讯息的国际化。

3. 应用程式自行定义的方法

某些应用程式有自己的一套讯息国际化的架构,像 linuxconf 就自己定义了一套方式来作讯息的国际化;这些方法通常跟 gettext 的基本架构很类似,作法的流程也有很多可以类比之处,不过这都不是标准的作法,自己重新发展一套讯息资料的处理方法也是一件费时费力的工作,过去 GNU/Linux 上面的 gettext 还不成熟的时候或许有需要,不过在目前 gettext 已经被广泛接受使用的情况下,通常是没必要自己再重新发明一遍轮子罗。

上面所述的三个方法里面,X 提供标准出现的最早,但是因为在程序设计阶段就得自行将讯息分离处理,用起来并不方便,也缺乏一些工具程序的支援,因此迟迟未能受到大家广泛使用。在 GNU gettext 已经逐渐成熟以后,多数的程序也不再用这个方法。目前 gettext 套件已经算是被大家接受的标准作法了,虽然它现在的版本还只是 0.10 版,但是对于讯息国际化的支援 (LC_MESSAGES) 已经算是稳定,再加上几乎所有的 GNU 程序也都支援 gettext ,因此现在把它当作一个标准,来作 Linux 上讯息中文化的工作,应该是一个适当的时机,我们也建议所有有心让自己程序显示多国语言的程序设计者都能善加利用 gettext 套件,因此本文讨论的对象会专注在 gettext 这个套件的使用方式之上,X 或其他程序的作法我们就不详谈了。

接下来让我们先来看看 gettext 的基本原理吧。

2 gettext 的使用

2.1 gettext 的工作原理

图 1 是从 GNU 的 info page 抄出来的,它总括说明了 gettext 套件的工作原理,就请读者们参照后面的说明以及这张图上的流程,一起来看看 gettext 套件的工作流程吧。

Figure 1: gettext 的工作流程图
\resizebox*{11.7cm}{8.26cm}{\includegraphics{gettext-flow.eps}}

2.1.1 修改程序码

我们前面提过,使用 gettext 套件的时候原始码要作一些适当的修改,图 1 里面的 “Source Code” 指的就是还没有经过任何修改的程序码,”Marked Source Code” 指的就是修改后的程序码,那么,要作些甚么修改呢?

要让你的程序支援讯息国际化,程序设计师在写作程序的时候就必须把有需要国际化的讯息标出来,这个时候该注意的是应该只标示出需要被翻译成他国文字的讯息,比如 “%s %d” 这样的字串是不必被翻译的,也就不必标示,而 “Hello World!!” 这类的说明讯息就需要标示出来,标示的方法如下:

printf(gettext(“Hello World”));

懂 C 的朋友应该可以看出这个标示的意义,程序码这样修改以后,程序在印出讯息时,并不是直接取得讯息的字串,而是透过一个 gettext() 函式呼叫来获得字串;这个 gettext() 函式是在 libintl.h 里面定义的,它会在程序真正被执行时检查 locale 的情况 (事实上是在程序开始时先呼叫 setlocale() 函式来设定 LC_MESSAGES 类别,在这一步程序会去检查 LC_ALL 或 LC_MESSAGES 或 LANG 这环境变数,并做好 locale LC_MESSAGES 类别的设定),然后以 gettext() 的参数 ( “Hello World” ) 作为一个 id key,去该 locale 底下的讯息资料库里面抓取合乎这个 id key 的讯息,这样子同一个程序,就可以 show 出许多不同语言的讯息了。

从 “Source Code” -> “Marked Source Code” 这一段修改程序码的流程,是程序设计师的工作,也是程序讯息多国语言的过程之一,更详细的修改步骤我们在第 3.1 节里面会用实例跟大家介绍,且留待后面再讨论吧。

2.1.2 制作 PO 档案

改好程序码以后,接着应该就要把讯息从程序码里面抽出来,制作出 “讯息资料库” ,那讯息资料库是甚么?要怎么作呢?

GNU gettext 套件里面有一个程序叫 xgettext,可以用来处理 Marked Source Code,把原始码里面有标记的讯息抓出来,产生 PO 档 (Portable Object),xgettext 的基本使用如下:

xgettext [选项] INPUTFILE …

INPUTFILE 就是 “Marked Source Code”,假如给定的输入档名为 `-‘ 的话,则程序会从标准输入读入资料,有多个档案需要处理的话,在后面列入就可以了,这个程序常用的选项有:

-d NAME
用 NAME.po 做为输出档名 (预设是  messages.po)
-D DIRECTORY
增加 DIRECTORY 到档案搜寻列表中
-f FILE
从档案 FILE 里面读取输入档档名的列表。
-keyword=WORD
其他用来标示的关键字 (若没有指定 WORD , 表示使用预设的关键字 gettext)。
-o FILE
把产生的结果写到 FILE 这个档案去。
-p DIR
把输出的档案放到 DIR 这个目录底下。
-s
制造排序妥当的输出,并移除重覆的栏位。
-h
显示说明讯息。

2.1.3 对 PO 档进行翻译

xgettext 产生的 .po 档案是一个标准的文字档,用 more 或者一般的文字处理程序就可以观看它的内容,它里面包含了xgettext 从输入的原始码档案里面抓出的所有讯息,里面每一项讯息的格式大约如下:

#: hello.c:22
msgid “Hello!!”
msgstr “”

这里面 ‘#’ 开头的行是注解,msgid 栏位是未翻译前原先的讯息,msgstr 就是用来填翻译过的讯息的,我们可以把翻译过的讯息填进去,如下:

#: hello.c:22
msgid “Hello!!”
msgstr “您好!”

如何,翻译的动作非常简单吧;翻译 PO 讯息并不需要有特别的工具,只要用一般的文字编辑器就可以了,此外由于 PO 档案是单纯的文字档,所以跟使用的平台无关,您甚至可以把 GNU/Linux 程序的 PO 档案拿到 Microsoft Windows 操作系统里面用 Notepad 编辑、翻译好再拿回来 GNU/Linux 底下使用,这也就是 PO 的全名 “Portable Object” 的意思。经过使用者把每个项目里面的讯息一一翻译完成的 PO 档,只要档用 msgfmt 编译以后摆到适当的地方,就可以让翻译过的讯息发生作用,接着我们就来看看怎么让 PO 档案生效吧。

2.1.4 编译并安装讯息资料库

作好 PO 档以后,就可以用 “msgfmt” 这个程序来把 PO 档变成真正机器可以处理的 .mo 档 (Machine Object):

msgfmt [选项] filename.po …

msgfmt 常用的选项有:

-o FILE
把产生的结果写到 FILE 这个档案去,FILE 的档名一般会以 .mo 结尾,没有指定的话,结果会写到 messages 这个档案里面。
-v
列出输入档中异常的部份,多用几个选项 -v 的话会显示更多的讯息。
-h
显示说明讯息。

用 msgfmt 产生出来的这个 .mo 档才是真正的讯息资料库档案,它的名称一般是跟应用程式的名称一致,例如 gedit 的讯息就叫做 “gedit.mo”,在程序安装的时候,这些讯息资料档应该要被放到该讯息对应的 LOCALE 目录里面去,一般而言就是在 /usr/share/locale/*/LC_MESSAGES/ 底下 (这里的 * 指的就是 locale 的名称),例如繁体中文的 locale 为 zh_TW.Big5,所以繁体中文的讯息就应该放在 /usr/share/locale/zh_TW.Big5/LC_MESSAGES/ 底下,以 GB2312 编码的简体中文讯息就该摆在 /usr/share/locale/zh_CN.GB2312/LC_MESSAGES/ 底下。如此一来,各种不同语文翻译的讯息就能够很自然的分开摆放不相冲突,在应用程式执行的时候,gettext 也就可以依照 locale 设定的不同而去不同的目录底下抓到适当的讯息翻译。下面这行指令可以列出您目前系统上安装的程序的各种不同语言的讯息资料档,读者不妨自己看一看您的 GNU/Linux 上面有多少程序已经有中文翻译罗:

ls /usr/share/locale/*/LC_MESSAGES/

2.1.5 合并旧有的翻译讯息

在程序经历过几次改版以后,很自然的会加入新的讯息或修改旧的讯息,需要重新产生 PO 档案并进行翻译,不过这时候其实很多讯息都已经翻译过了,有旧版的 .po 讯息档,我们当然不希望把已经翻译过,而且还可以用的讯息再打一次,此时就可以用 “msgmerge” 这个程序来把旧的 .po 档里面的讯息合并到新的 PO 档案里面:

msgmerge [选项] def.po ref.po

常用的选项有:

-i
对输出结果做缩排处理。
-o FILE
把输出的结果写入 FILE 这个档案。
-S
使用严格统一标准的输出结果。
-h
显示说明讯息。

上面 def.po 是旧的翻译过的 PO 档,ref.po 是新的 PO 档案 (xgettext 做出来的),在 def.po 里面翻译过的讯息只要是仍然找得到相符合栏位的,都会被合并到 ref.po 里面去,在找不到完全一致的栏位时,程序还会用模糊逻辑的方法来得到类似的翻译,这些用模糊逻辑合并进去的栏位会被标上一个 “#, fuzzy” 的记号,根据我们实际的经验,这些模糊逻辑对出来的翻译常常是错的,读者在进行翻译的时候要特别重新检查这些栏位才行。

2.2 程序设计师与讯息翻译者的分工

在图 1 的流程里面,大家可以看到 xgettext 产生的档案叫做 .pot,这是 Portable Object Template 的意思,表示刚产生的档案里面只有原来的讯息,还没有翻译。接下来有一个 .pox 档,这是 GNU 建议对还没完全翻译好的PO 档的称呼,意思是施工中的 PO 档案,等到翻译完工定案了,再把它改成 .po 档;.pot、.pox、.po 这三种档案都是 PO 档,格式是一模一样的,在图里面作这种不同的称呼只是为了方便区分翻译的状况罢了,没有特殊的意义,读者也不见得一定要遵守这个命名规则的。

图中的 .gmo 其实指的就是用 msgfmt 编译出来的 .mo 档,GNU 建议用 .gmo 来称呼还没安装到定位的 .mo 档,表示这个档案是由 GNU gettext 套件产生的,这个命名法在许多 GNU 程序套件里面都可以看到;从程序码到翻译到安装的整个流程还可以用档案的附加档名慨括如下:

source code -> .pot -> .pox -> po -> .gmo -> .mo -> <run time>

在这整个流程里面,写作程序跟制作 .pot 档案是属于程序设计者的工作,然而 PO 档案的翻译却不一定要是程序设计者本人来作,事实上写程序的人也不可能懂得各国的语言,这些翻译跟维护 PO 档案的工作是属于 L10N 的范畴,应该由世界各地使用不同语言的人来担任翻译的工作才是。作这些翻译工作的人只要是单纯的使用者、懂英文、会使用文字编辑程序就可以了,并不需要懂得程序设计,因此人人都可以参与,gettext 套件提供了一个程序设计师跟讯息翻译者可以分工的架构,在图 1 里面深色的部份表示的就是属于讯息翻译者可以发挥的地方,外围浅色的部份则是程序设计师的工作,由程序设计者制作出 .pot 档案以后,有兴趣翻译的人就可以去抓回来翻译,翻好以后再寄回去给程序设计者包在套件里面发行,下一节我们先用一个例子实际让有心利用 gettext 写作多国语言程序的读者可以有个实作的机会,接着在第 4 节里面我们会从单纯使用者跟翻译者的角度来看 gettext 套件,请读者依照您个人的 “志愿” 选择有兴趣的部份来阅读吧。

3 一个实际的例子

上一节里面我们对整个 gettext 工作的流程做了简单的说明,接下来我们就来写一个简单的程序,希望能够利用这个完整的例子来让读者了解利用 gettext 套件写作 I18N 程序的详细方法。

3.1 使用 gettext 相关的函式

要利用 gettext 套件写作 I18N 程序,第一件事当然是要把 getext 套件安装好,现在所有的 GNU/Linux 安装套件里面应该都有内含这个套件了,因此安装应该不困难,读者只要从光盘或 FTP 站上面把 gettext 套件抓回来装好就可以,在此就不赘述了;除了 gettext 之外,你还必须有支援 I18N 的 LIBC,这一点读者可以查看一下 /usr/include/ 底下是不是有 locale.h 以及 libintl.h 这两个档案,有的话就表示你的 LIBC 是支援 I18N 的,这在目前各家 GNU/Linux 安装套件厂商使用的 glibc-2.1.x 里面是不会有问题的。要写一个讯息国际化的程序,需要用到底下这两个 C 函式:

char *setlocale(int category, const char * locale);

extern char *textdomain(const char *domainname);

上面的 setlocale() 是用来设定区域化环境的,用法在前几期已经详细介绍过,这边就不赘述。textdomain() 这个函式接受一个字串当作参量,用来指定这个程序所使用的 “文字领域” (textdomain),textdomain 会决定以后的 gettext() 函式要到那个档名的档案里面去找讯息资料,gettext() 会在程序执行时到 domainname.mo 这个档案里找字串的翻译,一般而言这个 domainname 都设成和程序的名称一样的以避免不同程序的讯息档案相互冲突。

3.2 范例程序

写作应用程式时要用 gettext 来作讯息多国语言的步骤其实非常简单,只有以下几点:

1. 在程序开头引入 libintl.h 跟 locale.h 两个标头档。
2. 在主程序开头时用 setlocale() 设定区域化环境。
3. 用 textdomain() 设定文字领域。
4. 把程序码中所有需要翻译的讯息都包在 gettext() 函式里面。

底下就是一个完整的程序,我们把它存在 gettext_test.c 里面,读者不妨先一边看程序码,一边观察上面几个步骤出现的位置:

/******************** Example program start here ********************/
/* gettext_test.c by Yuan-Chung Cheng */
#include <stdio.h>
#include <locale.h>
#include <libintl.h>
#define _(STRING) gettext(STRING)
#define PACKAGE “gettext_test”

char *program_name;

main(int argc, char *argv[])
{
program_name=argv[0];
setlocale(LC_ALL, “”);
textdomain(PACKAGE);

if(argc < 2){
printf(_(“%s: too few arguments\n”), program_name);
exit(0);
}
printf(_(“Hello, %s.\nBe a Happy Linuxer!!\n”), argv[1]);
}

/******************** Example program end here ********************/

这个程序的流程极简单,只是在一开始多了 setlocale、设定 textdomain 两个步骤而已,读者应该不难在这个短短的程序里面看出配合 gettext 修改的部份,值得特别注意的是,在这个程序里面我们把 _() 定义成 gettext(),然后再用 _() 来标示需要翻译的字串,这个简化的用法是 GNU 应用程式里面惯用的,可以让程序设计师少打很多 gettext(),也可以增加程序的可读性,建议大家可以学起来。

3.3 产生 .pot 档案

有了程序码以后,就可以利用 xgettext 程序生成 .pot 档案:

xgettext -keyword=_ -o gettext_test.pot gettext_test.c

前面的程序码里面用 _() 代替了 gettext() 函式,所以在用 xgettext 抽出讯息的时候要多加一个参数 “-keyword=_”,告诉程序用 _() 标示的字串也要当成待翻译的讯息抽出来,存到档案 gettext_test.pot 里面去,以下就是产生的 .pot 档案的内容:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid “”
msgstr “”
“Project-Id-Version: PACKAGE VERSION\n”
“POT-Creation-Date: 2000-06-19 05:51+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: ENCODING\n”

#: gettext_test.c:21
#, c-format
msgid “%s: too few arguments\n”
msgstr “”

#: gettext_test.c:25
#, c-format
msgid “”
“Hello, %s.\n”
“Be a Happy Linuxer!!\n”
msgstr “”

3.4 翻译 PO 档案并安装

得到 .pot 档案以后就可以进行翻译的工作了,这个程序只有两行讯息,当然轻轻松松三两下就翻译完毕了:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2000 Free Software Foundation, Inc.
# Yuan-Chung Cheng <platin@ch.ntu.edu.tw>, 2000.
#
msgid “”
msgstr “”
“Project-Id-Version: hello-0.1\n”
“POT-Creation-Date: 2000-06-18 05:51+0800\n”
“PO-Revision-Date: 2000-06-19 08:30+0800\n”
“Last-Translator: Yuan-Chung Cheng <platin@ch.ntu.edu.tw>\n”
“Language-Team: Chinese <zh@li.org>\n”
“MIME-Version: 1.0\n”
“Content-Type: text/plain; charset=big5\n”
“Content-Transfer-Encoding: 8-bit\n”

#: gettext_test.c:21
#, c-format
msgid “%s: too few arguments\n”
msgstr “%s: 参数太少了\n”

#: gettext_test.c:25
#, c-format
msgid “”
“Hello, %s.\n”
“Be a Happy Linuxer!!\n”
msgstr “”
“您好, %s. \n”
“好好玩 Linux 喔!!\n”

上面便是完成翻译以后的 PO 档案,读者要注意除了翻译讯息以外,还要在一开头的标头部份把您的大名、E-Mail、翻译的编码等等栏位填一填才行。完成的 PO 档案可以用下面的指令编译成 .gmo 档案:

msgfmt -v -o gettext_test.gmo gettext_test.po

接着把完成的讯息资料库安装到定位,我们现在有的是繁体中文的翻译结果,所以安装在 zh_TW.Big5 目录底下:

install -m 644 gettext_test.gmo /usr/share/locale/zh_TW.Big5/LC_MESSAGES/gettext_test.mo

装好之后把原来的原始码用 gcc 编译起来,然后改一下 LANG 或 LC_MESSAGES 的设定,执行看看,是不是已经能够成功显示多国的讯息了呢?图 2 是在不同 LANG 设定下执行本程序的结果,读者可以看到当 locale 设定改变的时候,程序会自动显示不同语言的讯息出来。要加上其他语言的翻译的话,只要把 PO 档翻译好,用 msgfmt 编译成 .mo 档案并安装到该语言 locale 的 LC_MESSAGES 目录底下就可以了,并不需要重新编译程序。

Figure 2: 依照不同 locale 设定显示不同讯息
\resizebox*{6.63cm}{4.1cm}{\includegraphics{gettext_test.eps}}

在这一节里面,我们为了说明的方便,举的例子只是一个小小的档案而已,读者们在发展自己的程序时可能会遇到比较复杂的情况,例如程序码档案可能会不只一个,而且分布在不同目录底下,这个时候在产生 PO 档案的过程会比较复杂,不过只要善用 GNU 的 autoconf 套件与 gettextize 程序来帮您处理程序码,就可以省下不少功夫。限于篇幅我们只能为大家介绍最简单的情况,深入的应用就只好麻烦大家直接参考 gettext 的使用手册了:

http://www.gnu.org/manual/gettext/html_mono/gettext.html

4 从翻译者的观点来看 gettext 工具

让应用程式支援 gettext 是程序设计师的工作,不过要让程序在执行时能够显示出各种语言的讯息,就有赖讯息翻译者的努力了。参与翻译讯息的工作需要的知识跟写作 I18N 程序没什么相关性,也不需要对程序设计的原理有甚么了解,在翻译时更重要的是对该应用程式的 “使用经验”,知道怎么将程序的原文讯息适切的用本国的语言来表达,除此之外,就剩下要懂得编辑文字档而已了,所以任何人都可以参与翻译讯息的工作,事实上目前在 GNU/Linux 底下的中文讯息档很多都是由自愿的热心网友翻译出来的,许多参与的人也都完全不懂得设计程序呢。

4.1 PO 档案的翻译

4.1.1 取得 PO 档案

要进行 PO 档案翻译的第一件事就是取得还没翻好的 PO 档,PO 档一般含在应用程式的原始码里面,只要使用到 gettext 套件的应用程式都会有一个名为 po/ 的目录,这就是摆放 PO 档案的地方,通常在这底下可以找到一个结尾为 .pot 的未翻译档,以及许多各种语言的翻译结果,抓 POT 档案回去翻译之前,读者最好先检查看看以前是不是有人翻译过这个讯息了,以繁体中文为例,先找看看 po/ 目录底下是不是有叫做 zh_TW.Big5.po 这个档案,有的话,就表示已经有人翻译过这个讯息了。

在没人作过翻译的情况下,请注意一下 POT 档案里面标头部份显示的产生时间,记得不要抓太旧的档案回去,以免浪费时间在翻译过时的讯息,必要的时候写封 e-mail 请负责维护程序码的人更新一下 .pot 档案的版本,或者干脆自己把整个原始码抓回来,用 make 更新 .pot 档案,再重头翻译就是了。

发现档案已经有人翻译过的话,请尽量先跟前一位翻译的人取得联系再动手翻译;在 PO 档的标头部份都有翻译者的 e-mail 位址,写封信去询问他是否要继续维护这个讯息,甚至加入他一起来合作维护,我想读者也不希望您在辛苦翻译到一半的时候突然又冒出另外一个版本,结果浪费时间作白工了吧。假如确定前人已经不再继续维护这个讯息翻译以后,您就可以放心的以原来翻译的基础继续翻译的工作罗,不过还是要更新一下讯息的版本,保持翻译最新的程序讯息,此时请先拿到新版的 POT 档案,然后用下面指令拿到最新版本的 PO 档:

msgmerge zh_TW.Big5.po package_name.pot > package_name.po

上面指令中 zh_TW.Big5.po 是旧的 PO 档案,package_name.pot 表示新的 POT 档案,msgmerge 程序会把 zh_TW.Big5.po 里面还能用的翻译跟 package_name.pot 里面的讯息定义合并起来,输出到 package_name.po 里面去,这个新的 PO 档案里面会保留有旧的翻译再加上新的讯息定义,请拿这个档案来进行讯息翻译的工作。

4.1.2 翻译讯息

拿到最新的 PO 档案以后就可以开启您最爱用的文字编辑程序来进行翻译的工作罗。PO 档案是单纯的文字档,不管您用甚么程序来编辑它都没关系,前面已经说过,甚至用 Windows 的 Notepad 来翻译都没有关系,虽说如此,我们在网络上还是可以找到不少个可以帮助您翻译工作的程序,读者们到 http://freshmeat.net 底下用 gettext 为关键字找一下就可以找到好几个,请读者们自己试试看罗。小虫倒是建议有使用 Emacs 的朋友试试看 PO mode,在 gettext 套件里面可以找到一个 po-mode.el 的 emacs Lisp 程序档,参照里面的说明装好以后,您的 Emacs 在读到 PO 档的时候就会自动转到 PO mode 里面,PO mode 让您可以更简单的输入翻译出来的讯息,更另外提供了一些操作讯息项目的指令让您使用,像讯息的复制、删除等等都可以轻易的做到,读者可以自己在 PO mode 底下按 ‘h’ 看说明讯息来学习 PO mode 的使用方式,图 3 就是用 Emacs 的 PO mode 在编辑 PO 档案的情况。

Figure 3: Emacs 的 PO mode
\resizebox*{7.88cm}{7.1cm}{\includegraphics{gettext_emacs.eps}}

翻译时要记得填入标头资料,用 msgmerge 出来的档案进行翻译的时候要特别注意一下被标为 “#, fuzzy” 的项目,请务必检查一下这些项目,没问题的话要记得把 “#, fuzzy” 这一行删掉,否则接下来编译的时候会发生问题的。继承别人翻译成果的朋友请记得把标头资料改成自己,并且把前一位翻译者的资料放到一开始的版权信息里面,以示对前人翻译成果的尊重,并且也为这个档案的传承关系作一个见证。

在翻译时还有其他特别的事情需要注意,因为您在翻译的事实上是 C 语言的字串,所以一些讯息的写法得要依照 C 字串的规矩才行,例如字串中有引号 ‘”‘ 的时候要用打成 ‘\”‘ 才行,翻译出来的结果要保留原文讯息中的跳行字元跟 ‘%’ 带头的那些变数参考才行,例如下面的翻译:

#: locale/programs/ld-ctype.c:4131
#, c-format
msgid “%s: table for class \”%s\”: %lu bytes\n”
msgstr “%s: 类别 \”%s\” 表格: %lu 字节\n”

繁体中文翻译还会有一个额外的问题,因为许多中文字像许、功、摆… 等等的第二个字节是 ‘\’字元,这在 C 字串中会造成错误,因此在翻译遇到这些字的时候都要在后面多加一个 ‘\’ 才行,因为这个工作手动实在是太麻烦了,网络上有人写了一些小程序来处理这个问题,读者们可以在 http://i18n.linux.org.tw/ 找到 bg5cc 这个程序,用它来帮您加 ‘\’,我们在翻译的时候只要编辑 .pox 档案,不需要去担心这些中文字的问题,等到要编译成 .mo 档案的时候再来用 bg5cc 转一下:

bg5cc package_name.pox package_name.po

上面这个指令会帮您把 package_name.pox 里面需要多加 ‘\’ 的字找出来,把加上 ‘\’ 的结果存到 package_name.po 里面去,产生的这个 package_name.po 档案就可以放心交给 msgfmt 处理,不必担心中文字的问题罗。

4.1.3 测试翻译结果并送回给原作者

翻译好的 PO 档案可以用下面指令测试出是否正确无误:

msgfmt -v -o /dev/null package_name.po

例如:

[platin@bruceyu libc-zh_TW.Big5]$ msgfmt -v -o /dev/null libc-zh_TW.Big5.po 652 已翻译的讯息 , 496 未译的讯息 .

[platin@bruceyu libc-zh_TW.Big5]$

编译发生错误的话,msgfmt 会告诉您有问题的行号,您可以很快的找到错误所在;正确无误的 PO 档案就可以用第 3.4 节提到过的方法安装到 /usr/share/locale/… 底下,下次您开启这个程序的时候,就可以看到翻译的结果了。当然,独乐乐不如众乐乐,请记得把您翻译完成的 PO 档案用 e-mail 寄回去给程序的原作者,请他将这个讯息在程序下一次 release 的时候放进去,如此一来全世界所有使用这个程序的使用者都可以享受到您的贡献,也算是对 GNU/Linux 系统做出一番贡献喔。

由于程序是不停的在改版的,不时会有新的讯息加到程序里面,也时常会有修改原有讯息的情形,所以不要把翻译 PO 档案当成可以一次就搞定的工作,这些讯息翻译的持续维护是很重要的问题,讯息翻译者应该要时时检查自己负责的程序讯息是否有更新的情况,定时下载新的档案来更新、保持翻译结果的同步,这种持之以恒的持续维护也是很重要的喔。

4.2 翻译工作团体

翻译一个档案是很简单,可是要持续维护的工作就不容易了,因此世界各地都有热心人士出来组织翻译工作团体,希望可以简化 PO 档案的下载与更新工作以扩大参与的层面,更借着网站的协调机制避免有两人以上重复翻译同一份讯息,造成人力资源的浪费,还可以保存过去翻译的结果、持续 PO 档案翻译结果的维护工作。在台湾先是有李柏峰大哥 <pofeng.lee@ms7.url.com.tw> 出面担任 GNU 下游翻译组织 zh@li.org 的负责人,后有薛景中 <shyue@sonoma.com.tw>、陈更欣 <c17@linux.acer.com.tw> 等先进发起 “i18n 程序中文化计画” 来统合有兴趣参与翻译工作的力量,让有兴趣帮忙翻译的朋友可以透过这个网站取得最新的 PO 档案,也可以在这边找到翻译的技巧跟术语翻译的标准,图 4 就是这个网站的首页,其网址如下,有心进行翻译工作的朋友请务必进去看看:

http://i18n.linux.org.tw/

Figure 4: i18n 程序中文化计画首页
\resizebox*{10.16cm}{6.69cm}{\includegraphics{i18n_linux_org.eps}}

5 结语

看过上面的例子以后,读者是不是也觉得用 gettext 来让程序支援多国语言的讯息是很简单的一件事情呢?在当前软件国际化的风潮底下,让同一套程序码可以支援各种不同语言的讯息已经是写程序的人都必须面对的一件工作,GNU gettext 套件提供的架构可以让我们轻易的完成这件任务,有在 GNU/Linux 底下撰写应用程式的读者可要好好善加利用。

对单纯的使用者而言,如何用 gettext 来设计程序大概不是大多数人关心的问题,大家最在意的还是在使用程序时能不能看到讯息用自己熟悉的语言来显示出来吧?我们在网络上不时可以听到有人抱怨『GNU/Linux 底下怎么全都是英文,中文化那么差』,假如中文化指的是进去看不到中文讯息的话,那可就真的是冤枉了,其实缺少中文讯息这一点在技术上是最简单的问题,比起前几期讲的东西简单许多,而且还是任何人都可以一起来参与来改善的。可惜的是,跟其他国家的网友们参与的程度比起来,台湾朋友的热情似乎是少了一点,参与的人少,再加上国内的一些参与 GNU/Linux 市场的商业公司虽然都做过讯息翻译的工作,至今却还没能集中并持续的来维护中译的讯息。目前讯息中译的成果绝大多数都还是由网络上少数几个热心网友们贡献出来的,也因此目前 GNU/Linux 底下大多数的应用程式都还是没有中文讯息。我们在这边呼吁有心要改善这个问题的朋友或团体参考第 4.2 节里面的说明,一起来加入 i18n.linux.org.tw,只要看得懂英文、会编辑文字档案就可以帮 GNU/Linux 底下的应用程式加上中文的讯息,大家一起来,GNU/Linux 的明天会更好,您说是不是呢?

这一段 C 程序码会印出 “Hello World!!” 这个字串,假如要把程序的讯息改成其他的语言的话,必须要修改原始码,然后再重新把程序编译一遍才行,这样的作法在国际化的系统里面是行不通的,因为一个国际化的系统应该要有能力随时依照使用者设定的语言环境 (就是 LC_MESSAGES 的内容) 来选取要输出讯息的版本,也就是说,用同一份程序码就可以显示不同语言的讯息,才能称之为国际化的系统。GLIBC 虽然提供了 setlocale() 的界面以及 LC_MESSAGES 的环境类别,不过却没有定义讯息多国语言的实作方式,所以要做到讯息的国际化,必须得要使用 GLIBC 以外的函式套件才行。

由于在 C 函式库中并没有标准的定义,所以实际上在 GNU/Linux 里面进行讯息国际化的解决方案并不只一种,常见方式可以归类成下面三种:

1. 利用 X 的资源定义资料库 (X resource database) 来作讯息国际化

这是 X 视窗系统提供的标准方法,许多 X 应用程式都是用这种方法来作讯息的国际化的,例如 Netscape 或 xedit 等程序就是属于这一类;X 允许您将程序使用到的讯息存放在 X 的资源资料库 (resource database) 里面,由于 X 应用程式在执行时会依照 locale 设定到 /usr/X11R6/lib/X11/{locale}/app-defaults/ 目录下去寻找对应到该 locale 的资源定义档案,所以只要把讯息字串定义在资源定义档案里面,把这些字串翻译好,然后再放到该 locale 的 app-defaults 目录底下,就可以依照 locale 设定显示不同语言的讯息了,读者可以在 /usr/X11R6/lib/X11/app-defaults/ 目录下找到一些未翻译的资料档案。假如您有加装一些中文套件的话,很可能还可以在 /usr/X11R6/lib/X11/zh_TW.Big5/app-defaults/ 目录下找到 Netscape 的繁体中文定义档,这里面以 labelString 结尾的资源项目就是 Netscape 程序用到的讯息,读者可以自己参考看看。

2. 利用 GNU gettext 套件

gettext 套件是 GNU 对讯息国际化提出的一个统一的解决方案,只要程序写作时注意几点小事情,对程序码做好适当的修改并链接到 gettext 程序库,编译一次以后,不须重新编译就能显示多国的讯息,让国际化 (i18n) 的工作可以更简单的达成;因为使用简单,支援的程序工具也多,gettext 已经渐渐成为 GNU/Linux 上面讯息国际化的标准作法,像 GNOME 程序、KDE 程序等等都是用 gettext 来作讯息的国际化。

3. 应用程式自行定义的方法

某些应用程式有自己的一套讯息国际化的架构,像 linuxconf 就自己定义了一套方式来作讯息的国际化;这些方法通常跟 gettext 的基本架构很类似,作法的流程也有很多可以类比之处,不过这都不是标准的作法,自己重新发展一套讯息资料的处理方法也是一件费时费力的工作,过去 GNU/Linux 上面的 gettext 还不成熟的时候或许有需要,不过在目前 gettext 已经被广泛接受使用的情况下,通常是没必要自己再重新发明一遍轮子罗。

上面所述的三个方法里面,X 提供标准出现的最早,但是因为在程序设计阶段就得自行将讯息分离处理,用起来并不方便,也缺乏一些工具程序的支援,因此迟迟未能受到大家广泛使用。在 GNU gettext 已经逐渐成熟以后,多数的程序也不再用这个方法。目前 gettext 套件已经算是被大家接受的标准作法了,虽然它现在的版本还只是 0.10 版,但是对于讯息国际化的支援 (LC_MESSAGES) 已经算是稳定,再加上几乎所有的 GNU 程序也都支援 gettext ,因此现在把它当作一个标准,来作 Linux 上讯息中文化的工作,应该是一个适当的时机,我们也建议所有有心让自己程序显示多国语言的程序设计者都能善加利用 gettext 套件,因此本文讨论的对象会专注在 gettext 这个套件的使用方式之上,X 或其他程序的作法我们就不详谈了。

接下来让我们先来看看 gettext 的基本原理吧。

2 gettext 的使用

2.1 gettext 的工作原理

图 1 是从 GNU 的 info page 抄出来的,它总括说明了 gettext 套件的工作原理,就请读者们参照后面的说明以及这张图上的流程,一起来看看 gettext 套件的工作流程吧。

Figure 1: gettext 的工作流程图
\resizebox*{11.7cm}{8.26cm}{\includegraphics{gettext-flow.eps}}

2.1.1 修改程序码

我们前面提过,使用 gettext 套件的时候原始码要作一些适当的修改,图 1 里面的 “Source Code” 指的就是还没有经过任何修改的程序码,”Marked Source Code” 指的就是修改后的程序码,那么,要作些甚么修改呢?

要让你的程序支援讯息国际化,程序设计师在写作程序的时候就必须把有需要国际化的讯息标出来,这个时候该注意的是应该只标示出需要被翻译成他国文字的讯息,比如 “%s %d” 这样的字串是不必被翻译的,也就不必标示,而 “Hello World!!” 这类的说明讯息就需要标示出来,标示的方法如下:

printf(gettext(“Hello World”));

懂 C 的朋友应该可以看出这个标示的意义,程序码这样修改以后,程序在印出讯息时,并不是直接取得讯息的字串,而是透过一个 gettext() 函式呼叫来获得字串;这个 gettext() 函式是在 libintl.h 里面定义的,它会在程序真正被执行时检查 locale 的情况 (事实上是在程序开始时先呼叫 setlocale() 函式来设定 LC_MESSAGES 类别,在这一步程序会去检查 LC_ALL 或 LC_MESSAGES 或 LANG 这环境变数,并做好 locale LC_MESSAGES 类别的设定),然后以 gettext() 的参数 ( “Hello World” ) 作为一个 id key,去该 locale 底下的讯息资料库里面抓取合乎这个 id key 的讯息,这样子同一个程序,就可以 show 出许多不同语言的讯息了。

从 “Source Code” -> “Marked Source Code” 这一段修改程序码的流程,是程序设计师的工作,也是程序讯息多国语言的过程之一,更详细的修改步骤我们在第 3.1 节里面会用实例跟大家介绍,且留待后面再讨论吧。

2.1.2 制作 PO 档案

改好程序码以后,接着应该就要把讯息从程序码里面抽出来,制作出 “讯息资料库” ,那讯息资料库是甚么?要怎么作呢?

GNU gettext 套件里面有一个程序叫 xgettext,可以用来处理 Marked Source Code,把原始码里面有标记的讯息抓出来,产生 PO 档 (Portable Object),xgettext 的基本使用如下:

xgettext [选项] INPUTFILE …

INPUTFILE 就是 “Marked Source Code”,假如给定的输入档名为 `-‘ 的话,则程序会从标准输入读入资料,有多个档案需要处理的话,在后面列入就可以了,这个程序常用的选项有:

-d NAME
用 NAME.po 做为输出档名 (预设是  messages.po)
-D DIRECTORY
增加 DIRECTORY 到档案搜寻列表中
-f FILE
从档案 FILE 里面读取输入档档名的列表。
-keyword=WORD
其他用来标示的关键字 (若没有指定 WORD , 表示使用预设的关键字 gettext)。
-o FILE
把产生的结果写到 FILE 这个档案去。
-p DIR
把输出的档案放到 DIR 这个目录底下。
-s
制造排序妥当的输出,并移除重覆的栏位。
-h
显示说明讯息。

2.1.3 对 PO 档进行翻译

xgettext 产生的 .po 档案是一个标准的文字档,用 more 或者一般的文字处理程序就可以观看它的内容,它里面包含了xgettext 从输入的原始码档案里面抓出的所有讯息,里面每一项讯息的格式大约如下:

#: hello.c:22
msgid “Hello!!”
msgstr “”

这里面 ‘#’ 开头的行是注解,msgid 栏位是未翻译前原先的讯息,msgstr 就是用来填翻译过的讯息的,我们可以把翻译过的讯息填进去,如下:

#: hello.c:22
msgid “Hello!!”
msgstr “您好!”

如何,翻译的动作非常简单吧;翻译 PO 讯息并不需要有特别的工具,只要用一般的文字编辑器就可以了,此外由于 PO 档案是单纯的文字档,所以跟使用的平台无关,您甚至可以把 GNU/Linux 程序的 PO 档案拿到 Microsoft Windows 操作系统里面用 Notepad 编辑、翻译好再拿回来 GNU/Linux 底下使用,这也就是 PO 的全名 “Portable Object” 的意思。经过使用者把每个项目里面的讯息一一翻译完成的 PO 档,只要档用 msgfmt 编译以后摆到适当的地方,就可以让翻译过的讯息发生作用,接着我们就来看看怎么让 PO 档案生效吧。

2.1.4 编译并安装讯息资料库

作好 PO 档以后,就可以用 “msgfmt” 这个程序来把 PO 档变成真正机器可以处理的 .mo 档 (Machine Object):

msgfmt [选项] filename.po …

msgfmt 常用的选项有:

-o FILE
把产生的结果写到 FILE 这个档案去,FILE 的档名一般会以 .mo 结尾,没有指定的话,结果会写到 messages 这个档案里面。
-v
列出输入档中异常的部份,多用几个选项 -v 的话会显示更多的讯息。
-h
显示说明讯息。

用 msgfmt 产生出来的这个 .mo 档才是真正的讯息资料库档案,它的名称一般是跟应用程式的名称一致,例如 gedit 的讯息就叫做 “gedit.mo”,在程序安装的时候,这些讯息资料档应该要被放到该讯息对应的 LOCALE 目录里面去,一般而言就是在 /usr/share/locale/*/LC_MESSAGES/ 底下 (这里的 * 指的就是 locale 的名称),例如繁体中文的 locale 为 zh_TW.Big5,所以繁体中文的讯息就应该放在 /usr/share/locale/zh_TW.Big5/LC_MESSAGES/ 底下,以 GB2312 编码的简体中文讯息就该摆在 /usr/share/locale/zh_CN.GB2312/LC_MESSAGES/ 底下。如此一来,各种不同语文翻译的讯息就能够很自然的分开摆放不相冲突,在应用程式执行的时候,gettext 也就可以依照 locale 设定的不同而去不同的目录底下抓到适当的讯息翻译。下面这行指令可以列出您目前系统上安装的程序的各种不同语言的讯息资料档,读者不妨自己看一看您的 GNU/Linux 上面有多少程序已经有中文翻译罗:

ls /usr/share/locale/*/LC_MESSAGES/

2.1.5 合并旧有的翻译讯息

在程序经历过几次改版以后,很自然的会加入新的讯息或修改旧的讯息,需要重新产生 PO 档案并进行翻译,不过这时候其实很多讯息都已经翻译过了,有旧版的 .po 讯息档,我们当然不希望把已经翻译过,而且还可以用的讯息再打一次,此时就可以用 “msgmerge” 这个程序来把旧的 .po 档里面的讯息合并到新的 PO 档案里面:

msgmerge [选项] def.po ref.po

常用的选项有:

-i
对输出结果做缩排处理。
-o FILE
把输出的结果写入 FILE 这个档案。
-S
使用严格统一标准的输出结果。
-h
显示说明讯息。

上面 def.po 是旧的翻译过的 PO 档,ref.po 是新的 PO 档案 (xgettext 做出来的),在 def.po 里面翻译过的讯息只要是仍然找得到相符合栏位的,都会被合并到 ref.po 里面去,在找不到完全一致的栏位时,程序还会用模糊逻辑的方法来得到类似的翻译,这些用模糊逻辑合并进去的栏位会被标上一个 “#, fuzzy” 的记号,根据我们实际的经验,这些模糊逻辑对出来的翻译常常是错的,读者在进行翻译的时候要特别重新检查这些栏位才行。

2.2 程序设计师与讯息翻译者的分工

在图 1 的流程里面,大家可以看到 xgettext 产生的档案叫做 .pot,这是 Portable Object Template 的意思,表示刚产生的档案里面只有原来的讯息,还没有翻译。接下来有一个 .pox 档,这是 GNU 建议对还没完全翻译好的PO 档的称呼,意思是施工中的 PO 档案,等到翻译完工定案了,再把它改成 .po 档;.pot、.pox、.po 这三种档案都是 PO 档,格式是一模一样的,在图里面作这种不同的称呼只是为了方便区分翻译的状况罢了,没有特殊的意义,读者也不见得一定要遵守这个命名规则的。

图中的 .gmo 其实指的就是用 msgfmt 编译出来的 .mo 档,GNU 建议用 .gmo 来称呼还没安装到定位的 .mo 档,表示这个档案是由 GNU gettext 套件产生的,这个命名法在许多 GNU 程序套件里面都可以看到;从程序码到翻译到安装的整个流程还可以用档案的附加档名慨括如下:

source code -> .pot -> .pox -> po -> .gmo -> .mo -> <run time>

在这整个流程里面,写作程序跟制作 .pot 档案是属于程序设计者的工作,然而 PO 档案的翻译却不一定要是程序设计者本人来作,事实上写程序的人也不可能懂得各国的语言,这些翻译跟维护 PO 档案的工作是属于 L10N 的范畴,应该由世界各地使用不同语言的人来担任翻译的工作才是。作这些翻译工作的人只要是单纯的使用者、懂英文、会使用文字编辑程序就可以了,并不需要懂得程序设计,因此人人都可以参与,gettext 套件提供了一个程序设计师跟讯息翻译者可以分工的架构,在图 1 里面深色的部份表示的就是属于讯息翻译者可以发挥的地方,外围浅色的部份则是程序设计师的工作,由程序设计者制作出 .pot 档案以后,有兴趣翻译的人就可以去抓回来翻译,翻好以后再寄回去给程序设计者包在套件里面发行,下一节我们先用一个例子实际让有心利用 gettext 写作多国语言程序的读者可以有个实作的机会,接着在第 4 节里面我们会从单纯使用者跟翻译者的角度来看 gettext 套件,请读者依照您个人的 “志愿” 选择有兴趣的部份来阅读吧。

3 一个实际的例子

上一节里面我们对整个 gettext 工作的流程做了简单的说明,接下来我们就来写一个简单的程序,希望能够利用这个完整的例子来让读者了解利用 gettext 套件写作 I18N 程序的详细方法。

3.1 使用 gettext 相关的函式

要利用 gettext 套件写作 I18N 程序,第一件事当然是要把 getext 套件安装好,现在所有的 GNU/Linux 安装套件里面应该都有内含这个套件了,因此安装应该不困难,读者只要从光盘或 FTP 站上面把 gettext 套件抓回来装好就可以,在此就不赘述了;除了 gettext 之外,你还必须有支援 I18N 的 LIBC,这一点读者可以查看一下 /usr/include/ 底下是不是有 locale.h 以及 libintl.h 这两个档案,有的话就表示你的 LIBC 是支援 I18N 的,这在目前各家 GNU/Linux 安装套件厂商使用的 glibc-2.1.x 里面是不会有问题的。要写一个讯息国际化的程序,需要用到底下这两个 C 函式:

char *setlocale(int category, const char * locale);

extern char *textdomain(const char *domainname);

上面的 setlocale() 是用来设定区域化环境的,用法在前几期已经详细介绍过,这边就不赘述。textdomain() 这个函式接受一个字串当作参量,用来指定这个程序所使用的 “文字领域” (textdomain),textdomain 会决定以后的 gettext() 函式要到那个档名的档案里面去找讯息资料,gettext() 会在程序执行时到 domainname.mo 这个档案里找字串的翻译,一般而言这个 domainname 都设成和程序的名称一样的以避免不同程序的讯息档案相互冲突。

3.2 范例程序

写作应用程式时要用 gettext 来作讯息多国语言的步骤其实非常简单,只有以下几点:

1. 在程序开头引入 libintl.h 跟 locale.h 两个标头档。
2. 在主程序开头时用 setlocale() 设定区域化环境。
3. 用 textdomain() 设定文字领域。
4. 把程序码中所有需要翻译的讯息都包在 gettext() 函式里面。

底下就是一个完整的程序,我们把它存在 gettext_test.c 里面,读者不妨先一边看程序码,一边观察上面几个步骤出现的位置:

/******************** Example program start here ********************/
/* gettext_test.c by Yuan-Chung Cheng */
#include <stdio.h>
#include <locale.h>
#include <libintl.h>
#define _(STRING) gettext(STRING)
#define PACKAGE “gettext_test”

char *program_name;

main(int argc, char *argv[])
{
program_name=argv[0];
setlocale(LC_ALL, “”);
textdomain(PACKAGE);

if(argc < 2){
printf(_(“%s: too few arguments\n”), program_name);
exit(0);
}
printf(_(“Hello, %s.\nBe a Happy Linuxer!!\n”), argv[1]);
}

/******************** Example program end here ********************/

这个程序的流程极简单,只是在一开始多了 setlocale、设定 textdomain 两个步骤而已,读者应该不难在这个短短的程序里面看出配合 gettext 修改的部份,值得特别注意的是,在这个程序里面我们把 _() 定义成 gettext(),然后再用 _() 来标示需要翻译的字串,这个简化的用法是 GNU 应用程式里面惯用的,可以让程序设计师少打很多 gettext(),也可以增加程序的可读性,建议大家可以学起来。

3.3 产生 .pot 档案

有了程序码以后,就可以利用 xgettext 程序生成 .pot 档案:

xgettext -keyword=_ -o gettext_test.pot gettext_test.c

前面的程序码里面用 _() 代替了 gettext() 函式,所以在用 xgettext 抽出讯息的时候要多加一个参数 “-keyword=_”,告诉程序用 _() 标示的字串也要当成待翻译的讯息抽出来,存到档案 gettext_test.pot 里面去,以下就是产生的 .pot 档案的内容:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid “”
msgstr “”
“Project-Id-Version: PACKAGE VERSION\n”
“POT-Creation-Date: 2000-06-19 05:51+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: ENCODING\n”

#: gettext_test.c:21
#, c-format
msgid “%s: too few arguments\n”
msgstr “”

#: gettext_test.c:25
#, c-format
msgid “”
“Hello, %s.\n”
“Be a Happy Linuxer!!\n”
msgstr “”

3.4 翻译 PO 档案并安装

得到 .pot 档案以后就可以进行翻译的工作了,这个程序只有两行讯息,当然轻轻松松三两下就翻译完毕了:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2000 Free Software Foundation, Inc.
# Yuan-Chung Cheng <platin@ch.ntu.edu.tw>, 2000.
#
msgid “”
msgstr “”
“Project-Id-Version: hello-0.1\n”
“POT-Creation-Date: 2000-06-18 05:51+0800\n”
“PO-Revision-Date: 2000-06-19 08:30+0800\n”
“Last-Translator: Yuan-Chung Cheng <platin@ch.ntu.edu.tw>\n”
“Language-Team: Chinese <zh@li.org>\n”
“MIME-Version: 1.0\n”
“Content-Type: text/plain; charset=big5\n”
“Content-Transfer-Encoding: 8-bit\n”

#: gettext_test.c:21
#, c-format
msgid “%s: too few arguments\n”
msgstr “%s: 参数太少了\n”

#: gettext_test.c:25
#, c-format
msgid “”
“Hello, %s.\n”
“Be a Happy Linuxer!!\n”
msgstr “”
“您好, %s. \n”
“好好玩 Linux 喔!!\n”

上面便是完成翻译以后的 PO 档案,读者要注意除了翻译讯息以外,还要在一开头的标头部份把您的大名、E-Mail、翻译的编码等等栏位填一填才行。完成的 PO 档案可以用下面的指令编译成 .gmo 档案:

msgfmt -v -o gettext_test.gmo gettext_test.po

接着把完成的讯息资料库安装到定位,我们现在有的是繁体中文的翻译结果,所以安装在 zh_TW.Big5 目录底下:

install -m 644 gettext_test.gmo /usr/share/locale/zh_TW.Big5/LC_MESSAGES/gettext_test.mo

装好之后把原来的原始码用 gcc 编译起来,然后改一下 LANG 或 LC_MESSAGES 的设定,执行看看,是不是已经能够成功显示多国的讯息了呢?图 2 是在不同 LANG 设定下执行本程序的结果,读者可以看到当 locale 设定改变的时候,程序会自动显示不同语言的讯息出来。要加上其他语言的翻译的话,只要把 PO 档翻译好,用 msgfmt 编译成 .mo 档案并安装到该语言 locale 的 LC_MESSAGES 目录底下就可以了,并不需要重新编译程序。

Figure 2: 依照不同 locale 设定显示不同讯息
\resizebox*{6.63cm}{4.1cm}{\includegraphics{gettext_test.eps}}

在这一节里面,我们为了说明的方便,举的例子只是一个小小的档案而已,读者们在发展自己的程序时可能会遇到比较复杂的情况,例如程序码档案可能会不只一个,而且分布在不同目录底下,这个时候在产生 PO 档案的过程会比较复杂,不过只要善用 GNU 的 autoconf 套件与 gettextize 程序来帮您处理程序码,就可以省下不少功夫。限于篇幅我们只能为大家介绍最简单的情况,深入的应用就只好麻烦大家直接参考 gettext 的使用手册了:

http://www.gnu.org/manual/gettext/html_mono/gettext.html

4 从翻译者的观点来看 gettext 工具

让应用程式支援 gettext 是程序设计师的工作,不过要让程序在执行时能够显示出各种语言的讯息,就有赖讯息翻译者的努力了。参与翻译讯息的工作需要的知识跟写作 I18N 程序没什么相关性,也不需要对程序设计的原理有甚么了解,在翻译时更重要的是对该应用程式的 “使用经验”,知道怎么将程序的原文讯息适切的用本国的语言来表达,除此之外,就剩下要懂得编辑文字档而已了,所以任何人都可以参与翻译讯息的工作,事实上目前在 GNU/Linux 底下的中文讯息档很多都是由自愿的热心网友翻译出来的,许多参与的人也都完全不懂得设计程序呢。

4.1 PO 档案的翻译

4.1.1 取得 PO 档案

要进行 PO 档案翻译的第一件事就是取得还没翻好的 PO 档,PO 档一般含在应用程式的原始码里面,只要使用到 gettext 套件的应用程式都会有一个名为 po/ 的目录,这就是摆放 PO 档案的地方,通常在这底下可以找到一个结尾为 .pot 的未翻译档,以及许多各种语言的翻译结果,抓 POT 档案回去翻译之前,读者最好先检查看看以前是不是有人翻译过这个讯息了,以繁体中文为例,先找看看 po/ 目录底下是不是有叫做 zh_TW.Big5.po 这个档案,有的话,就表示已经有人翻译过这个讯息了。

在没人作过翻译的情况下,请注意一下 POT 档案里面标头部份显示的产生时间,记得不要抓太旧的档案回去,以免浪费时间在翻译过时的讯息,必要的时候写封 e-mail 请负责维护程序码的人更新一下 .pot 档案的版本,或者干脆自己把整个原始码抓回来,用 make 更新 .pot 档案,再重头翻译就是了。

发现档案已经有人翻译过的话,请尽量先跟前一位翻译的人取得联系再动手翻译;在 PO 档的标头部份都有翻译者的 e-mail 位址,写封信去询问他是否要继续维护这个讯息,甚至加入他一起来合作维护,我想读者也不希望您在辛苦翻译到一半的时候突然又冒出另外一个版本,结果浪费时间作白工了吧。假如确定前人已经不再继续维护这个讯息翻译以后,您就可以放心的以原来翻译的基础继续翻译的工作罗,不过还是要更新一下讯息的版本,保持翻译最新的程序讯息,此时请先拿到新版的 POT 档案,然后用下面指令拿到最新版本的 PO 档:

msgmerge zh_TW.Big5.po package_name.pot > package_name.po

上面指令中 zh_TW.Big5.po 是旧的 PO 档案,package_name.pot 表示新的 POT 档案,msgmerge 程序会把 zh_TW.Big5.po 里面还能用的翻译跟 package_name.pot 里面的讯息定义合并起来,输出到 package_name.po 里面去,这个新的 PO 档案里面会保留有旧的翻译再加上新的讯息定义,请拿这个档案来进行讯息翻译的工作。

4.1.2 翻译讯息

拿到最新的 PO 档案以后就可以开启您最爱用的文字编辑程序来进行翻译的工作罗。PO 档案是单纯的文字档,不管您用甚么程序来编辑它都没关系,前面已经说过,甚至用 Windows 的 Notepad 来翻译都没有关系,虽说如此,我们在网络上还是可以找到不少个可以帮助您翻译工作的程序,读者们到 http://freshmeat.net 底下用 gettext 为关键字找一下就可以找到好几个,请读者们自己试试看罗。小虫倒是建议有使用 Emacs 的朋友试试看 PO mode,在 gettext 套件里面可以找到一个 po-mode.el 的 emacs Lisp 程序档,参照里面的说明装好以后,您的 Emacs 在读到 PO 档的时候就会自动转到 PO mode 里面,PO mode 让您可以更简单的输入翻译出来的讯息,更另外提供了一些操作讯息项目的指令让您使用,像讯息的复制、删除等等都可以轻易的做到,读者可以自己在 PO mode 底下按 ‘h’ 看说明讯息来学习 PO mode 的使用方式,图 3 就是用 Emacs 的 PO mode 在编辑 PO 档案的情况。

Figure 3: Emacs 的 PO mode
\resizebox*{7.88cm}{7.1cm}{\includegraphics{gettext_emacs.eps}}

翻译时要记得填入标头资料,用 msgmerge 出来的档案进行翻译的时候要特别注意一下被标为 “#, fuzzy” 的项目,请务必检查一下这些项目,没问题的话要记得把 “#, fuzzy” 这一行删掉,否则接下来编译的时候会发生问题的。继承别人翻译成果的朋友请记得把标头资料改成自己,并且把前一位翻译者的资料放到一开始的版权信息里面,以示对前人翻译成果的尊重,并且也为这个档案的传承关系作一个见证。

在翻译时还有其他特别的事情需要注意,因为您在翻译的事实上是 C 语言的字串,所以一些讯息的写法得要依照 C 字串的规矩才行,例如字串中有引号 ‘”‘ 的时候要用打成 ‘\”‘ 才行,翻译出来的结果要保留原文讯息中的跳行字元跟 ‘%’ 带头的那些变数参考才行,例如下面的翻译:

#: locale/programs/ld-ctype.c:4131
#, c-format
msgid “%s: table for class \”%s\”: %lu bytes\n”
msgstr “%s: 类别 \”%s\” 表格: %lu 字节\n”

繁体中文翻译还会有一个额外的问题,因为许多中文字像许、功、摆… 等等的第二个字节是 ‘\’字元,这在 C 字串中会造成错误,因此在翻译遇到这些字的时候都要在后面多加一个 ‘\’ 才行,因为这个工作手动实在是太麻烦了,网络上有人写了一些小程序来处理这个问题,读者们可以在 http://i18n.linux.org.tw/ 找到 bg5cc 这个程序,用它来帮您加 ‘\’,我们在翻译的时候只要编辑 .pox 档案,不需要去担心这些中文字的问题,等到要编译成 .mo 档案的时候再来用 bg5cc 转一下:

bg5cc package_name.pox package_name.po

上面这个指令会帮您把 package_name.pox 里面需要多加 ‘\’ 的字找出来,把加上 ‘\’ 的结果存到 package_name.po 里面去,产生的这个 package_name.po 档案就可以放心交给 msgfmt 处理,不必担心中文字的问题罗。

4.1.3 测试翻译结果并送回给原作者

翻译好的 PO 档案可以用下面指令测试出是否正确无误:

msgfmt -v -o /dev/null package_name.po

例如:

[platin@bruceyu libc-zh_TW.Big5]$ msgfmt -v -o /dev/null libc-zh_TW.Big5.po 652 已翻译的讯息 , 496 未译的讯息 .

[platin@bruceyu libc-zh_TW.Big5]$

编译发生错误的话,msgfmt 会告诉您有问题的行号,您可以很快的找到错误所在;正确无误的 PO 档案就可以用第 3.4 节提到过的方法安装到 /usr/share/locale/… 底下,下次您开启这个程序的时候,就可以看到翻译的结果了。当然,独乐乐不如众乐乐,请记得把您翻译完成的 PO 档案用 e-mail 寄回去给程序的原作者,请他将这个讯息在程序下一次 release 的时候放进去,如此一来全世界所有使用这个程序的使用者都可以享受到您的贡献,也算是对 GNU/Linux 系统做出一番贡献喔。

由于程序是不停的在改版的,不时会有新的讯息加到程序里面,也时常会有修改原有讯息的情形,所以不要把翻译 PO 档案当成可以一次就搞定的工作,这些讯息翻译的持续维护是很重要的问题,讯息翻译者应该要时时检查自己负责的程序讯息是否有更新的情况,定时下载新的档案来更新、保持翻译结果的同步,这种持之以恒的持续维护也是很重要的喔。

4.2 翻译工作团体

翻译一个档案是很简单,可是要持续维护的工作就不容易了,因此世界各地都有热心人士出来组织翻译工作团体,希望可以简化 PO 档案的下载与更新工作以扩大参与的层面,更借着网站的协调机制避免有两人以上重复翻译同一份讯息,造成人力资源的浪费,还可以保存过去翻译的结果、持续 PO 档案翻译结果的维护工作。在台湾先是有李柏峰大哥 <pofeng.lee@ms7.url.com.tw> 出面担任 GNU 下游翻译组织 zh@li.org 的负责人,后有薛景中 <shyue@sonoma.com.tw>、陈更欣 <c17@linux.acer.com.tw> 等先进发起 “i18n 程序中文化计画” 来统合有兴趣参与翻译工作的力量,让有兴趣帮忙翻译的朋友可以透过这个网站取得最新的 PO 档案,也可以在这边找到翻译的技巧跟术语翻译的标准,图 4 就是这个网站的首页,其网址如下,有心进行翻译工作的朋友请务必进去看看:

http://i18n.linux.org.tw/

Figure 4: i18n 程序中文化计画首页
\resizebox*{10.16cm}{6.69cm}{\includegraphics{i18n_linux_org.eps}}

5 结语

看过上面的例子以后,读者是不是也觉得用 gettext 来让程序支援多国语言的讯息是很简单的一件事情呢?在当前软件国际化的风潮底下,让同一套程序码可以支援各种不同语言的讯息已经是写程序的人都必须面对的一件工作,GNU gettext 套件提供的架构可以让我们轻易的完成这件任务,有在 GNU/Linux 底下撰写应用程式的读者可要好好善加利用。
对单纯的使用者而言,如何用 gettext 来设计程序大概不是大多数人关心的问题,大家最在意的还是在使用程序时能不能看到讯息用自己熟悉的语言来显示出来吧?我们在网络上不时可以听到有人抱怨『GNU/Linux 底下怎么全都是英文,中文化那么差』,假如中文化指的是进去看不到中文讯息的话,那可就真的是冤枉了,其实缺少中文讯息这一点在技术上是最简单的问题,比起前几期讲的东西简单许多,而且还是任何人都可以一起来参与来改善的。可惜的是,跟其他国家的网友们参与的程度比起来,台湾朋友的热情似乎是少了一点,参与的人少,再加上国内的一些参与 GNU/Linux 市场的商业公司虽然都做过讯息翻译的工作,至今却还没能集中并持续的来维护中译的讯息。目前讯息中译的成果绝大多数都还是由网络上少数几个热心网友们贡献出来的,也因此目前 GNU/Linux 底下大多数的应用程式都还是没有中文讯息。我们在这边呼吁有心要改善这个问题的朋友或团体参考第 5.2 节里面的说明,一起来加入 i18n.linux.org.tw,只要看得懂英文、会编辑文字档案就可以帮 GNU/Linux 底下的应用程式加上中文的讯息,大家一起来,GNU/Linux 的明天会更好,您说是不是呢?

Openssh for Windows.

Posted by – December 26, 2009

http://sourceforge.net/projects/sshwindows/
http://www.openssh.com/