How to change languages(translations) dynamically on PyQt5?

Question:

I wonder if it is possible to change the languages(translations) dynamically without using qt designer to make the UI? That means I don’t want to use the function retranslateUi() to update the program interface.

Here is my code, but I’m stuck on lines marked #1 #2 #3. Don’t know what I should use to update the interface.

import sys
from PyQt5.QtCore import Qt, QTranslator
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, 
QComboBox, QVBoxLayout


class Demo(QWidget):
    def __init__(self):
        super(Demo, self).__init__()
        self.button = QPushButton(self.tr('Start'), self)
        self.label = QLabel(self.tr('Hello, World'), self)
        self.label.setAlignment(Qt.AlignCenter)

        self.combo = QComboBox(self)
        self.combo.addItem('English')
        self.combo.addItem('中文')
        self.combo.addItem('français')
        self.combo.currentTextChanged.connect(self.change_func)

        self.trans = QTranslator(self)

        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.combo)
        self.v_layout.addWidget(self.button)
        self.v_layout.addWidget(self.label)
        self.setLayout(self.v_layout)

    def change_func(self):
        print(self.combo.currentText())
        if self.combo.currentText() == '中文':
            self.trans.load('eng-chs')
            _app = QApplication.instance()
            _app.installTranslator(self.trans)
            # 1

        elif self.combo.currentText() == 'français':
            self.trans.load('eng-fr')
            _app = QApplication.instance()
            _app.installTranslator(self.trans)
            # 2

        else:
            _app = QApplication.instance()
            _app.removeTranslator(self.trans) 
             # 3


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

Any help would be appreciated.

Asked By: just_be_happy

||

Answers:

No you will have to use a technique like Qt Designer does with retranslateUi because the Qt widget system does not have a way to redo the translation on it’s own (otherwise QT Designer would be using that to).

Building such a system would require a fundamental change to the widgets as for each string property you would need to know that it contains a translatable string (not a data value) and knowing the original string for looking up the new translation would be best (reversing translations could be ambiguous).

Answered By: Eelke

TL; DR; It is not necessary to use Qt Designer


You should not use Qt Designer necessarily but you should use the same technique, that is, create a method that could be called retranslateUi() and in it set the texts using translate() instead of tr() (for more details read the docs). Calling that method when you change language for it must use the changeEvent() event. For example in your case the code is as follows:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Demo(QtWidgets.QWidget):
    def __init__(self):
        super(Demo, self).__init__()
        self.button = QtWidgets.QPushButton()
        self.label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)

        self.combo = QtWidgets.QComboBox(self)
        self.combo.currentIndexChanged.connect(self.change_func)

        self.trans = QtCore.QTranslator(self)

        self.v_layout = QtWidgets.QVBoxLayout(self)
        self.v_layout.addWidget(self.combo)
        self.v_layout.addWidget(self.button)
        self.v_layout.addWidget(self.label)

        options = ([('English', ''), ('français', 'eng-fr' ), ('中文', 'eng-chs'), ])
        
        for i, (text, lang) in enumerate(options):
            self.combo.addItem(text)
            self.combo.setItemData(i, lang)
        self.retranslateUi()

    @QtCore.pyqtSlot(int)
    def change_func(self, index):
        data = self.combo.itemData(index)
        if data:
            self.trans.load(data)
            QtWidgets.QApplication.instance().installTranslator(self.trans)
        else:
            QtWidgets.QApplication.instance().removeTranslator(self.trans)

    def changeEvent(self, event):
        if event.type() == QtCore.QEvent.LanguageChange:
            self.retranslateUi()
        super(Demo, self).changeEvent(event)

    def retranslateUi(self):
        self.button.setText(QtWidgets.QApplication.translate('Demo', 'Start'))
        self.label.setText(QtWidgets.QApplication.translate('Demo', 'Hello, World'))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

Then generate the .ts:

pylupdate5 main.py  -ts eng-chs.ts
pylupdate5 main.py  -ts eng-fr.ts

Then use Qt Linguist to do the translations.

And finally the .qm:

lrelease eng-fr.ts eng-chs.qm

enter image description here

enter image description here

enter image description here

The complete project you find here.

Answered By: eyllanesc

@eyllanesc i used this method on my code and the gui is not loading because of the line super(Demo, self).changeEvent(event) which obviously i changed according to my code, could you help me with it ?

Answered By: Bilal Nasir