QT:语言翻译

目录

需求说明

概念说明

原理介绍

代码举例

lineEdit控件特别说明


需求说明

QT项目完成后,如果想给国外使用,势必要添加一个语言选择功能。选择中文时,页面展示中文,选择英语时,页面展示英文。那该如何实现呢?

方案1:手动设置控件文本。以QPushButton* btn为例,名称是"姓名"。设置中文时,btn->setText("姓名"),设置英文时,btn->setText("name")。

这个方案很容易想到,也很容易实现。那有没有问题呢?有。

从功能方面看,一点问题没有,需求能够实现。但这种方案过于繁琐,说直白些,就是这是个笨方法。试想下,如果我要翻译成十几个国家的语言,那是不是在代码中写十几次翻译逻辑,代码会变得很臃肿。同时也不好维护,因为每新增一个控件,都要去改动代码。

能不能进一步优化呢?可以。我们可以借鉴配置文件的功能。比如我们通常会把不确定的、未知的或变化的东西存在配置文件中,如ip、串口名等。程序启动后,读取配置文件,是什么就加载什么。

现在把语言功能抽象出来,一个国家语言对应一个配置文件。这个配置文件中存储所有需要翻译的控件,及其要展示的名称。当你切换语言时,我就加载对应的文件,遍历里面所有的控件,并将它们的名字重新设置为要翻译的名字。当新增控件或新增语言时,只需要修改配置文件即可。代码几乎不在需要改动。到这里,恭喜你,已经实现了一个更为通用的标准方案。也就是下面所讲的方案2。

方案2:使用QT框架自带的语言翻译功能。

概念说明

*.ts(zh_CN.ts/en_US.ts):

ts文件就是普通文本,里面存在翻译的内容。xml格式,人能看懂,可以编辑。如:

cpp 复制代码
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en">
<context>
    <name>MainWindow</name>
    <message>
        <location filename="../mainwindow.ui" line="14"/>
        <source>MainWindow</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="27"/>
        <source>中文</source>
        <translation>zhongwen</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="40"/>
        <source>英语</source>
        <translation>english1111</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="53"/>
        <source>这是一个测试文本</source>
        <translation>this is a text</translation>
    </message>
    <message>
        <location filename="../mainwindow.ui" line="66"/>
        <source>数学</source>
        <translation>math</translation>
    </message>
    <message>
        <location filename="../mainwindow.cpp" line="10"/>
        <source>姓名</source>
        <translation>name</translation>
    </message>
</context>
</TS>

*.qm(zh_CN.qm/en_US.qm):

qm文件不是普通文本,是二进制文件,给程序看的。它是由ts文件编译而来。所以两者一一对应。程序代码中加载的就是qm文件而不是ts文件。

lupdate/lrelease:

QT自带的工具。lupdate用来生成或更新ts文件。比如新增控件,或代码中用tr时。都需要重新lupdate下,这样ts文件更新后,就可以在里面查看和编辑新增内容。lrelease是用来把ts文件转成qm文件。页面位置如下:

翻译过程简要为:

代码中 tr("姓名") → lupdate更新 → en_US.ts →把里面的姓名翻译成name → lrelease发布→代码加载en_US.qm→程序运行→显示英文。

原理介绍

翻译的文件有了,QT内部又是如何加载和调用呢?这就涉及到了ui_mainwindow.h。当我们创建一个UI(如mainwindow.ui),在上面添加控件,设置布局等等后。执行编译,QT便会自动创建一个ui_mainwindow.h文件。里面有两个主要函数setupUi和retranslateUi。

setupUi:

作用:初始化整个界面。如创建按钮、标签、输入框,并设置位置、大小、布局、图标、样式等。随后会调用 retranslateUi 设置文字,整个程序只调用 1 次(构造函数里)。

retranslateUi:

作用:只刷新所有文本翻译。不创建控件,不改变布局、大小、样式等。切换语言时必须调用,可以调用 N 次。ui_mainwindow.h内容如下:

