1. 程式人生 > >亂談Qt程式之i18n的實現(從C++到Qt)

亂談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()
QCoreApplication::translate()

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