亂談Qt程式之i18n的實現(從C++到Qt)
嘿嘿,本文只是試圖從純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