Qt 編寫應用支持多語言版本--一個GUI應用示例

簡介

上一篇博文已經說過如何編寫支持多語言的Qt 命令行應用,這一篇說說Qt GUI 應用多語言支持的坑。html

本人喜歡用代碼來寫佈局,而不是用 Qt Designer 來設計佈局,手寫佈局比 Qt Desiner 佈局有如下優勢:c++

  1. 手工佈局,所想即所見,
  2. 源代碼方式修改佈局很是方便,只須要拷貝代碼、註釋代碼就行,若是用 Qt Designer 修改原有佈局,簡直要人老命,拖放一下 widget 就會打亂了原有的 layout;

本文示例程序是一個GUI應用,手寫佈局,在工具欄中添加兩個 QAction,能夠切換中文、英文界面,主界面一個標籤顯示文本,一個按鈕用來退出應用。編程

英文界面以下:ide

中文界面以下:函數

開發環境及步驟

VS 版本: vs2017 社區版
Qt 版本: 5.11.1
Qt VS tools: 2.3.2工具

第一步 新建 HelloQt 工程

默認的會建立 UI 文件,直接編譯工程,複製編譯產生的 ui_HelloQt.h 文件,重命名爲 myUI_HelloQt.h 以這個文件爲模板,手工佈局。佈局

第二步 在 setupUI() 中實現佈局

使用MVC編程模型,只在 myUI_HelloQt.h 文件中實現GUI,這裏雖然新建了兩個 QAction 和一個 QPushButton ,可是沒有鏈接信號和槽函數。ui

佈局完成調試看看是否佈局合理。this

第三步 翻譯並添加到資源

  1. 新建翻譯文件 helloqt_zh.ts
  2. 用工具 lupdate 提取字符串,並語言家來翻譯;
  3. 用工具 lrelease 發佈 QM 文件,即 helloqt_zh.qm 文件;
  4. 在資源文件 HelloQt.qrc 中添加 QM 文件,並把資源重命名爲 :/translations/helloqt_zh
    如圖所示:

    上面的步驟只是準備了翻譯字符串,實際上應用程序並不能切換語言,還須要執行下面的步驟:
  5. 新建一個成員變量 QTranslator *language_zh;,並在主窗口的構造方法中加載對應的QM文件, language_zh->load(":/translations/helloqt_zh");
  6. 加載了 QM 文件還不能切換多語言,須要安裝才行。到底什麼時候安裝呢?這時候想起了前面的兩個 QAction,只有點擊了對應的工具條才切換對應的語言,在主窗口的構造方法中鏈接信號和槽函數:spa

    // signals and slots
        QObject::connect(ui.act_language_zh, SIGNAL(triggered(bool)), this, SLOT(on_trigger_language_zh(bool)));
            QObject::connect(ui.act_language_en, SIGNAL(triggered(bool)), this, SLOT(on_trigger_language_en(bool)));
            QObject::connect(ui.btn_quit, SIGNAL(clicked(bool)), this, SLOT(on_btn_quit_clicked(bool)));
  7. 分別實現 action 對應的槽函數,加載中文時安裝 QTranslation,加載英文時卸載,以下面的代碼所示:

    void HelloQt::on_trigger_language_zh(bool checked)
    {
        qDebug() << tr("Chinese") << endl;
        qApp->installTranslator(language_zh);
    }
    
    void HelloQt::on_trigger_language_en(bool checked)
    {
        qDebug() << tr("English") << endl;
        qApp->removeTranslator(language_zh);
    }
  8. 執行完上一步你就急着編譯查看結果,點擊兩個工具欄你卻發現並不能切換語言,爲何呢?由於切換語言的事件尚未處理呢。覆蓋 changeEvent() 便可:

    void HelloQt::changeEvent(QEvent *event)
    {
        if (event->type() == QEvent::LanguageChange) {
            ui.retranslateUi(this);
            } else {
            QMainWindow::changeEvent(event);
        }
    }

至此兩個工具欄終於能夠正常地切換語言顯示了。

深刻一點

你能夠看到工程中至少出現了3次 retranslateUi() 函數,一次是定義,兩次是調用,第一次調用是在UI 初始化中,第二次調用是 changeEvent() 中切換語言,它所作的工做就是把UI widgets 上顯示的字符串替換爲特定語言的字符串。例如給主窗口設定窗口標題 HelloQtClass->setWindowTitle(QApplication::translate("HelloQtClass", "HelloQt", nullptr)); 最關鍵的是裏面的 [static] QString QCoreApplication::translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1),這個函數纔是負責翻譯語言的。

第一個參數 context 理解爲上下文,什麼是上下文,Qt 助手說它就是一個類名。不一樣的字符串會分配到不一樣的上下文。分配規則是 tr() 是用哪一個類名來限定的,以下面的代碼,分別調用 QObject::tr("install Chinese")HelloQt::tr("install English") 那麼就有兩個上下文,分別是 QObjectHelloQt,這一點也能夠從語言家看到。

void HelloQt::on_trigger_language_zh(bool checked)
{
    qDebug() << QObject::tr("install Chinese") << endl;
    qApp->installTranslator(language_zh);
}

void HelloQt::on_trigger_language_en(bool checked)
{
    qDebug() << HelloQt::tr("install English") << endl;
    qApp->removeTranslator(language_zh);
}

語言家界面的顯示:

第二個參數 sourceText 就是源代碼中以 tr() 括起來的字符串。

後面的兩個參數暫時沒有用到,忽略。

一張圖說明上下文,源字符串兩者的關係,若是代碼中某個字符串被刪除了可是 TS 文件中還存在,並且從此不須要這個翻譯,就能夠用記事本打開 TS 文件,直接刪除以 vanished 標記的翻譯:

回到 retranslateUi() 函數,這是一個自定義的函數,用戶在這裏翻譯GUI界面,若是發現GUI上某個 widget 語言沒有變化,就須要在這裏翻譯,以下面的代碼翻譯標籤和按鈕的文本,以及兩個工具欄的文本:

```C++
    // add your own translate here.
    act_language_zh->setText(QApplication::translate("QObject", "Chinese", nullptr));
    act_language_en->setText(QApplication::translate("QObject", "English", nullptr));
    lbl_hello->setText(QApplication::translate("QObject", "<h2><i>Hello</i><font color=red>Qt!</font></h2>", nullptr));
    btn_quit->setText(QApplication::translate("QObject", "&Quit", nullptr));
```

上面的代碼中標籤文本是一個 HTML 代碼,這個也能夠翻譯的, HTML 樣式保留,只須要替換裏面的 HelloQt! 便可。

源代碼

只列出關鍵的源代碼,首先是佈局文件 myUI_HelloQt.h

#pragma once
#ifndef MYUI_HELLOQT_H
#define MYUI_HELLOQT_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QWidget>
#include <QtWidgets/QMenu>
#include <QtWidgets/QAction>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>

QT_BEGIN_NAMESPACE

class Ui_HelloQtClass
{
public:
    QMenuBar *menuBar;
    QToolBar *mainToolBar;
    QWidget *centralWidget;
    QStatusBar *statusBar;
    QAction *act_language_zh;
    QAction *act_language_en;
    QLabel *lbl_hello;
    QPushButton *btn_quit;


    void setupUi(QMainWindow *HelloQtClass)
    {
        if (HelloQtClass->objectName().isEmpty())
            HelloQtClass->setObjectName(QStringLiteral("HelloQtClass"));
        //HelloQtClass->resize(600, 400);
        HelloQtClass->setFixedSize(400, 160);
        menuBar = new QMenuBar(HelloQtClass);
        menuBar->setObjectName(QStringLiteral("menuBar"));
        HelloQtClass->setMenuBar(menuBar);
        mainToolBar = new QToolBar(HelloQtClass);
        mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
        HelloQtClass->addToolBar(mainToolBar);
        centralWidget = new QWidget(HelloQtClass);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));

        // add actions here.
        act_language_zh = new QAction(QObject::tr("Chinese"), HelloQtClass);
        act_language_en = new QAction(QObject::tr("English"), HelloQtClass);
        mainToolBar->addAction(act_language_zh);
        mainToolBar->addAction(act_language_en);

        // add your widgets here.
        lbl_hello = new QLabel(HelloQtClass);
        lbl_hello->setText(QObject::tr("<h2><i>Hello</i><font color=red>Qt!</font></h2>"));
        lbl_hello->setAlignment(Qt::AlignHCenter);
        
        btn_quit = new QPushButton(QObject::tr("&Quit"), HelloQtClass);
                
        QVBoxLayout *vLayout = new QVBoxLayout;
        vLayout->addWidget(lbl_hello);
        vLayout->addWidget(btn_quit);
        centralWidget->setLayout(vLayout);

        HelloQtClass->setCentralWidget(centralWidget);
        statusBar = new QStatusBar(HelloQtClass);
        statusBar->setObjectName(QStringLiteral("statusBar"));
        HelloQtClass->setStatusBar(statusBar);

        retranslateUi(HelloQtClass);

        QMetaObject::connectSlotsByName(HelloQtClass);
    } // setupUi

    void retranslateUi(QMainWindow *HelloQtClass)
    {
        // NOTE: the first parameter `context` in translate() must match the `context` in Qt Linguist.
        // [static] QString QCoreApplication::translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1)
        HelloQtClass->setWindowTitle(QApplication::translate("HelloQtClass", "HelloQt", nullptr));

        // add your own translate here.
        act_language_zh->setText(QApplication::translate("QObject", "Chinese", nullptr));
        act_language_en->setText(QApplication::translate("QObject", "English", nullptr));
        lbl_hello->setText(QApplication::translate("QObject", "<h2><i>Hello</i><font color=red>Qt!</font></h2>", nullptr));
        btn_quit->setText(QApplication::translate("QObject", "&Quit", nullptr));
    } // retranslateUi

};

