Qt 编写应用支持多语言版本--一个GUI应用示例
简介
上一篇博文已经说过如何编写支持多语言的Qt 命令行应用,这一篇说说Qt GUI 应用多语言支持的坑。
本人喜欢用代码来写布局,而不是用 Qt Designer 来设计布局,手写布局比 Qt Desiner 布局有以下优点:
- 手工布局,所想即所见,
- 源代码方式修改布局非常方便,只需要拷贝代码、注释代码就行,如果用 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,但是没有连接信号和槽函数。
布局完成调试看看是否布局合理。
第三步 翻译并添加到资源
- 新建翻译文件 helloqt_zh.ts;
- 用工具
lupdate
提取字符串,并语言家来翻译; - 用工具
lrelease
发布 QM 文件,即 helloqt_zh.qm 文件; - 在资源文件
HelloQt.qrc
中添加 QM 文件,并把资源重命名为:/translations/helloqt_zh
如图所示:
上面的步骤只是准备了翻译字符串,实际上应用程序并不能切换语言,还需要执行下面的步骤: - 新建一个成员变量
QTranslator *language_zh;
,并在主窗口的构造方法中加载对应的QM文件,language_zh->load(":/translations/helloqt_zh");
加载了 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)));
分别实现 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); }
执行完上一步你就急着编译查看结果,点击两个工具栏你却发现并不能切换语言,为什么呢?因为切换语言的事件还没有处理呢。覆盖
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")那么就有两个上下文,分别是
QObject和
HelloQt,这一点也可以从语言家看到。
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 样式保留,只需要替换里面的 Hello 和 Qt! 即可。
源代码
只列出关键的源代码,首先是布局文件
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><h2><i>Hello</i><font color=red>Qt!</font></h2></source> <translation><h2><i>你好</i><font color=red>Qt!</font></h2></translation> </message> <message> <location filename="myUI_HelloQt.h" line="59"/> <location filename="myUI_HelloQt.h" line="86"/> <source>&Quit</source> <translation>&退出</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
本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
- 利用线性布局和相对布局实现一个简单的页面并且使应用支持国际化语言
- [20081226更新(加了日文版本支持)]一个JavaScript WEB日历控件,支持IE6,FireFox,可支持不同语言版本,目前支持中文简繁,英,日语
- php 5.6版本中编写一个PHP扩展的简单示例
- 开源自己用python封装的一个Windows GUI(UI Automation)自动化工具,支持MFC,Windows Forms,WPF,Metro,Qt
- 编写的一个遍历文件夹下所有文件的类,含有一个应用示例
- php 5.6版本中编写一个PHP扩展的简单示例
- 一个JavaScript WEB日历控件,支持IE6,FireFox,可支持不同语言版本,目前支持中英文。
- 编写一个Qt支持TGA图像格式的插件
- go语言练习 : 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。
- 分享一个关于Symbian上开发QT应用的文档吧
- 别再为了iOS新系统设备而重新安装一个新版Xcode了.其实我们可以添加版本支持
- GUI福音-QT5.9(LTS)发布与版本回顾
- 创建一个简单的WebPart应用示例
- eoLinker-API_Shop_短信服务接口-调用示例代码,支持PHP、Python、Java等语言
- python day05 编写一个给定多项式的简易计算机 re模块及正则匹配应用
- android一个应用如何在一个手机上装多个不同版本的方法
- 用80x86汇编语言,编写一个输出16进制数字的程序
- ABAP--编写应用服务器执行外部命令的程序示例
- JavaScript 这类弱类型的语言,在判断相等方面很麻烦,明明不是一个东西,却相等了。这里通过示例展示 null、 undefined、 0、 false、 "" 的相等情况。 var arr
- 【Oencv2计算机视觉学习笔记(二)】使用Qt创建GUI应用