cpp 复制代码
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.12.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
    QWidget *centralWidget;
    QPushButton *chBtn;
    QPushButton *enBtn;
    QLabel *label;
    QLabel *label_2;
    QLabel *name;
    QPushButton *pushButton;
    QTabWidget *tabWidget;
    QWidget *tab;
    QLineEdit *lineEdit;
    QWidget *tab_2;
    QLineEdit *lineEdit_2;
    QMenuBar *menuBar;
    QToolBar *mainToolBar;
    QStatusBar *statusBar;

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
        MainWindow->resize(649, 499);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
        chBtn = new QPushButton(centralWidget);
        chBtn->setObjectName(QString::fromUtf8("chBtn"));
        chBtn->setGeometry(QRect(50, 50, 75, 23));
        enBtn = new QPushButton(centralWidget);
        enBtn->setObjectName(QString::fromUtf8("enBtn"));
        enBtn->setGeometry(QRect(180, 50, 75, 23));
        label = new QLabel(centralWidget);
        label->setObjectName(QString::fromUtf8("label"));
        label->setGeometry(QRect(70, 140, 181, 31));
        label_2 = new QLabel(centralWidget);
        label_2->setObjectName(QString::fromUtf8("label_2"));
        label_2->setGeometry(QRect(70, 180, 181, 31));
        name = new QLabel(centralWidget);
        name->setObjectName(QString::fromUtf8("name"));
        name->setGeometry(QRect(70, 230, 181, 31));
        pushButton = new QPushButton(centralWidget);
        pushButton->setObjectName(QString::fromUtf8("pushButton"));
        pushButton->setGeometry(QRect(370, 100, 75, 23));
        tabWidget = new QTabWidget(centralWidget);
        tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
        tabWidget->setGeometry(QRect(130, 190, 451, 181));
        tab = new QWidget();
        tab->setObjectName(QString::fromUtf8("tab"));
        lineEdit = new QLineEdit(tab);
        lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
        lineEdit->setEnabled(true);
        lineEdit->setGeometry(QRect(10, 40, 131, 41));
        lineEdit->setText(QString::fromUtf8("0"));
        tabWidget->addTab(tab, QString());
        tab_2 = new QWidget();
        tab_2->setObjectName(QString::fromUtf8("tab_2"));
        lineEdit_2 = new QLineEdit(tab_2);
        lineEdit_2->setObjectName(QString::fromUtf8("lineEdit_2"));
        lineEdit_2->setGeometry(QRect(10, 40, 131, 41));
        tabWidget->addTab(tab_2, QString());
        MainWindow->setCentralWidget(centralWidget);
        menuBar = new QMenuBar(MainWindow);
        menuBar->setObjectName(QString::fromUtf8("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 649, 23));
        MainWindow->setMenuBar(menuBar);
        mainToolBar = new QToolBar(MainWindow);
        mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
        MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
        statusBar = new QStatusBar(MainWindow);
        statusBar->setObjectName(QString::fromUtf8("statusBar"));
        MainWindow->setStatusBar(statusBar);

        retranslateUi(MainWindow);

        tabWidget->setCurrentIndex(0);


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

    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr));
        chBtn->setText(QApplication::translate("MainWindow", "\344\270\255\346\226\207", nullptr));
        enBtn->setText(QApplication::translate("MainWindow", "\350\213\261\350\257\255", nullptr));
        label->setText(QApplication::translate("MainWindow", "\350\277\231\346\230\257\344\270\200\344\270\252\346\265\213\350\257\225\346\226\207\346\234\254", nullptr));
        label_2->setText(QApplication::translate("MainWindow", "\346\225\260\345\255\246", nullptr));
        name->setText(QString());
        pushButton->setText(QApplication::translate("MainWindow", "PushButton", nullptr));
        tabWidget->setTabText(tabWidget->indexOf(tab), QApplication::translate("MainWindow", "Tab 1", nullptr));
        lineEdit_2->setText(QApplication::translate("MainWindow", "0", nullptr));
        tabWidget->setTabText(tabWidget->indexOf(tab_2), QApplication::translate("MainWindow", "Tab 2", nullptr));
    } // retranslateUi

};

namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAINWINDOW_H

从上面可以看出,文本显示靠的是retranslateUi函数。所以在切换语言时,只需要重新调用一次retranslateUi函数即可。

代码举例

1.创建一个简单项目Language

2.手动创建lang目录,用来存储后续的ts、qm文件

