PyQt5 中的界面动态翻译

PyQt5 中的界面动态翻译

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环境下运行。
    

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值