嘿嘿,本文只是试图从纯C++的角度告诉你 Qt 的国际化是到底是怎么一回事(注:本文只看一个点,不看面)。而不会一步一步告诉你Qt的国际化/本地化怎么用(这些东西在Qt Manual、论坛 以及 相关书籍中介绍的够多了)。
Qt 国际化所做的就是这点东西:
- 首先,提取要翻译的字符串,手动翻译,生成一个qm文件,以备使用
- 其次,程序中使用QTranslator安装翻译文件
- 最后,tr函数去查找有没有对应的字符串,有则使用,
-
没有怎么办,就按照某种编码将参数窄字符串变成QString呗
-
至于动态翻译:点一下菜单,界面文字全改变,这在Qt中是相当容易实现的东西。
其实,根本就没有动态这回事。所谓的动态翻译,就是我们加载了一个新的翻译文件,然后将界面的文字用新的重新设置了一遍。
我们找个超简单的C++的小例子看看,并一步一步让它变的复杂一点点。
例子一
看一个 hello world 的例子:
- 为了稍后国际化,我们先用一个 tr 宏包住了所有要显示的字符串。
- 同样为了程序可以在所有平台下运行,我们的输出使用的是宽字符wchar_t
#include <iostream>
#include <string>
#include <locale.h>
#define tr(X) L##X
int main()
{
setlocale(LC_ALL, "");
std::wcout<<tr("hello")<<tr(" ")<<tr("world")<<std::endl;
return 0;
}
恩,编译运行,看到结果 hello world
例子二
如何翻译这个程序呢?
- 要有 翻译后的文字 吧
- 要使用 翻译后的文字 吧
- 要处理 没有翻译的文字 吧
词典?
源单词和目标单词的对应关系,我们就叫它词典好了。
- 创建3个全局的map,分别用来存在中文、日文、挪威文的翻译
- 创建一个辅助函数create_map和辅助变量dummy用来初始化这3个map
typedef std::map<std::string, std::wstring> Map;
Map chinese;
Map japanese;
Map norwegian;
int create_maps()
{
chinese["hello"] = L"你好";
chinese["world"] = L"世界";
japanese["hello"] = L"こんにちは";
japanese["world"] = L"世界";
norwegian["hello"] = L"hallo";
norwegian["world"] = L"verden";
return 0;
}
int dummy = create_maps();
关联
有了翻译的内容,需要安装一下,让我们的程序知道翻译内容的存在吧?
Map * globalMap = 0;
int main()
{
setlocale(LC_ALL, "");
globalMap = & chinese; //install
std::wcout<<tr("hello")<<tr(" ")<<tr("world")<<std::endl;
return 0;
}
使用
第一个例子中的宏可以丢掉了,我们写一个函数:
- 如果安装了词典,且存在翻译的内容,使用之
- 其他,将窄字符串用某种规则直接转成宽字符串
std::wstring tr(const char * text)
{
if (globalMap && globalMap->count(text)) {
return (*globalMap)[text];
}
wchar_t wcs[100];
mbstowcs(wcs, text, 99);
return std::wstring(wcs);
}
拼盘
将3部分合到一块:
#include <iostream>
#include <string>
#include <cstdlib>
#include <map>
#include <locale.h>
typedef std::map<std::string, std::wstring> Map;
Map chinese;
Map japanese;
Map norwegian;
int create_maps()
{
chinese["hello"] = L"你好";
chinese["world"] = L"世界";
japanese["hello"] = L"こんにちは";
japanese["world"] = L"世界";
norwegian["hello"] = L"hallo";
norwegian["world"] = L"verden";
return 0;
}
int dummy = create_maps();
Map * globalMap = 0;
std::wstring tr(const char * text)
{
if (globalMap && globalMap->count(text)) {
return (*globalMap)[text];
}
wchar_t wcs[100];
mbstowcs(wcs, text, 99);
return std::wstring(wcs);
}
//#define tr(X) L##X
int main()
{
setlocale(LC_ALL, "");
globalMap = & chinese;
std::wcout<<tr("hello")<<tr(" ")<<tr("world")<<std::endl;
return 0;
}
看看运行结果:
你好 世界
对比Qt
Qt 又做了什么呢?
Qt |
我们的例子 |
|
lupdate/lrelease/... |
Map/Map/Map |
生成翻译/词典文件 |
QTranslator |
globalMap |
安装翻译文件 |
QObject::tr() |
tr() |
使用翻译文件 |
从根本上说,Qt 国际化所做的就是这点东西:
- 首先,提取要翻译的字符串,手动翻译,生成一个qm文件,以备使用
- 其次,程序中使用QTranslator安装翻译文件
- 最后,tr函数去查找有没有对应的字符串,有则使用,
-
没有怎么办,就按照某种编码将参数窄字符串变成QString呗?
-
注意,tr就是一个将 const char * 变成 QString 的函数:
QString QObject::tr ( const char * sourceText,...)
对于tr,我们在Qt中translate、tr关系 与中文问题有了比较详细的讨论,此处就不重复了。
Qt动态翻译
点一下菜单,界面文字全改变,这在Qt中是相当容易实现的东西。也就是大家所说的动态翻译。
其实,根本就没有动态这回事。所谓的动态翻译,就是我们加载了一个新的翻译文件,然后将界面的文字用新的重新设置了一遍。
考虑我们前面的例子,稍微改改:
int main()
{
setlocale(LC_ALL, "");
std::wstring welcome = tr("hello"); //1st
globalMap = & chinese;
welcome = tr("hello"); //2nd
globalMap = & japanese;
welcome = tr("hello"); //3rd
return 0;
}
尽管都是用tr,但3处 welcome 的内容却不相同,动态翻译也就是这回事。
- 一个button的文字如何改变? 通过 setText
-
在button上,"hello" ==> "你好" 是改变么? 显然,于是这个过程需要setText
这也是为什么,uic生成的代码 ui_xxx.h 中始终有:
void retranslateUi(QDialog *Dialog)
{
Dialog->setWindowTitle(QApplication::translate("Dialog", "Dialog", 0, QApplication::UnicodeUTF8));
groupBox->setTitle(QApplication::translate("Dialog", "GroupBox", 0, QApplication::UnicodeUTF8));
} // retranslateUi
这种函数存在的原因。
当你安装了新的翻译文件以后,只需要重新调用一遍这个函数就行了。
但你安装新的翻译文件后,应用程序会给各个窗口发送一个事件,此时,是调用上述函数的最佳时机
void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
大家对这个函数应该都不陌生,毕竟,Qt Creator会自动为你生成它。不管你到底用还是不用。
原文链接:https://blog.csdn.net/dbzhang800/article/details/6709688