天天看點

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

随着Qt 6的釋出,cmake也正式宣告接管qmake的工作了。

在之前的一篇部落格裡我介紹了如何使用cmake管理你的qt項目,不過有一點我沒有講,那就是對國際化(i18n)的處理。

今天我們就來介紹下如何使用cmake+clion配置管理一個包含了國際化支援的項目。

準備工作

你需要準備下面的工具Qt 5.13+(我使用的是Qt 5.15.2)

CLion 2020.3+

GCC 9.0+ (最好支援c++17,最低要求是支援c++11)

其中GCC一般自己安裝的Qt會有附帶,否則在Windows上使用vs2019的編譯器也是可以的。

在Linux上如果不想自己下載下傳安裝Qt的話也可以使用系統倉庫打包好的:# ubuntu

sudo apt-get install build-essential libglu1-mesa-dev libpulse-dev libglib2.0-dev

sudo apt-get --no-install-recommends install libqt*5-dev qt*5-dev qml-module-qtquick-* qt*5-doc-html

# Arch/Manjato

sudo pacman -S base-devel

sudo pacman -S --needed qt5

選擇CLion的2020.3及以上版本是因為它提供了自帶的Qt項目模闆,省去了我們自己搭架構的麻煩。

當然如果你還在使用舊版CLion的話可以參考這篇文章配置Qt項目。

Qt 6在cmake的配置上是類似的,隻需要修改幾個函數的名稱即可,後面會提及。

建立項目

前置工作完成之後就可以建立項目了,如下圖所示:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

預設是c++14标準,我個人更喜歡用c++17,不過今天的例子使用c++14也是可以的。

建立完成後你會得到如下的CMakeLists.txt檔案:cmake_minimum_required(VERSION 3.17)

project(untitled1)

set(CMAKE_CXX_STANDARD 17)

set(CMAKE_AUTOMOC ON)

set(CMAKE_AUTORCC ON)

set(CMAKE_AUTOUIC ON)

set(QT_VERSION 5)

# 設定需要用到的Qt modules

set(REQUIRED_LIBS Core Gui Widgets)

set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets)

add_executable(${PROJECT_NAME} main.cpp)

# 提示你應該指定qt的cmake子產品的路徑,使用系統預設配置時無需關心

# 嫌這個警告啰嗦的話完全可以注釋掉或者删除

if (NOT CMAKE_PREFIX_PATH)

message(WARNING "CMAKE_PREFIX_PATH is not defined, you may need to set it "

"(-DCMAKE_PREFIX_PATH=\"path/to/Qt/lib/cmake\" or -DCMAKE_PREFIX_PATH=/usr/include/{host}/qt{version}/ on Ubuntu)")

endif ()

# 引入并連結用到的Qt modules

find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)

target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED})

CLion就是靠cmake來組織項目的,對于cmake的配置自然是必不可少的。

項目下還有一個提前寫入了Hello World示例的main.cpp。點選編譯運作你就會看到程式建立的視窗了。

下面我們就該進入正題了。

配置國際化支援

為了能更好地展示國際化,我們需要把例子代碼改成下面這樣:#include 

#include 

#include 

#include 

#include 

int main(int argc, char *argv[]) {

QApplication a(argc, argv);

QWidget window;

auto btn1 = new QPushButton{QObject::tr("click left button")};

QObject::connect(btn1, &QPushButton::clicked, [w = &window]() {

QMessageBox::information(w,

QObject::tr("clicked left button"),

QObject::tr("you clicked left button"));

});

auto btn2 = new QPushButton{QObject::tr("click right button")};

QObject::connect(btn2, &QPushButton::clicked, [w = &window]() {

QMessageBox::information(w,

QObject::tr("clicked right button"),

QObject::tr("you clicked right button"));

});

auto mainLayout = new QHBoxLayout;

mainLayout->addWidget(btn1);

mainLayout->addWidget(btn2);

window.setLayout(mainLayout);

window.show();

return QApplication::exec();

}

