Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PySide Qt tr() does not translate, translate() does - context wrong?

I want to use a QTranslator to be able to use English text labels and still have the software showing German labels.

Unfortunately my app does not translate, except when I specify the context. The following static function instanciates a QApplication and adds the desired translators.

The first print translates 'Apple2' correctly to 'Apfel2'. The context in Qt Linguist also has the context 'app'. The second print does not translate though. tr() calls in classes (defined in the same python file) don't translate either.

def load_application():
    app = QApplication()

    qt_translator = QTranslator()
    qt_translator.load('qt_' + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath))
    app.installTranslator(qt_translator)

    app_translator = QTranslator()
    r = app_translator.load('i18n/' + QLocale.system().name())
    app.installTranslator(app_translator)

    print(app.translate('app', 'Apple2'))
    print(app.tr('Apple'))

    return app

EDIT:

The part for the static function was correct. The context for the application was QApplication. This did not help with the QMainWindow subclass though. I updated the code accordingly. The context generated by pyside-lupdate for the class is MainWindow:

view

class MainWindow(QMainWindow):
    add_model_widget = None

    def __init__(self):
        QMainWindow.__init__(self)

        # Create menu bar
        menu_bar = QMenuBar(self)
        m_file = QMenu(self.tr('File'), menu_bar)
        a_add_model = QAction(QIcon('add.png'), self.tr('Add Jewel'), self)

        m_file.addAction(a_add_model)
        menu_bar.addMenu(m_file)
        self.setMenuBar(menu_bar)

def load_application():
    app = QApplication()

    app_translator = QTranslator()
    app_translator.load(QLocale.system().name(), 'i18n')
    app.installTranslator(app_translator)

    return app

controller

def initiate():
    model.initiate_mongodb()
    app = view.load_application()

    main_window = view.MainWindow()

    main_window.show()
    sys.exit(app.exec_())

Solution: The solution to my problem was that the QTranslator didn't have any parent. QTranslator(app) solved my problem.

like image 487
Josch Avatar asked Feb 07 '15 17:02

Josch


2 Answers

This seems to happen because, unlike Qt, PySide/PyQt determines the context at runtime.

In your example, the context will (I think) resolve to QApplication at runtime, whereas the pyside/pyqt lupdate tools will hardcode it as app. The tools only do static analysis of the source code, and so I suppose they are not smart enough to figure out what the correct class should be.

The example code should work if you do something like this, though:

class App(QtGui.QApplication):
    def __init__(self):
        super(App, self).__init__()
        message = self.tr('Apple')
...
app = App()
...
print(app.tr('Apple'))

(Obviously you will need to update the translation files first).

EDIT:

Here's a simplified demo that works for me:

test.py:

import sys, os
from PySide import QtCore, QtGui

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        menu = self.menuBar().addMenu(self.tr('File'))
        menu.addAction(self.tr('Hello World'))

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    translator = QtCore.QTranslator(app)
    translator.load('i18n/tr_de', os.path.dirname(__file__))
    app.installTranslator(translator)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

i18n/tr.pro:

SOURCES = ../test.py
TRANSLATIONS = tr_de.ts

i18n/tr_de.ts:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="de_DE">
<context>
    <name>MainWindow</name>
    <message>
        <location filename="../test.py" line="7"/>
        <source>File</source>
        <translation>Datei</translation>
    </message>
    <message>
        <location filename="../test.py" line="8"/>
        <source>Hello World</source>
        <translation>Hallo Welt</translation>
    </message>
</context>
</TS>

command output:

$ pyside-lupdate -verbose -noobsolete i18n/tr.pro
Updating 'tr_de.ts'...
    Found 2 source texts (2 new and 0 already existing)
$ lrelease-qt4 i18n/tr.pro
Updating './i18n/tr_de.qm'...
    Generated 2 translation(s) (2 finished and 0 unfinished)
like image 98
ekhumoro Avatar answered Nov 07 '22 01:11

ekhumoro


If you got here like me, wanting to understand how you should do translation using PySide.

Use translate(), not tr()

The annoying part about using tr() is that you have to manually edit the generated .ts files every time you edit your source with the method he proposes, this editing takes a lot of time especially when you update your .ts file, because it puts all .tr() calls in the "Unknown context" context, you have to manually move all the translations back to the correct contexts again, this gets out of hand really quickly.

Instead, there's a very simple solution.

I read the official pyqt4-lupdate documentation, and it says

The PyQt4 behaviour is unsatisfactory and may be changed in the future. It is recommended that QCoreApplication.translate() be used in preference to tr() (and trUtf8()). This is guaranteed to work with current and future versions of PyQt4 and makes it much easier to share message files between Python and C++ code. Below is the alternative implementation of A that uses QCoreApplication.translate():

from QtCore import QCoreApplication
translate = QCoreApplication.translate

class A(QtCore.QObject):
    def hello(self):
        return translate("A", "Hello")

Where "A" is the hard-coded context, which pyside-lupdate will find. A lot better!

like image 21
Azeirah Avatar answered Nov 07 '22 02:11

Azeirah