1、功能概述
有些软件需要开发多语言界面版本,例如中文版和英文版。
开发PyQt5的多语言界面应用程序,主要包括以下几个步骤。
(1)可视化设计UI窗体时用一种语言,例如汉语。
(2)在Python中基于PyQt5编写的程序,凡是用到字符串的地方都需要将字符串用类函数QCoreApplication.translate()封装。
(3)创建一个需要翻译的源文件的配置文件,例如命名为transSources.txt,文件格式类似于Qt Creator的项目配置文件,将需要翻译的.ui文件和.py文件都添加进去,并指定需要生成的翻译文件(.ts文件)。
(4)使用PyQt5的工具软件pylupdate5.exe对文件transSources.txt进行编译,生成指定的多个翻译文件。
(5)使用Qt Linguist软件打开生成的翻译文件。翻译文件提取了界面和程序中的所有字符串,将这些字符串翻译为需要的语言,例如将所有中文字符串翻译为英文。
(6)在Linguist软件中完成翻译后,使用Linguist软件的发布功能,可以导出更为紧凑的语言资源文件(.qm文件),以便在程序中使用。
(7)在程序中创建QTranslator对象并载入某个.qm文件,然后用QCoreApplication的类函数installTranslator()加载QTranslator对象,就可以使用.qm文件的内容显示界面,实现不同的语言界面。
2、主要类与函数
QCoreApplication:用translate()函数对字符串进行封装
text = QCoreApplication.translate("QmyMainWindow","文件名:")
self.ui.statusBar.showMessage(text)
translate(context, sourceText):第一个参数是上下文,也就是代码所在类的名称,第二个参数是需要翻译的字符串。
translate()函数使用注意事项:
(1) 尽量使用字符串常量,不要使用字符串变量。
#这种写法错误
textFile = "选择的文件名"
text = QCoreApplication.translate("QmyMainWindow", textFile)
self.ui.statusBar.showMessage(text)
#这种写法正确
text = QCoreApplication.translate('QmyMainWindow', '选择的文件名')
self.ui.statusBar.showMessage(text)
(2)使用字符串变量时需要用QT_TRANSLATE_NOOP()宏进行标记
PyQt5.QtCore模块中的宏QT_TRANSLATE_NOOP()进行标记,一般用于字符串列表的初始化。
from PyQt5.QtCore import QT_TRANSLATE_NOOP
cityList = [QT_TRANSLATE_NOOP("QmyMainWindow", "北京市"),
QT_TRANSLATE_NOOP("QmyMainWindow", "上海市"),
QT_TRANSLATE_NOOP("QmyMainWindow", "四川省")]
text = QCoreApplication.translate("QmyMainWindow",cityList[0])
(3)translate()中不能使用拼接的动态字符串
下面两行生成字符串变量text的语句都是可以运行的,在界面上也可以显示拼接后的字符串,但是在生成翻译文件时,无法提取其中的字符串。
#无法翻译
num = 100
text = QCoreApplication.translate("QmyMainWindow", "第" + str(num) + "行")
text = QCoreApplication.translate("QmyMainWindow", "第%d行" %num)
self.ui.statusBar.showMessage(text)
removeTranslator(translator):
3、详细代码
示例中运行时中文界面如上图所示。
在工具栏上有界面语言选择的两个按钮,当点击“English”按钮切换到英文时的界面如下图所示。
程序运行时点击工具栏上的两个按钮就可以在中文和英文两种界面之间切换,且当前设置的语言会被自动保存到注册表里,程序下次启动时自动使用上次的界面语言。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow,QActionGroup
from PyQt5.QtGui import QTextCharFormat, QFont
from PyQt5.QtCore import (Qt, pyqtSlot,QCoreApplication,
QTranslator,QSettings,QT_TRANSLATE_NOOP)
from ui_MainWindow import Ui_MainWindow
class QmyMainWindow(QMainWindow):
_tr = QCoreApplication.translate #替代符
def __init__(self, parent=None):
super().__init__(parent) #调用父类构造函数,创建窗体
self.ui = Ui_MainWindow() #创建UI对象
self.ui.setupUi(self) #构造UI界面
text = self._tr("QmyMainWindow","文件名: ")
self.ui.statusBar.showMessage(text)
actionGroup = QActionGroup(self)
actionGroup.addAction(self.ui.actLang_CN)
actionGroup.addAction(self.ui.actLang_EN)
actionGroup.setExclusive(True) #互斥型分组
self.__translator = None #QTranslator对象
self.setCentralWidget(self.ui.textEdit)
## ============自定义功能函数================================
def setTranslator(self,translator,Language):
self.__translator = translator
if Language=="EN":
self.ui.actLang_EN.setChecked(True)
else:
self.ui.actLang_CN.setChecked(True)
自定义函数setTranslator(self, translator, Language)用于在应用程序启动时,向主窗口传递QTranslator对象和上次界面语言类型。在创建应用程序的代码里调用此函数,而不是在QmyMainWindow类的构造函数里调用。
生成翻译文件:程序中为需要翻译的字符串做好封装后,就可以用PyQt5的工具软件pylupdate5生成翻译文件(.ts)文件。pylupdate5.exe是安装PyQt5时自动安装的一个工具软件,改软件所在的目录被添加到了Windows系统的PATH环境变量里,所以在cmd环境里可以直接执行pylupdate5。
要执行pylupdate5程序,先要为示例项目编写一个配置文件,这个配置文件是类似于Qt Creator的项目文件,所以可以用“.pro”作为文件后缀。但是双击.pro文件会打开Qt Creator,不便于修改,所以我们直接命名为transSources.txt,下面是这个文件的内容:
## transSources.txt 用于生成翻译文件(.ts)文件的配置文件
## .ts是需要生成的翻译资源文件
TRANSLATIONS =appLang_CN.ts\
appLang_EN.ts
## SOURCES指定需要翻译的 .py、 .pyw文件
SOURCES += myMainWindow.py
## FORMS指定需要翻译的 .ui文件
FORMS +=MainWindow.ui
这个文件里有以下3部分。
(1)TRANSLATIONS 指定了要生成的翻译文件名,可以生成多个翻译文件,一个文件是一种语言,多个文件时用“\”续行。
(2)SOURCES 指定了需要翻译的源程序文件,也就是项目中的.py、.pyw文件,多个文件时用“\”续行。
(3)FORMS 指定了需要翻译的.ui界面文件。
可以翻译.ui文件,也可以翻译.ui文件编译后的.py文件
TRANSLATIONS =appLang_CN.ts\
appLang_EN.ts
SOURCES += myMainWindow.py\
ui_MainWindow.py
准备好文件transSources.txt后,就用pylupdate5编译生成指定的.ts文件。为了方便执行pylupdate5指令,编写一个批处理文件transUpdate.bat,下面是此文件的内容:
rem 用pylupdate5.exe 编译transSources.txt文件,生成.ts文件
pylupdate5 -noobsolete transSources.txt
指令中的参数-noobsolete表示删除翻译文件中一些无用的项。
在Windows资源管理器里双击文件transUpdate.bat就可以执行编译指令。如果目录下不存在文件appLang_CN.ts和appLang_EN.ts就生成这两个文件,如果文件已经存在就更新这两个文件的内容,所以修改界面和程序后可以再次生成翻译文件,而不会删除已经翻译的内容。
使用Qt Linguist翻译文件:生成的翻译文件appLang_CN.ts和appLang_EN.ts内包含了UI界面和Python程序里的所有字符串,使用Qt Linguist将这些字符串翻译为需要的语言版本。在Qt的程序组里可以找到Qt Linguist软件。
appLang_CN.ts是中文界面的翻译文件,因为源程序的界面就是用中文设计的,所以无须再翻译。appLang_EN.ts是英文翻译文件,需要将提取的所有中文字符串翻译为英文。
在Linguist软件中打开文件appLang_EN.ts,第一次打开一个.ts文件时,Linguist会出现如下图所示的语言设置对话框,用于设置目标语言和所在国家/地区。这个对话框也可以通过Linguist主菜单的“编辑”→“翻译文件设置…”菜单项调出。appLang_EN.ts是用于英文界面的翻译文件,所以选择语言English,国家/地区可选择“任意国家”。
打开appLang_EN.ts文件后的Linguist软件界面如下图示。左侧“上下文”列表里列出了项目中的UI窗口或类,这里MainWindow是UI文件中的界面类,QmyMainWindow是窗体业务逻辑类。“字符串”列表里列出了左侧某个上下文对象中提取的字符串,右侧“短语和表单”会显示窗口界面的预览或字符串在源程序中出现的代码段。
调用翻译文件改变界面语言:要在程序里使用.qm文件,需要创建QTranslator对象并载入.qm文件,然后用QCoreApplication的类函数installTranslator()安装翻译器。这个过程在应用程序启动时完成,所以在myMainWindow.py文件的窗体测试程序部分编写代码如下:
## ===========窗体测试程序=================================
if __name__ == "__main__": #用于当前窗体测试
app = QApplication(sys.argv) #创建GUI应用程序
# 读取注册表里的语言设置
QCoreApplication.setOrganizationName("mySoft")
QCoreApplication.setApplicationName("Demo11_1")
##organization="mySoft" #用于注册表
##appName="Demo11_1" #HKEY_CURRENT_USER/Software/mySoft/Demo11_1
regSettings = QSettings(QCoreApplication.organizationName(),
QCoreApplication.applicationName()) #创建
Language = regSettings.value("Language","EN") #读取Language键的值,缺省"EN"
trans = QTranslator()
if Language=="EN":
trans.load("appLang_EN.qm")
else:
trans.load("appLang_CN.qm")
QCoreApplication.installTranslator(trans)
form = QmyMainWindow() #创建窗体
form.setTranslator(trans,Language) #赋值给QmyMainWindow的类变量translator
form.show()
sys.exit(app.exec_())
这里用到了读取注册表键值的功能。QCoreApplication有几个类函数用于设置和表示一些全局信息。
setOrganizationName(orgName)和organizationName():用于设置和返回机构名称。
setApplicationName(appName)和applicationName():用于设置和返回应用程序名称。
QSettings是用于读取和保存应用程序设置的类,这些设置保存的位置与平台有关,在Windows平台上就是注册表,在macOS和iOS系统上就是属性列表文件。按照程序中的设置,创建的regSettings对应注册表中的目录是:
HKEY_CURRENT_USER/Software/mySoft/Demo11_1
使用QSettings的value(key, defaultValue)函数读出key键的值,如果这个键不存在,就返回默认值defaultValue。
QTranslator类是翻译器类,主要功能就是载入.qm文件,然后由QCoreApplication的类函数installTranslator()安装到应用程序里,就可以实现相应的语言界面。
代码里还调用了QmyMainWindow类的自定义接口函数setTranslator(),这是为了将创建的QTranslator类对象和当前语言类型传递给主窗口对象form,让主窗口可以保存此QTranslator对象。
在程序启动时做这样的工作后,程序启动时就可以使用上次的界面语言。在程序运行时,点击工具栏上的“汉语”或“English”按钮就可以实时切换界面语言,这两个按钮关联的槽函数代码如下:
## ===========由connectSlotsByName() 自动连接的槽函数=====================
@pyqtSlot() ##英语界面
def on_actLang_EN_triggered(self):
QCoreApplication.removeTranslator(self.__translator)
self.__translator = QTranslator()
self.__translator.load("appLang_EN.qm")
QCoreApplication.installTranslator(self.__translator)
self.ui.retranslateUi(self)
regSettings = QSettings(QCoreApplication.organizationName(),
QCoreApplication.applicationName())
regSettings.setValue("Language","EN") #保存设置
@pyqtSlot() ##汉语界面
def on_actLang_CN_triggered(self):
QCoreApplication.removeTranslator(self.__translator)
self.__translator = QTranslator()
self.__translator.load("appLang_CN.qm")
QCoreApplication.installTranslator(self.__translator)
self.ui.retranslateUi(self)
regSettings = QSettings(QCoreApplication.organizationName(),
QCoreApplication.applicationName())
regSettings.setValue("Language","CN") #保存设置
@pyqtSlot(bool) ##设置粗体
def on_actFont_Bold_triggered(self, checked):
fmt = self.ui.textEdit.currentCharFormat()
if (checked == True):
fmt.setFontWeight(QFont.Bold)
else:
fmt.setFontWeight(QFont.Normal)
self.ui.textEdit.mergeCurrentCharFormat(fmt)
@pyqtSlot(bool) ##设置斜体
def on_actFont_Italic_triggered(self, checked):
fmt=self.ui.textEdit.currentCharFormat()
fmt.setFontItalic(checked)
self.ui.textEdit.mergeCurrentCharFormat(fmt)
@pyqtSlot(bool) ##设置下划线
def on_actFont_UnderLine_triggered(self,checked):
fmt = self.ui.textEdit.currentCharFormat()
fmt.setFontUnderline(checked)
self.ui.textEdit.mergeCurrentCharFormat(fmt)
def on_textEdit_copyAvailable(self, avi): ##文本框内容可copy
self.ui.actEdit_Cut.setEnabled(avi)
self.ui.actEdit_Copy.setEnabled(avi)
self.ui.actEdit_Paste.setEnabled(self.ui.textEdit.canPaste())
def on_textEdit_selectionChanged(self): ##文本选择内容发生变化
fmt=self.ui.textEdit.currentCharFormat()
self.ui.actFont_Bold.setChecked(fmt.font().bold())
self.ui.actFont_Italic.setChecked(fmt.fontItalic())
self.ui.actFont_UnderLine.setChecked(fmt.fontUnderline())
def on_textEdit_customContextMenuRequested(self,pos): ##标准右键菜单
#创建一个内建的标准的编辑功能快捷菜单
popMenu = self.ui.textEdit.createStandardContextMenu()
popMenu.exec(pos)
@pyqtSlot(bool) ##设置工具栏按钮样式
def on_actSys_ToggleText_triggered(self,checked):
btnStyle = Qt.ToolButtonIconOnly
if(checked):
btnStyle = Qt.ToolButtonTextUnderIcon
self.ui.mainToolBar.setToolButtonStyle(btnStyle)
def on_actFile_New_triggered(self): ##新建文件,不实现具体功能
text = self._tr("QmyMainWindow","新建文件")
self.ui.statusBar.showMessage(text)
def on_actFile_Open_triggered(self): ##打开文件,不实现具体功能
text=self._tr("QmyMainWindow","打开的文件:")
self.ui.statusBar.showMessage(text)
def on_actFile_Save_triggered(self): ##保存文件,不实现具体功能
text=self._tr("QmyMainWindow","文件已保存")
self.ui.statusBar.showMessage(text)
## =============自定义槽函数===============================
这两个槽函数的功能和代码类似。首先要移除原来的翻译器,虽然可以在应用程序里安装多个翻译器,但只有最后安装的被使用,一般最好只安装一个翻译器。然后再创建翻译器、载入.qm文件、安装到应用程序,再保存当前界面语言类型到注册表。
函数retranslateUi():让界面重新翻译了一次,如果不执行这条语句,界面的语言是无法实时切换的。
打开文件ui_MainWindow.py,可以看到Ui_MainWindow类里定义的函数retranslateUi()的代码,下面仅显示了其中的一部分代码:
class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
self.menu_E.setTitle(_translate("MainWindow", "编辑(&E)"))
self.menu_F.setTitle(_translate("MainWindow", "格式(&M)"))
self.menu.setTitle(_translate("MainWindow", "界面语言"))
self.menu_F_2.setTitle(_translate("MainWindow", "文件(&F)"))
self.actEdit_Cut.setText(_translate("MainWindow", "剪切"))
self.actEdit_Cut.setShortcut(_translate("MainWindow", "Ctrl+X"))
self.actEdit_Copy.setText(_translate("MainWindow", "复制"))
可以看出retranslateUi()函数的功能就是将界面上的所有涉及字符串静态设置的集中到一起,用QtCore.QCoreApplication.translate()函数封装。所以,切换语言时需要调用窗体的retranslateUi()函数重新翻译。在Ui_MainWindow类的setupUi()函数里调用了retranslateUi()函数,所以新建窗体时会自动翻译界面。
如果一个应用程序有多个窗体,新建窗体时调用的setupUi()会自动翻译。但是如果一个窗体已经存在于内存中,切换语言后再显示这个窗体时就需要显式地调用其retranslateUi()函数。当然可以在切换语言时发射信号,用槽函数进行响应,但这样会增加额外的开销和复杂度,特别是当一个应用程序的窗口很多时。所以,大型的软件在选择界面语言后,一般要下次启动时才生效。