對于國際化的細節我們不過多介紹,這裡隻要知道需要翻譯的文字要用QObject::tr處理即可。

點選運作,你會看到界面上都是英文,因為現在我們還沒添加國際化支援:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

Qt的翻譯檔案由ts檔案和qm檔案組成,ts檔案用于人類進行翻譯工作,而Qt會根據ts檔案生成qm檔案,這是供程式使用的二進制檔案,人類無法直接閱讀。

是以在項目中我們隻需要關心ts檔案即可,下面我們建立一個lang子目錄,我們要在其中進行英語到漢語和日語的翻譯工作:mkdir lang

不過我們不用自己建立ts檔案,因為這是Qt能自動完成的。

下面我們修改一下cmake,讓他能支援國際化:cmake_minimum_required(VERSION 3.17)

project(untitled1)

set(CMAKE_CXX_STANDARD 17)

set(CMAKE_AUTOMOC ON)

set(CMAKE_AUTORCC ON)

set(CMAKE_AUTOUIC ON)

set(QT_VERSION 5)

set(REQUIRED_LIBS Core Gui Widgets)

set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets)

set(TS_FILES

${CMAKE_SOURCE_DIR}/lang/zh_CN.ts

${CMAKE_SOURCE_DIR}/lang/ja_JP.ts)

find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} LinguistTools REQUIRED)

qt5_create_translation(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_FILES})

add_executable(${PROJECT_NAME} main.cpp ${TS_FILES})

if (NOT CMAKE_PREFIX_PATH)

message(WARNING "CMAKE_PREFIX_PATH is not defined, you may need to set it "

"(-DCMAKE_PREFIX_PATH=\"path/to/Qt/lib/cmake\" or -DCMAKE_PREFIX_PATH=/usr/include/{host}/qt{version}/ on Ubuntu)")

endif ()

target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED})

哇,配置變得更長更吓人了,其實核心内容一共隻有這幾行:set(TS_FILES

${CMAKE_SOURCE_DIR}/lang/zh_CN.ts

${CMAKE_SOURCE_DIR}/lang/ja_JP.ts)

find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} LinguistTools REQUIRED)

qt5_create_translation(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_FILES})

add_executable(${PROJECT_NAME} main.cpp ${TS_FILES} ${QM_FILES})

第一行很好了解,把我們的需要的ts檔案的名字先設定到變量裡。

接着我們引入Qt5::LinguistTools,這不是c++庫,隻是一個幫助生成ts檔案和qm檔案的cmake子產品,是以不能連結到程式裡。

最後一行也很簡單,把ts檔案加入編譯程式的依賴項目裡,一旦發生改變就重新建構我們的程式。

關鍵在于qt5_create_translation這裡,這個函數會幫我們建立ts檔案,如果ts檔案已經存在就會更新ts檔案把新添加的翻譯追加進去;

到這步還沒結束,在更新完ts檔案後它會檢查ts檔案中是否有有效的翻譯資訊,如果有就在cmake的編譯目錄下生成和ts檔案同名的qm檔案。

中間的${CMAKE_CURRENT_SOURCE_DIR}就是指定從哪個目錄下的源檔案裡獲得需要翻譯的文本的。

注意,如果qm檔案最終沒被用到的話,那麼實際上不會被生成。為了能生成qm檔案是以我們把它加入了依賴,還有更好的辦法,後面介紹。

在Qt 6中我們隻需要把函數名改成qt_create_translation就行了。

如果你想自己建立和更新ts檔案,隻需要把函數換成qt5_add_translation,它會自動根據ts檔案生成qm檔案,不過要是沒有ts檔案存在他就會報錯。在Qt 6中它的名字會變為qt_add_translation。

上述的工作會在make的時候進行,比如這樣:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

添加翻譯

添加翻譯沒什麼好講的,你可以直接編輯ts檔案,因為它是xml格式的,編輯起來還是很容易的。

