简介

上一篇博文已经说过如何编写支持多语言的Qt 命令行应用,这一篇说说Qt GUI 应用多语言支持的坑。

本人喜欢用代码来写布局,而不是用 Qt Designer 来设计布局,手写布局比 Qt Desiner 布局有以下优点:

  1. 手工布局,所想即所见,
  2. 源代码方式修改布局非常方便,只需要拷贝代码、注释代码就行,如果用 Qt Designer 修改原有布局,简直要人老命,拖放一下 widget 就会打乱了原有的 layout;

本文示例程序是一个GUI应用,手写布局,在工具栏中添加两个 QAction,可以切换中文、英文界面,主界面一个标签显示文本,一个按钮用来退出应用。

英文界面如下:

中文界面如下:

开发环境及步骤

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 ,但是没有连接信号和槽函数。

布局完成调试看看是否布局合理。

第三步 翻译并添加到资源

  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,只有点击了对应的工具条才切换对应的语言,在主窗口的构造方法中连接信号和槽函数:

            // 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

出处:https://www.cnblogs.com/LinTeX9527/p/11015060.html

本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Qt 编写应用支持多语言版本--一个GUI应用示例的更多相关文章

  1. go语言的一个gui 开源 项目 https://github.com/andlabs/ui

    go语言的一个gui 开源 项目  https://github.com/andlabs/ui 1 安装  mingw-w64  链接地址: http://mingw-w64.sourceforge. ...

  2. Qt编写控件属性设计器

    一.前言 自从研究Qt编写自定义控件以来,一发不可收拾,越多越多人有类似的需求找我定制控件,陆陆续续写了上百个控件,目前已超过150个,于是逐渐衍生了另外一个需求,提供一个控件属性设计器,类似QtDe ...

  3. Qt编写项目作品大全(自定义控件+输入法+大屏电子看板+视频监控+楼宇对讲+气体安全等)

    一.自定义控件大全 (一).控件介绍 超过160个精美控件,涵盖了各种仪表盘.进度条.进度球.指南针.曲线图.标尺.温度计.导航条.导航栏,flatui.高亮按钮.滑动选择器.农历等.远超qwt集成的 ...

  4. Qt编写控件属性设计器11-导入xml

    一.前言 上一篇文章负责把设计好的控件数据导出到了xml文件,本偏文章负责把导出的xml数据文件导入,然后在画布上自动生成对应的控件,Qt内置的xml数据解析功能,非常强大,都封装在QtXml组件中, ...

  5. Qt编写控件属性设计器10-导出xml

    一.前言 能够导出控件布局和属性设置数据到xml文件或者其他文件,也是一个非常实用的功能,类似于QtDesigner中把页面设计好以后生成的.ui结尾的文件,其实就是xml文件,按照约定的规则存储好控 ...

  6. Qt编写控件属性设计器12-用户属性

    一.前言 用户属性是后面新增加的一个功能,自定义控件如果采用的Q_PROPERTY修饰的属性,会自动识别到属性栏中,这个一般称为控件属性,在组态设计软件中,光有控件本身的控件属性还是不够的,毕竟这些属 ...

  7. Qt编写控件属性设计器9-数据库采集

    一.前言 数据库作为数据源,在很多组态软件中使用非常多,指定数据库类型,填写好数据库连接信息,指定对应的数据库表和字段,采集间隔,程序按照采集间隔自动采集数据库数据,绑定到界面上的控件赋值显示即可.使 ...

  8. Qt编写控件属性设计器8-网络采集

    一.前言 上一篇文章已经打通了数据源之一的串口采集,这次要说的是网络采集,网络通信目前用的最多的是三种,TCP/UDP/HTTP,其中tcp通信又包括了客户端服务端两种,tcp通信才用了多次握手机制不 ...

  9. Qt编写控件属性设计器7-串口采集

    一.前言 数据源是组态软件的核心灵魂,少了数据源,组态就是个花架子没卵用,一般数据源有三种方式获取,串口.网络.数据库,至于数据规则是什么,这个用户自己指定,本设计器全部采用第一个字节作为数据来演示. ...

随机推荐

  1. Java知识点脑图

    做服务器开发有十几年了,其中大部分用到的都是Java服务器开发,从JDK1.4到现在的JDK1.8,从基本的Java Application到 J2EE(JBOSS,Glassfish),OSGI,到 ...

  2. _bzoj1002 [FJOI2007]轮状病毒【瞎搞】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1002 这种题真是有毒,很多叼一点的都用matrix tree定理推出了递推公式,也有一些用好 ...

  3. 前缀+排序 HDOJ 4311 Meeting point-1

    题目传送门 题意:给一些坐标轴上的点,选一个点,使得其他点到该点曼哈顿距离和最小 分析:这题有很强的技巧性,直接计算每个点的曼哈顿距离和是不可行的.这里用到了前缀的思想,先对点按照x从左到右排序,p[ ...

  4. 转 Docker 组件如何协作?- 每天5分钟玩转容器技术(8)

    http://www.cnblogs.com/CloudMan6/p/6774519.html 记得我们运行的第一个容器吗?现在通过它来体会一下 Docker 各个组件是如何协作的. 容器启动过程如下 ...

  5. C# PropertyInfo(官网)

    using System; using System.Reflection; class Module1 { public static void Main() { // This variable ...

  6. visual studio 2015 key vs2015密钥

    Visual Studio Professional 2015简体中文版(专业版)KEY:HMGNV-WCYXV-X7G9W-YCX63-B98R2Visual Studio Enterprise 2 ...

  7. 搞定redis面试--Redis的过期策略?手写一个LRU?

    1 面试题 Redis的过期策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现? 2 考点分析 1)我往redis里写的数据怎么没了? 我们生产环境的redis怎么经常会丢掉一些数据?写进去了 ...

  8. 如何理解JavaScript的单线程

    JS的本质是单线程的.这点区别于JAVA的两个线程并发 但是,平时的JS,确实是同时运行很多任务,这又是怎么回事???? First,js的代码分为两种.同步代码和异步代码. console.log( ...

  9. 移动端展示pdf(在线打开pdf)

    需求:在手机微信浏览器或者其他浏览器中打开pdf 准备:前端插件:查找pdf.js  官网地址:http://mozilla.github.io/pdf.js/ 在官网中下载demo 注释:pdf的d ...

  10. [Android]Android Design之Navigation Drawer

    概述 在以前ActionBar是Android 4.0的独有的,后来的ActionBarSherlock的独步武林,对了还有SlidingMenu,但是这个可以对4.0下的可以做很好的适配.自从Goo ...