乱谈Qt程序之i18n的实现(从C++到Qt)
转载自:http://blog.csdn.net/dbzhang800/article/details/6709688
嘿嘿,本文只是试图从纯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会自动为你生成它。不管你到底用还是不用。