不過有時候翻譯需要參考文本和代碼的上下文,這時候就需要用到Qt Linguist了:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

具體的使用細節不再贅述,你可以參考園内和網上的其他優質文章。

編譯運作後你就會在建構目錄看到兩個qm檔案,這時候我們的程式還沒有完成國際化支援,在代碼中我們需要使用這些qm檔案:#include 

#include 

int main()

{

QApplication a(argc, argv);

QTranslator trans;

if (trans.load("./" + QLocale().name() + ".qm")) {

QCoreApplication::installTranslator(&trans);

}

...

return a.exec();

}

我們用QTranslator加載本地的qm檔案,QLocale的name方法正好可以傳回諸如“ja_JP”的名字。

我的系統預設設定是日語,是以運作程式會看到這樣的界面:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

如果你的環境是中文的,那麼顯示的界面也是中文的,這就是Qt的i18n國際化支援。

将多語言資源綁定程序式

到上一節結束我們其實就把i18n講完了,你完全可以打包程式的時候把qm檔案和程式放在一起,安裝的時候也放在同一目錄。

但這種方案還是有些缺點的:如果支援的語言比較多,那麼就會有大量的小檔案需要處理,難免會出錯;

需要把qm這種無關的檔案放進編譯依賴裡

對于有代碼潔癖的我來說第二點尤其忍不了,但是如果不用這些qm檔案的話最終是不會生成他們的,怎麼辦呢?

其實還有辦法,我們可以把這些qm檔案都內建到qrc裡。

首先在目錄下建立一個translations.qrc檔案:

zh_CN.qm

ja_JP.qm

這裡不用管資源檔案的路徑,因為我們還要對CMakeLists.txt做些修改處理這些問題:set(CMAKE_AUTORCC ON) # 一定得開啟rcc

# 注意這行

configure_file(translations.qrc ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)

...

add_executable(${PROJECT_NAME} main.cpp ${TS_FILES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)

需要注意的是configure_file那行,我們把qrc檔案原樣複制到了編譯目錄裡,因為qm檔案也是在那裡生成的。

現在qrc會被自動處理,然後我們的qm檔案作為其依賴項也能被自動生成了。

再次編譯項目,這次我們就能發現qm被生成了:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

因為使用了Qt的rcc把資源嵌入了程式,是以cpp代碼也得做一些調整:QTranslator trans;

-if (trans.load("./" + QLocale().name() + ".qm")) {

+if (trans.load(":/" + QLocale().name() + ".qm")) {

QCoreApplication::installTranslator(&trans);

}

注意.變成了:,這代表我們指定的路徑是嵌入資源的。

這樣國際化支援就徹底完成了,通過修改Linux的LANG環境變量我們可以自由設定程式的語言,效果如下:

qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援
qt linux 國際化,多語言,配置CLion管理Qt項目國際化支援

總結

現在我把完整的CMakeLists.txt貼上來作為參考,對于你自己的項目當然是要做調整的:cmake_minimum_required(VERSION 3.17)

project(untitled1)

set(CMAKE_CXX_STANDARD 17)

set(CMAKE_AUTOMOC ON)

set(CMAKE_AUTORCC ON)

set(CMAKE_AUTOUIC ON)

set(QT_VERSION 5)

set(REQUIRED_LIBS Core Gui Widgets)

set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets)

set(TS_FILES

${CMAKE_SOURCE_DIR}/lang/zh_CN.ts

${CMAKE_SOURCE_DIR}/lang/ja_JP.ts)

find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} LinguistTools REQUIRED)

configure_file(translations.qrc ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)

qt5_create_translation(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_FILES})

add_executable(${PROJECT_NAME} main.cpp ${TS_FILES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)

message(${QM_FILES})

target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED})

不過這種方案也不是沒有問題,那就是每次隻能在編譯期間更新ts檔案,這點需要注意。

如果想要看更具體的項目是如何配置i18n的,我這也有一個例子,如果不嫌棄覺得有幫助的話可以star一下。