2D绘图部分基本告一段落,还在想下面的部分要写什么,本来计划先说下view-model的相关问题,但是前面看到有朋友问关于国际化的问题,所以现在先来说说Qt的国际化吧!
Qt中的国际化的方法有很多,常用的有使用QTextCodec类和使用tr()函数。前者将编码名称写到代码里面,除非你使用Unicode编码,否则国际化依然是一个问题;后者就不会有这个问题,并且这也是Qt推荐的做法。因此,我们主要来说使用tr()函数的方法进行应用程序的国际化。
我们先来看一个很简单的MainWindow。为了清楚起见,这里只给出了cpp文件的内容:
#include
"mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menuBar = new QMenuBar( this);
QMenu *fileMenu = new QMenu(tr( "&File"), menuBar);
QAction *newFile = new QAction(tr( "&New..."), fileMenu);
fileMenu->addAction(newFile);
QAction *openFile = new QAction(tr( "&Open..."), fileMenu);
fileMenu->addAction(openFile);
menuBar->addMenu(fileMenu);
setMenuBar(menuBar);
connect(openFile, SIGNAL(triggered()), this, SLOT(fileOpen()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::fileOpen()
{
QFileDialog *fileDialog = new QFileDialog( this);
fileDialog->setWindowTitle(tr( "Open File"));
fileDialog->setDirectory( ".");
if(fileDialog->exec() == QDialog::Accepted) {
QString path = fileDialog->selectedFiles()[0];
QMessageBox::information(NULL, tr( "Path"), tr( "You selected\n%1").arg(path));
} else {
QMessageBox::information(NULL, tr( "Path"), tr( "You didn't select any files."));
}
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menuBar = new QMenuBar( this);
QMenu *fileMenu = new QMenu(tr( "&File"), menuBar);
QAction *newFile = new QAction(tr( "&New..."), fileMenu);
fileMenu->addAction(newFile);
QAction *openFile = new QAction(tr( "&Open..."), fileMenu);
fileMenu->addAction(openFile);
menuBar->addMenu(fileMenu);
setMenuBar(menuBar);
connect(openFile, SIGNAL(triggered()), this, SLOT(fileOpen()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::fileOpen()
{
QFileDialog *fileDialog = new QFileDialog( this);
fileDialog->setWindowTitle(tr( "Open File"));
fileDialog->setDirectory( ".");
if(fileDialog->exec() == QDialog::Accepted) {
QString path = fileDialog->selectedFiles()[0];
QMessageBox::information(NULL, tr( "Path"), tr( "You selected\n%1").arg(path));
} else {
QMessageBox::information(NULL, tr( "Path"), tr( "You didn't select any files."));
}
}
这是一个很简单的类,运行结果想必大家也都非常清楚:就是一个主窗口,上面有一个菜单栏,一个File菜单,里面有两个菜单项:
之所以把运行图贴出来,是为了大家能够看清,在代码中的&符号实际在界面中显示成为一条下划线,标记出这个菜单或者菜单项的快捷键。按照代码,当我们点击了Open时,会弹出一个打开文件的对话框:
这里的slot里面的代码在前文中已经详细介绍过。也许你会问,为什么要用这种麻烦的写法呢?因为我们曾经说过,使用static函数实际上是直接调用系统的对话框,而这种构造函数法是Qt自己绘制的。这对我们后面的国际化是有一定的影响的。
好了,都已经准备好了,下面开始进行国际化。所谓国际化,实际上不仅仅是把界面中的各种文字翻译成另外的语言,还有一些工作是要进行书写方式、货币等的转换。比如,阿拉伯书写时从右向左的,这些在国际化工作中必须完成。但是在这里,我们只进行最简单的工作,就是把界面的文字翻译成中文。
首先,我们需要在pro文件中增加一行:
TRANSLATIONS += myapp.ts
myapp.ts是我们需要创建的翻译文件。这个文件的名字是任意的,不过后缀名需要是ts。然后我们打开命令提示符,进入到工程所在目录,比如我的是E:\My Documents\Workspace\Qt\MyApp,也就是pro文件所在的文件夹,然后输入命令
lupdate MyApp.pro
,如果你出现的是命令不存在,请注意将Qt的bin目录添加到环境变量中。此时,如果更新的数目,说明ts文件创建成功:
最后一行是说,找到7个需要翻译的原文字,0个已经存在。也就是说,这个文件是新建的。这时你会在工程目录下找到这个myapp.ts文件。也许你会奇怪,为什么这里还会说已存在的数目呢?因为Qt这个工具很智能的能够识别出已经存在的文字和修改或新增的文字,这样在以后的工作中就不需要一遍遍重复翻译以前的文字了。这也就是为什么这个工具的名字是“lupdate”的原因,因为它是“update”,而不仅仅是生成。
如果你有兴趣的话,可以用记事本打开这个ts文件,这个文件实际上是一个XML文件,结构很清晰。不过,我们要使用专业的翻译工具进行翻译。Qt提供了一个工具,Qt Linguist,你可以在开始菜单的Qt项下面的Tools中找到。用它可以打开我们的ts文件,然后进行我们的翻译工作:
完全翻译完成后保存文件,然后在文件菜单下有个“发布”。点击这个按钮,工程目录下会有一个myapp.qm文件,这就是我们翻译得到的文件。Qt的qm文件实际上是二进制格式的,因此它经过了高度的优化,体积很小。
下面我们要修改main()函数,使之加载这个qm文件:
int main(
int argc,
char *argv[])
{
QApplication a(argc, argv);
QTranslator qtTranslator;
qtTranslator.load( "myapp.qm");
a.installTranslator(&qtTranslator);
MainWindow w;
w.resize(800, 600);
w.show();
return a.exec();
}
{
QApplication a(argc, argv);
QTranslator qtTranslator;
qtTranslator.load( "myapp.qm");
a.installTranslator(&qtTranslator);
MainWindow w;
w.resize(800, 600);
w.show();
return a.exec();
}
注意,QTranslator类实际是在QtCore下面的。代码还是很清晰:创建一个QTranslator对象,然后加载qm文件,然后将这个对象安装到QApplication类。好了,现在大功告成,重新编译后运行下程序吧!
咦?怎么还是英文的?哪里有错误了呢?这里往往令人疑惑,其实,这是由于我们使用load()函数加载qm文件时使用的是相对路径,这样直接load(“myapp.qm”),其实会在当前编译后的exe所在目录下寻找这个qm文件,所以,只要我们把qm文件同exe放在同一目录下,再次运行:
现在,这个界面已经是中文了吧!其实,这一小细节已经说明,qm文件其实是动态加载到exe文件中的,而不是直接编译进去的。这一点为我们进行动态切换语言提供了基础。
上次说了国际化的过程,现在来看一下具体的国际化的相关代码。
在代码中,我们使用tr()将需要翻译的字符串标记出来。lupdate工具就是提取出tr()函数中的相关字符串。tr()函数是QObject类的一个static函数,其签名如下:
static QString tr(
const
char *sourceText,
const
char *comment = 0,
int n = -1);
虽然我们只传了一个参数,但是实际上tr()函数是接受3个参数的。第一个参数是我们需要翻译的文字,如果使用qm文件有对应的字符串,则使用对应的字符串进行替换,否则将显示sourceText参数指定的字符串。第二个参数是一个注释,用于解释前面的sourceText的含义,比如table一词既可以当做桌子翻译,又可以当成表格翻译,这时你就需要提供这个注释。或许你会问,使用翻译工具的时候不是有源代码吗?问题是,有可能人家不使用这个翻译工具,而使用别的工具,这样就不能保证会有这个源代码的预览;并且,你的程序不一定必须要发布源代码的;翻译人员往往只得到我们导出的ts文件,如果你加上注释,就可以方便翻译人员进行翻译。最后一个参数n用于指定字符串是否为复数。我们知道,很多语言,比如英语,很多名词的单复数形式是不相同的,为了解决这个问题,Qt在tr()函数中提供了一个参数n。请看如下代码:
int n = messages.count();
showMessage(tr( "%n message(s) saved", "", n));
showMessage(tr( "%n message(s) saved", "", n));
对于n的值的不同,Qt会翻译成不同的文字,例如:
n | 翻译结果 |
0 | 0 message saved |
1 | 1 message saved |
2 | 2 messages saved |
5 | 5 messages saved |
tr()函数是QObject的函数,如果你的类不是继承自QObject,就不能直接使用tr()函数。比如我们在main()函数中希望增加一句设置MainWindow的title的代码:
w.setWindowTitle(tr(
"MyApp"));
直接这样写是无法通过编译的,因为main()函数是全局函数,所以这个tr()是找不到的。解决办法一是显式地调用QObject的函数:
w.setWindowTitle(QObject::tr(
"MyApp"));
或者,你可以使用QCoreApplication的translate()函数。你一定还记得,我们的main()函数的第一句总是QApplication app;,其实,QApplication就是QCoreApplication的子类。所以,我们也能这样去写:
w.setWindowTitle(app.translate(
"MyApp"));
由于在Qt程序中,QCoreApplication是一个单例类,因此,Qt提供了一个宏qApp,用于很方便的访问QCoreApplication的这个单例。所以,在其他文件中,我们也可以直接调用qApp.translate()来替换tr(),不过这并没有必要。
如果你的翻译文本中包含了需要动态显示的数据,比如我们上次代码中的
QMessageBox::information(NULL, tr(
"Path"), tr(
"You selected\n%1").arg(path));
这句你当然可以写成
QMessageBox::information(NULL, tr(
"Path"),
"You selected\n" + path);
但这种连接字符串的方式就
不能够使用tr()函数了!因此,如果你需要像C语言的printf()函数这种能够格式化输出并且需要翻译时,你必须使用我们例子中的%1加arg()函数!
如果你想要翻译函数外部的字符串,你需要使用两个宏QT_TR_NOOP()和QT_TRANSLATE_NOOP()。前者是用来翻译一个字符串,后者可以翻译多个字符串。它们的使用方法如下:
QString FriendlyConversation::greeting(
int type)
{
static const char *greeting_strings[] = {
QT_TR_NOOP( "Hello"),
QT_TR_NOOP( "Goodbye")
};
return tr(greeting_strings[type]);
}
{
static const char *greeting_strings[] = {
QT_TR_NOOP( "Hello"),
QT_TR_NOOP( "Goodbye")
};
return tr(greeting_strings[type]);
}
static
const
char *greeting_strings[] = {
QT_TRANSLATE_NOOP( "FriendlyConversation", "Hello"),
QT_TRANSLATE_NOOP( "FriendlyConversation", "Goodbye")
};
QString FriendlyConversation::greeting( int type)
{
return tr(greeting_strings[type]);
}
QString global_greeting( int type)
{
return qApp->translate( "FriendlyConversation",
greeting_strings[type]);
}
QT_TRANSLATE_NOOP( "FriendlyConversation", "Hello"),
QT_TRANSLATE_NOOP( "FriendlyConversation", "Goodbye")
};
QString FriendlyConversation::greeting( int type)
{
return tr(greeting_strings[type]);
}
QString global_greeting( int type)
{
return qApp->translate( "FriendlyConversation",
greeting_strings[type]);
}
好了,以上就是我们用到的大部分函数和宏。除此之外,如果我们运行前面的例子就会发现,实际上我们只是翻译了菜单等内容,打开文件对话框并没有被翻译。原因是我们没有给出国际化的信息。那么,怎么才能让Qt翻译这些内建的文字呢?我们要在main()函数中添加几句:
int main(
int argc,
char *argv[])
{
QApplication a(argc, argv);
QTranslator qtTranslator;
qtTranslator.load( "myapp.qm");
a.installTranslator(&qtTranslator);
QTranslator qtTranslator2;
qtTranslator2.load( "qt_zh_CN.qm");
a.installTranslator(&qtTranslator2);
MainWindow w;
w.resize(800, 600);
w.show();
return a.exec();
}
{
QApplication a(argc, argv);
QTranslator qtTranslator;
qtTranslator.load( "myapp.qm");
a.installTranslator(&qtTranslator);
QTranslator qtTranslator2;
qtTranslator2.load( "qt_zh_CN.qm");
a.installTranslator(&qtTranslator2);
MainWindow w;
w.resize(800, 600);
w.show();
return a.exec();
}
我们又增加了一个QTranslator对象。Qt实际上是提供了内置字符串的翻译qm文件的。我们需要在Qt安装目录下的translations文件夹下找到qt_zh_CN.qm,然后同前面一样,将它复制到exe所在目录。现在再运行一下程序:哈哈已经完全变成中文了吧!
至此,我们的Qt程序的国际化翻译部分就结束啦!