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.最后编译、运行,效果和使用绝对路径一样。

相关推荐
Shadow(⊙o⊙)1 小时前
C++常见错误解析2.0
开发语言·数据结构·c++·后端·学习·算法
谢谢 啊sir2 小时前
L2-057 姥姥改作业 - java
java·开发语言
l1t2 小时前
duckdb excel插件和rusty_sheet插件在python中的不同表现
开发语言·python·excel
人道领域2 小时前
【黑马点评日记】高并发秒杀:库存超卖与锁机制解析
java·开发语言·redis·spring·intellij-idea
lsx2024062 小时前
《jEasyUI 创建树形下拉框》
开发语言
minji...2 小时前
Linux 网络套接字编程(一)端口号port,socket套接字,socket,bind,socket 通用结构体
linux·运维·服务器·开发语言·网络
2301_814809862 小时前
踩坑实战pywebview:用 Python + Web 技术打造轻量级桌面应用
开发语言·前端·python
南境十里·墨染春水2 小时前
C++流类库 字符串流
开发语言·c++