3.修改Language.pro文件,添加翻译文件

cpp 复制代码
# 生成翻译文件
TRANSLATIONS += \
    lang/zh_CN.ts \
    lang/en_US.ts

4.点击lupdate更新,lang目录下便会生成两个ts文件

5.编辑en_US.ts文件,填写翻译结果。用linguist打开。

6.编辑里面的内容,介绍如下

需要翻译的源文,翻译后要点击上面的对号确认,点击后文本前面会由问号变为对号。表示已经生效。全部改为后如下:

然后点击保存按钮,最后退出。

7.点击发布lrelease,生成qm文件

8.核心代码实现

mainWindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QTranslator>

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void switchLanguage(bool isEnglish);

private slots:
    void on_chBtn_clicked();

    void on_enBtn_clicked();

private:
    Ui::MainWindow *ui;
    QTranslator *g_translator;
    int val=100;
};

#endif // MAINWINDOW_H

mainWindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->lineEdit->setText(QString::number(100));

    // 默认加载中文
    g_translator = new QTranslator(this);
    g_translator->load("E:/code/Qt/Language/lang/zh_CN.qm");
    qApp->installTranslator(g_translator);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_chBtn_clicked()
{
    switchLanguage(false);
}

void MainWindow::on_enBtn_clicked()
{
    switchLanguage(true);
}

void MainWindow::switchLanguage(bool isEnglish)
{
    bool res;
    if (isEnglish) {
         res =g_translator->load("E:/code/Qt/Language/lang/en_US.qm");
    } else {
        res = g_translator->load("E:/code/Qt/Language/lang/zh_CN.qm");
    }
     qDebug() << "加载是否成功:" << res;
    // 关键:刷新界面
    ui->retranslateUi(this);
}

9.运行效果如下:

lineEdit控件特别说明

从上面的演示结果看 ,语言翻译功能基本实现。不过上面也预留了一个"坑",就是lineEdit初始值是100,但在语言切换后,居然变成0了。这显然不对,因为我只想修改文本,不想改动lineEdit数值。

导致这个现象的原因,可以在retranslateUi中找到。

我们看到,每次执行retranslateUi后,lineEdit也会设置下,结果就是0。实际上,在UI中lineEdit值设置多少,执行retranslateUi后,不管原来展示多少,最终就会展示初始设置的值。也就是,初始值0,切换后展示0,初始值100,切换后展示100,初始值空,切换后展示空。

知道了原因,就好解决了。既然翻译时候,会调用lineEdit的setText,那我不让他设置不就可以了。答案是很容易做到。只需将lineEdit属性的text的可翻译取消勾选即可。

原来页面:

取消勾选:

重新编译后我们再次查看ui_mainwindow.h,可以看到刚才lineEdit->setText代码已经没有了。也就是说,再次调用retranslateUi后,lineEdit结果不会再改变。展示如下:

添加资源

上面加载qm使用的是绝对路径,生产部署时候肯定不行,现在改为加载资源方式。

1.右键项目,添加一个新的资源文件,如命名lang.qrc。路径选择lang目录

此时会发现,lang目录下已经生成了lang.qrc

2.添加资源前缀和资源文件

添加→添加前缀→改为/lang

添加→添加文件→选择lang目录下的en_US.qm和zh_CN.qm两个文件

最后ctrl s保存

3.修改代码,将其中的绝对路径地址改为资源路径地址

mainWindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->lineEdit->setText(QString::number(100));

    // 默认加载中文
    g_translator = new QTranslator(this);
    g_translator->load(":/lang/zh_CN.qm");
    qApp->installTranslator(g_translator);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_chBtn_clicked()
{
    switchLanguage(false);
}

void MainWindow::on_enBtn_clicked()
{
    switchLanguage(true);
}

void MainWindow::switchLanguage(bool isEnglish)
{
    bool res;
    if (isEnglish) {
         res =g_translator->load(":/lang/en_US.qm");
    } else {
        res = g_translator->load(":/lang/zh_CN.qm");
    }
     qDebug() << "加载是否成功:" << res;
    // 关键:刷新界面
    ui->retranslateUi(this);
}

4.最后编译、运行,效果和使用绝对路径一样。

相关推荐
用户8055336980310 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner10 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript