pythonpyqt5localizationqtranslator

Translation of Qt buttons not working with QTranslator


Usually I do translate my Python applications with GNU gettext. But I realized that standard elements (e.g. QDialogButtonBox.Yes) in a Qt GUI are not translated.

So I found examples with QTranslator. I'm not sure if this is the correct approach. I would expect that Qt handles the translation itself and realize which locale is currently activate. I would expect a simple switch like .use_current_local().

I couldn't find such an Qt option so I tried the QTranslator examples without understanding them in all details. The example here does run without errors but the buttons are not translated and looking like this.

I tried three variants for the value of the locale variable: explicit de_DE.UTF8, QLocale.system().name (returns de_DE) and qt_de_DE. The latter seems wired but comes from an real world example which I don't understand.

enter image description here

#!/usr/bin/env python3
from PyQt5.QtWidgets import QApplication, QDialogButtonBox
from PyQt5.QtCore import QTranslator, QLocale, QLibraryInfo


def get_translator():
    translator = QTranslator()

    # Variant 1
    loc = QLocale.system().name()

    # Variant 2
    # loc = 'de_DE.utf-8',  # qt_%s' % locale,

    # Variant 3
    # loc = 'qt_de_DE',

    print(f'{loc=}')  # <-- de_DE

    translator.load(
        loc,
        QLibraryInfo.location(QLibraryInfo.TranslationsPath)
    )

    return translator


if __name__ == '__main__':
    app = QApplication([])
    t = get_translator()
    app.installTranslator(t)

    buttonBox = QDialogButtonBox(
        QDialogButtonBox.Ok
        | QDialogButtonBox.Cancel
        | QDialogButtonBox.Yes
        | QDialogButtonBox.No
    )

    buttonBox.show()
    app.exec()

Please note I'm not interested to use Qt to replace my GNU gettext setup. I just want to have the standard Qt elements translated also. All other strings I set myself are still translated via GNU gettext.

I do use Debian 12 here. The qttranslations5-l10n package is installed. Other Qt applications (e.g. backintime-qt) do work and translate standard GUI elements like buttons without problems.


Solution

  • The problem is caused by a bug (see below) in the PyQt5 PyPI package. This package installs a local copy of Qt5 which includes invalid translation files. The specific issue is that the qt_xx.qm file depends on several other translation files, and if any one of them is missing, QTranslator.load() will fail.

    A temporary work-around is to copy/link the missing translation files from your system qt5 installation to your local pip installation, like this:

    cp /usr/share/qt5/translations/qtscript_*.qm ~/.local/lib/python3.9/site-packages/PyQt5/Qt5/translations
    

    Note that, ironically, it doesn't actually matter what the contents of these files are, since PyQt5 has never provided the QtScript module (which Qt has long-since deprecated anyway).


    The bug is revealed by decompiling the qt_xx.qm file, like so:

    lconvert /usr/share/qt/translations/qt_de.qm
    

    which gives:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE TS>
    <TS version="2.1" language="de">
    <dependencies>
    <dependency catalog="qtbase_de"/>
    <dependency catalog="qtscript_de"/>
    <dependency catalog="qtmultimedia_de"/>
    <dependency catalog="qtxmlpatterns_de"/>
    </dependencies>
    </TS>
    

    The relevant translations are actually provided by the qtbase_de file, but because the PyQt5 PyPI package does not include the qtscript_de file, QTranslator bails out of the entire load operation!

    UPDATE:

    I reported the bug on the pyqt mailing-list, and the author has now fixed the underlying issue. However, it appears the PyPI packages won't be updated until a new version of PyQt5 is released, so until then, the above work-around will still be needed.