namespace Ui {
    class HelloQtClass : public Ui_HelloQtClass {};
} // namespace Ui

QT_END_NAMESPACE

#endif // MYUI_HELLOQT_H

而後是 HelloQt.h

#pragma once

#include <QtWidgets/QMainWindow>
#include <QTranslator>
#include "myUI_HelloQt.h"

class HelloQt : public QMainWindow
{
    Q_OBJECT

public:
    HelloQt(QWidget *parent = Q_NULLPTR);

public slots:
    void on_trigger_language_zh(bool checked);
    void on_trigger_language_en(bool checked);

protected slots:
    void on_btn_quit_clicked(bool clicked);
    

protected:
    void changeEvent(QEvent *event) override;

private:
    Ui::HelloQtClass ui;
    QTranslator *language_zh;
};

而後是 HelloQt.c

#include <QDebug>
#include "HelloQt.h"

HelloQt::HelloQt(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    
    // load translation
    language_zh = new QTranslator(this);
    language_zh->load(":/translations/helloqt_zh");

    // signals and slots
    QObject::connect(ui.act_language_zh, SIGNAL(triggered(bool)), this, SLOT(on_trigger_language_zh(bool)));
    QObject::connect(ui.act_language_en, SIGNAL(triggered(bool)), this, SLOT(on_trigger_language_en(bool)));
    QObject::connect(ui.btn_quit, SIGNAL(clicked(bool)), this, SLOT(on_btn_quit_clicked(bool)));
}

void HelloQt::on_trigger_language_zh(bool checked)
{
    qDebug() << QObject::tr("install Chinese") << endl;
    qApp->installTranslator(language_zh);
}

void HelloQt::on_trigger_language_en(bool checked)
{
    qDebug() << HelloQt::tr("install English") << endl;
    qApp->removeTranslator(language_zh);
}

void HelloQt::on_btn_quit_clicked(bool clicked)
{
    qApp->quit();
}

//************************************
// Method:    changeEvent
// FullName:  HelloQt::changeEvent
// Access:    protected 
// Returns:   void
// Qualifier:
// Parameter: QEvent * event
// Description: change language.
//************************************
void HelloQt::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange) {
        ui.retranslateUi(this);
    } else {
        QMainWindow::changeEvent(event);
    }
}

而後是翻譯文件 helloqt_zh.ts

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
    <name>HelloQt</name>
    <message>
        <location filename="HelloQt.cpp" line="27"/>
        <source>install English</source>
        <oldsource>English</oldsource>
        <translation>安裝英文</translation>
    </message>
</context>
<context>
    <name>HelloQtClass</name>
    <message>
        <location filename="myUI_HelloQt.h" line="80"/>
        <source>HelloQt</source>
        <translation>你好Qt</translation>
    </message>
</context>
<context>
    <name>QObject</name>
    <message>
        <location filename="myUI_HelloQt.h" line="49"/>
        <location filename="myUI_HelloQt.h" line="83"/>
        <source>Chinese</source>
        <translation>中文</translation>
    </message>
    <message>
        <location filename="myUI_HelloQt.h" line="50"/>
        <location filename="myUI_HelloQt.h" line="84"/>
        <source>English</source>
        <translation>英文</translation>
    </message>
    <message>
        <location filename="myUI_HelloQt.h" line="56"/>
        <location filename="myUI_HelloQt.h" line="85"/>
        <source>&lt;h2&gt;&lt;i&gt;Hello&lt;/i&gt;&lt;font color=red&gt;Qt!&lt;/font&gt;&lt;/h2&gt;</source>
        <translation>&lt;h2&gt;&lt;i&gt;你好&lt;/i&gt;&lt;font color=red&gt;Qt!&lt;/font&gt;&lt;/h2&gt;</translation>
    </message>
    <message>
        <location filename="myUI_HelloQt.h" line="59"/>
        <location filename="myUI_HelloQt.h" line="86"/>
        <source>&amp;Quit</source>
        <translation>&amp;退出</translation>
    </message>
    <message>
        <location filename="HelloQt.cpp" line="21"/>
        <source>install Chinese</source>
        <translation>安裝中文</translation>
    </message>
</context>
</TS>

而後是資源文件 HelloQt.qrc

<RCC>
    <qresource prefix="/translations">
        <file alias="helloqt_zh">helloqt_zh.qm</file>
    </qresource>
</RCC>

聲明

歡迎轉載,請註明出處和做者,同時保留聲明。
做者:LinTeX9527
出處:http://www.javashuo.com/article/p-hbpvjhvm-gr.html 本博客的文章如無特殊說明,均爲原創,轉載請註明出處。如未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索