嘿嘿,本文只是试图从纯C++的角度告诉你 Qt 的国际化是到底是怎么一回事(注:本文只看一个点,不看面)。而不会一步一步告诉你Qt的国际化/本地化怎么用(这些东西在Qt Manual、论坛 以及 相关书籍中介绍的够多了)。
Qt 国际化所做的就是这点东西:
没有怎么办,就按照某种编码将参数窄字符串变成QString呗
至于动态翻译:点一下菜单,界面文字全改变,这在Qt中是相当容易实现的东西。
其实,根本就没有动态这回事。所谓的动态翻译,就是我们加载了一个新的翻译文件,然后将界面的文字用新的重新设置了一遍。
我们找个超简单的C++的小例子看看,并一步一步让它变的复杂一点点。
看一个 hello world 的例子:
#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
如何翻译这个程序呢?
源单词和目标单词的对应关系,我们就叫它词典好了。
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 |
我们的例子 |
|
lupdate/lrelease/... |
Map/Map/Map |
生成翻译/词典文件 |
QTranslator |
globalMap |
安装翻译文件 |
QObject::tr() |
tr() |
使用翻译文件 |
从根本上说,Qt 国际化所做的就是这点东西:
没有怎么办,就按照某种编码将参数窄字符串变成QString呗?
注意,tr就是一个将 const char * 变成 QString 的函数:
QString QObject::tr ( const char * sourceText,...)
对于tr,我们在Qt中translate、tr关系 与中文问题 有了比较详细的讨论,此处就不重复了。
点一下菜单,界面文字全改变,这在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上,"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会自动为你生成它。不管你到底用还是不用。