PyQt5 中的界面动态翻译
文章目录
- PyQt5 中的界面动态翻译
- 1 引言
- 2 界面文本的加载步骤
- 3 动态翻译需要注意的问题
- 4 动态翻译程序实例要点
- 5 实例程序项目的内容
- 5.1 global_var.py 的内容
- 5.2 main_window.py 文件内容
- 5.3 main_window_app.py 的内容
- 5.4 main.py 的内容
- 5.5 test_01.pro 的内容
- 5.6 sample_01.zh_CN.ts 的内容
- 6 后记
1 引言
在 Qt5 Python 程序开发中,一般使用 QTranslator 类来实现界面的多国语言翻译。实现的方法和步骤的要点如下:
1. 在程序中,将需要翻译的文本字符串用 tr 函数包含起来;
2. 程序完成后,用 pylupdate.exe 或 pylupdate5.exe 生成 .ts 文件;
3. 用 Linguist.exe 程序,对 .ts 文件中的词语进行翻译,并生成 .qm 文件;
4. 在程序启动时,通过QTranslator 实例加载翻译文件;
5. 将上述 QTranslator 实例加载到当前 QApplication 实例中;
6. 执行 QApplication 实例;
7. 执行中,用 tr 函数包含的文本字符串都会根据 .qm 文件指定语言在界面上展示。
其中使用的 pylupdate 和 Linguist 都是 PyQt5 包含的应用程序,在用pip 安装 PyQt5 工具时,会自动被安装。tr 则是 QObject 的方法,必须在 QObject 类或其派生类的实例中使用,PyQt5 中的控件、容器等都派生自 QObject,所以一般情况下,在界面程序中使用 self.tr() 就可以了。
上述步骤保证了程序能够在启动时,加载合适的翻译(.qm)文件,显示指定的语言。网络上有很多有关这方面的描述,本文就不赘述了。
如果需要在程序运行中根据需要改变语言,则需要进行动态加载。本文主要涉及动态加载的有关问题。
2 界面文本的加载步骤
无论是启动时加载,还是动态加载,要使指定的语言文本能够在界面上显示出来,程序必须经历以下步骤:
1. 定义QTranslator 翻译器的实例;
2. 将指定的翻译文件加载到翻译器实例;
3. 在 QApplication 运行实例中加载上述 QTranslator 的实例;
4. 设置界面上的文本;
5. 绘制或重新绘制界面。
下面的一段程序是 Python Qt 程序启动时加载翻译文件的代码片段(即上面的步骤1、2、3):
# 生成 QTranslator 对象
app_translator = QTranslator()
# 加载翻译文件:sample_01.zh_CN.qm,如果要显示程序中的原来的语言,则去掉该语句
app_translator.load("sample_01.zh_CN")
# app 是 Qt 应用的运行实例
app = QtWidgets.QApplication(sys.argv)
# 为 app 安装翻译器
app.installTranslator(app_translator)
这里假设程序中界面文本均是英文。安装翻译器以后,界面上的所有以 tr 函数定义的文本都会按照 sample_01.zh_CN.qm 的定义进行翻译后显示。
3 动态翻译需要注意的问题
对于动态翻译来说,由于需要执行重新设置界面文本的操作,所以有些应当注意的问题。
第一个问题是程序结构问题
在进行动态翻译时,需要重新设置页面中标题、标签等各种控件中的文本字符串,如果这些设置字符串的语句分布在程序的不同部位,则这些部位都要考虑重新加载的问题。
开发 Qt5 程序时,有两种方式来生成界面:一是使用 Qt Designer 和 uic (pyuic)来生成,二是直接编程实现。前者在生成时,已经考虑了这个问题,集中用一个 retranslateUi 成员函数来实现控件文本的设置,动态翻译时只要调用这个函数,就可以实现文本的重新加载;如果直接编程实现界面,最好借鉴Qt Designer 和 uic 自动生成的程序结构,编程实现组合控件(如QForm、QDialog等)类时,集中用一个方法来设置所有界面文本,这样,可以大大方便动态翻译的实现。
第二个问题是 QTranslator 的重新加载问题
重新加载时,在处理上述几个步骤前,必须先将已经加载在应用实例(QApplication)的 QTranslator 删除掉。如果不执行这一步,恢复使用界面原有语言时,会发现原有加载的语言翻译并没有去掉。例如,程序已经加载了一个翻译文件,我们想恢复界面原有语言,就新生成一个新的 QTtanslator 实例,不加载任何翻译文件,并安装到 QApplication 实例上:
# 获取当前运行实例
app = QApplication.instance()
# 生成新的 QTraslator 实例,不加载任何翻译文件
transl = QTranslator()
# 安装到运行实例上
app.installTranslator(trasl)
这时,即使重新设置界面文本并重绘(repaint)界面,我们会发现,界面上的文本仍然是以前安装的语言。为此,必须使用 QApplication 的 removeTranslator 方法来将以前安装的翻译器删除。为了做到这一点,如何找到在主程序中定义的原有翻译器是个问题,QApplication 中似乎没有获取其已安装的 QTranslator 实例的方法(如果哪位大神知道,望在评论中指教),为此我们考虑采用以下三种方法解决这一问题:
1. 将主程序中的 QTranslator 实例定义为全局变量,使所有模块都能存取该变量,下面的例子就是用的这种方法;
2. 派生 QApplication,将 QTranslator 实例定义为派生类的一个成员变量,通过它的实例获取翻译器;
3. 将 QTranslator 实例作为参数,通过初始化函数传入需要使用它的对象中去,定义为其成员,在对象中使用。
当然还可以用其他一些方法,例如,定义全局的操作函数。
4 动态翻译程序实例要点
下面的两个图是用Qt Designer 实现的简单主窗口的运行情况,一个显示的是英文,一个显示是中文。这个窗口的是用 Qt Designer 设计,pyuic 生成的 Python 界面模块,辅以其他一些模块和主程序,构成一个完整的例子,用以展示动态翻译程序的编程要点。
程序的要点及功能如下:
1. 编程时的文本使用的语言是英语;
2. 编写了一个 sample_01.pro 的文件,用以定义pylupdate.py 的操作;
3. 用 pylupdate.exe 生成一个名为 sample_01.zh.CN.ts 的翻译文件;
4. 用 linguist.exe 生成一个编译后的翻译文件,文件名为 sample_01.zh_CN.qm;
5. 程序是一个简单的主窗口界面,仅仅为了展示动态翻译功能;
6. 在界面上点击:编辑(edit)-> 重新翻译(re-translate),就可以使菜单项的显示动态地在中英文之间转换。
这里提供的项目程序如下,每条是一个文件:
1. main.py:主程序;
2. main_window.py:由 pyuic 转换 Qt Designer .ui 文件,生成的窗口类程序;
3. main_window_app.py:用 main_window.Ui_MainWindow 和 QMainWindow 派生的主窗口处理程序;
4. global_var.py:定义全局变量;
5. test_01.pro:pylupdate 的定义文件,用 pylupdate test_01.pro 就可以从源程序得到下面一条的 .ts 文件;
6. test_01.zh_CN.ts:如果不需要在本项目程序上扩展,可以直接用 Lingust.exe 将这个文件转换为.qm 文件在程序中使用。
本文中没有包含主窗口的 main_window.ui 文件和 test_01.zh_CN.qm 翻译文件,因为前者已经转换为main_window.py,可以直接使用;后者是二进制文件,无法在帖子中展示,需要运行程序的网友可以使用 Linguist.exe 转换 test_01.zh_CN.ts 生成。
5 实例程序项目的内容
5.1 global_var.py 的内容
global_var.py 中包含了两个全局变量:
1. app_tanslator 是全局的 QTranslator 实例,将它设为全局变量的原因前面已有描述;
2. translate_status 是当前界面的状态,”ZH“ 表示当前是中文,“EN” 表示当前是英文,供转换时判断。
from PyQt5.QtCore import QTranslator
app_translator = QTranslator()
translate_status = "ZH"
5.2 main_window.py 文件内容
下面是 main_window.py 文件的内容,如果需要运行项目,这个程序可以直接使用,不用再经过 Qt Designer 和 pyuic 的处理了。这个程序的功能是绘出前面图中的主窗口。应当注意的是:
1. 它有一个 retranslateUi 的成员函数,程序初始化(setupUi)时和重新翻译界面时,都要使用它;
2. 所有界面文本用 _translate 包含,效果和前面所述的 tr 函数是一样的,它是由 pyuic 生成的,Designer 进行设计时相应窗口和控件的 “可翻译” 选项处于选中状态才会生成这种包含。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setTearOffEnabled(False)
self.menu.setObjectName("menu")
self.menu_2 = QtWidgets.QMenu(self.menubar)
self.menu_2.setObjectName("menu_2")
self.menuConnect = QtWidgets.QMenu(self.menubar)
self.menuConnect.setObjectName("menuConnect")
self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionRe_translate = QtWidgets.QAction(MainWindow)
self.actionRe_translate.setObjectName("actionRe_translate")
self.menu_2.addAction(self.actionRe_translate)
self.menubar.addAction(self.menu.menuAction())
self.menubar.addAction(self.menu_2.menuAction())
self.menubar.addAction(self.menuConnect.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menu.setTitle(_translate("MainWindow", "File"))
self.menu_2.setTitle(_translate("MainWindow", "Edit"))
self.menuConnect.setTitle(_translate("MainWindow", "Connect"))
self.menuHelp.setTitle(_translate("MainWindow", "Help"))
self.actionRe_translate.setText(_translate("MainWindow", "Re-translate"))
5.3 main_window_app.py 的内容
为了在程序中使用这个窗口,需要从上述的 Ui_MainWidow 派生出一个实际的窗口类(前者只是一个 Object),在另一个文件中实现,是为了以使界面和处理逻辑分离,用 Qt Designer 改变外观时,不会影响增加的处理逻辑。下面是派生的窗口类(文件名:main_window_app.py)。这个程序实现窗口处理功能,处理功能很简单,当单击菜单中的“重新翻译”(“Re-translate”)项目时,改变界面(菜单)的语言,如果原来是英文就改成中文,反之亦然。
改变界面语言的功能是在成员函数 re_translate 中实现的,实现要点如下:
1. 首先,获取当前运行的 QApplication 的实例;
2. 从当前的 QApplication 实例中移除已经安装的 翻译器(QTranslator) 实例;
3. 刷新全局变量 global.var.app_translator,生成新的翻译器实例;
3. 判断当前界面语言,如果当前是英语,则对翻译器实例加载中文翻译文件;
4. 刷新当前界面语言状态;
5. 为 QApplication 实例安装刷新后的翻译器;
6. 重新设置界面控件(菜单项)的文本;
7. 重绘界面。
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTranslator
from main_window import Ui_MainWindow
import global_var
class MainWindow(Ui_MainWindow, QMainWindow):
def __init__(self):
super().__init__()
# setupUi 是父类Ui_MainWindow 方法
self.setupUi(self)
# 信号与槽函数绑定
self.setup_signal()
def re_translate(self):
# 获取当前运行的 Qt 应用实例
the_app = QApplication.instance()
# 删除已经安装的翻译器,该翻译器是在全局变量文件中定义的
the_app.removeTranslator(global_var.app_translator)
# 重新初始化翻译器
global_var.app_translator = QTranslator()
# 判断当前使用的语言
if global_var.translate_status == "EN":
# 如果当前是英文,则对翻译器加载中文翻译文件,并将语言状态改为中文("ZH")
global_var.app_translator.load("sample_01.zh_CN")
global_var.translate_status = "ZH"
else:
# 如果当前是中文,则状态改为英文(“EN”),因为编程时文本是英文,所以不需要加载翻译文件
global_var.translate_status = "EN"
# 为 Qt 应用实例安装翻译器
the_app.installTranslator(global_var.app_translator)
# 调用父类(Ui_MainWindow)的函数,重新设置界面的字符串
self.retranslateUi(self)
# 重绘界面
self.repaint()
# 将信号与槽绑定,即:选中“重新翻译”或“Re-translate”,就执行本对象的re_translate 方法
def setup_signal(self):
self.actionRe_translate.triggered.connect(self.re_translate)
5.4 main.py 的内容
main.py 是应用的入口程序,展示了翻译器的安装,它的内容如下:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QTranslator, QCoreApplication
import main_window_app
import global_var
if __name__ == "__main__":
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
# 为翻译器加载中文翻译文件(.qm 文件),程序进入时,界面为中文
global_var.app_translator.load("sample_01.zh_CN")
global_var.translate_status = "ZH"
app = QtWidgets.QApplication(sys.argv)
# 为应用实例安装翻译器
app.installTranslator(global_var.app_translator)
ui = main_window_app.MainWindow()
ui.show()
sys.exit(app.exec_())
5.5 test_01.pro 的内容
pylupdate 程序有两种运行方式:一种是为一个 python 程序文件生成 .ts 文件;另一种是为多个 Python 程序生成 .ts 文件。test_01.pro 就是用于后一方法,其中定义了多个需要生成翻译文件的 Python 源文件和生成的不同语种的目标翻译文件(.ts)。
生成时,用:pylupdate test_01.pro
命令,就可以生成其中定义的 sample_01.zh_CN.ts 和 sample_01.zh_ZH.ts 两个 .ts 文件。下面是文件的内容:
SOURCES += main_window.py \
main_window_app.py
TRANSLATIONS += sample_01.zh_CN.ts \
sample_01.zh_HK.ts
5.6 sample_01.zh_CN.ts 的内容
如果想执行本项目程序,可以直接使用该文件,经 Linguist 的转换(发布,release),生成 sample_01.zh_CN.qm 文件以后,在程序中引用。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>MainWindow</name>
<message>
<location filename="main_window.py" line="50"/>
<source>MainWindow</source>
<translatorcomment>主窗口</translatorcomment>
<translation type="unfinished">主窗口</translation>
</message>
<message>
<location filename="main_window.py" line="51"/>
<source>File</source>
<translation type="unfinished">文件</translation>
</message>
<message>
<location filename="main_window.py" line="52"/>
<source>Edit</source>
<translation type="unfinished">编辑</translation>
</message>
<message>
<location filename="main_window.py" line="53"/>
<source>Connect</source>
<translation type="unfinished">连接</translation>
</message>
<message>
<location filename="main_window.py" line="54"/>
<source>Help</source>
<translation type="unfinished">帮助</translation>
</message>
<message>
<location filename="main_window.py" line="55"/>
<source>Re-translate</source>
<translation type="unfinished">重新翻译</translation>
</message>
</context>
</TS>
6 后记
本文展示了如何在一个项目中实现界面语言的动态翻译。其中的程序项目是可以运行的,运行的条件和步骤如下:
1. 安装 Python3 语言、PyQt5 模块和 PyQt5 工具(特别是 pylupdate、Linguist、Qt Designer、Pyuic 等);
2. (可选)安装 Python 的开发环境和 IDE 开发工具,包括 anacoda、Pycharm等;
3. 建立项目目录,将本文中的文件内容按要求的文件名,存为文件,放在项目目录下;
4. 用 Linuist.exe(Qt5 工具)将 test_01.zh_CN.ts 转换(发布,release)为 test_01.zh_CN.qm 文件,放在同一目录下;
5. 用 python main.py 或 python3 main.py 命令运行就可以看到结果了,当然,也可以在 Python 的 IDE环境下运行。