Inter­na­tio­na­li­sie­rung in PyQt mit QtDe­si­gner et al

Mit dem Qt Desi­gner las­sen sich gra­fi­sche Ober­flä­chen von Pro­gram­men leicht erstel­len, auch für die PyQt-Biblio­thek von Python. Aller­dings wird die GUI immer nur in einer Spra­che erstellt. Zur Lauf­zeit wäre es sehr auf­wän­dig, die Tex­te mit den übli­chen Mit­teln wie gnu get­text aus­zu­tau­schen. Qt sieht für die­ses Pro­blem den Qt Trans­la­tor vor.

Er nutzt binä­re Über­set­zungs­da­tei­en, die mit dem Qt Lin­gu­ist erzeug­ten und com­pi­liert wer­den. Grund­la­ge sind Wort­lis­ten, die das Hilfs­pro­gramm lup­date aus den Quell­tex­ten der .ui Datei­en extrahiert.

Um Desi­gner und Lin­gu­ist nut­zen zu kön­nen, müs­sen unter Debi­an die Qt-Ent­wick­lungs­werk­zeu­ge instal­liert werden:

sudo apt-get qt6-tools-dev-tools

Die Appli­ka­tio­nen (u.a. desi­gner, lin­gu­ist, lup­date) befin­den sich danach im Verzeichnis

/usr/lib/qt6/bin

Zur Veri­en­fa­chung soll­te man die­ses direc­to­ry in den PATH aufnehmen.

Zunächst erstellt man also in übli­cher Wei­se mit dem Qt Desi­gner die .ui Datei. Sie ent­hält in einer XML-Struk­tur die Beschrei­bung der Bedien­ober­flä­che inklu­si­ve der ange­zeig­ten Tex­te. Das berühm­te Hallo-Welt-Beispiel:

…erzeugt zum Bei­spiel fol­gen­de .ui Date:

<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>150</x>
     <y>130</y>
     <width>91</width>
     <height>22</height>
    </rect>
   </property>
   <property name="text">
    <string>Hallo Welt!</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Das Hilfs­pro­gramm lupdate extra­hiert die Tex­te aus der .ui Datei:

/usr/lib/qt6/bin/lupdate hallowelt.ui -ts hallowelt.ts

… und erzeugt fol­gen­de .ts Datei, in der die Über­set­zung noch als uner­le­digt mar­kiert ist:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
    <name>Dialog</name>
    <message>
        <location filename="hallowelt.ui" line="14"/>
        <source>Dialog</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="hallowelt.ui" line="26"/>
        <source>Hallo Welt!</source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>

Die Über­set­zung erle­digt man mit dem Qt Lin­gu­ist, in dem man die .ts Datei öff­net und zunächst Quell- und Ziel­spra­che auswählt:

Danach klickt man den jewei­li­gen Ursprungs­text an, trägt die Über­set­zung ein und mar­kiert sie mit dem Haken in der Menü­leis­te als erledigt.

Die um die Über­set­zun­gen ergänz­te .ts Datei wird über „Datei -> Speichern” gespei­chert und sieht dann so aus:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US" sourcelanguage="de_DE">
<context>
    <name>Dialog</name>
    <message>
        <location filename="hallowelt.ui" line="14"/>
        <source>Dialog</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="hallowelt.ui" line="26"/>
        <source>Hallo Welt!</source>
        <translation>Hello World</translation>
    </message>
</context>
</TS>

Wenn alle Über­set­zun­gen ein­ge­tra­gen sind, kann die Datei in Binär­form über „Datei -> Freigeben” gespei­chert wer­den. Das erzeugt die .qm Datei, die spä­ter in Python gela­den wird.

Damit sind die Vor­be­rei­tun­gen getrof­fen, und die Über­set­zung kann in der Python-Appli­ka­ti­on gela­den wer­den. Eine mini­ma­le Test-Appli­ka­ti­on lädt mit uic.load die mit dem Desi­gner erzeug­te GUI-Datei hallowelt.ui:

import sys
from os import getcwd, path
from PyQt6 import uic
from PyQt6.QtWidgets import QApplication, QDialog


class HalloWelt(QDialog):

    def __init__(self):
        super(HalloWelt, self).__init__()

        uic.loadUi(path.join(getcwd(), 'hallowelt.ui'), self)
        self.show()


app = QApplication(sys.argv)
window = HalloWelt()
app.exec()

Sie erzeugt fol­gen­des Fenster:

Nun bin­den wir den Trans­la­tor ein und laden die oben erzeug­te eng­li­sche Über­set­zung in Binär­form (hallowelt.qm):

import sys
from os import getcwd, path
from PyQt6 import uic
from PyQt6.QtCore import QTranslator
from PyQt6.QtWidgets import QApplication, QDialog


class HalloWelt(QDialog):

    def __init__(self):
        super(HalloWelt, self).__init__()

        uic.loadUi(path.join(getcwd(), 'hallowelt.ui'), self)
        self.show()


app = QApplication(sys.argv)
translator = QTranslator(app)
if translator.load("hallowelt.qm", getcwd()):
    app.installTranslator(translator)
window = HalloWelt()
app.exec()

Damit sieht das Fens­ter dann so aus:

Um die Spra­che dyna­misch wech­seln zu kön­nen, erwei­tern wir die Ober­flä­che um zwei Tas­ten zum Umschalten:

… und geben ihnen die Funk­ti­on, die Über­set­zung neu zu laden:

import sys
from os import getcwd, path
from PyQt6 import uic
from PyQt6.uic import loadUiType
from PyQt6.QtCore import QTranslator
from PyQt6.QtWidgets import QApplication, QDialog

UiClass, BaseClass = loadUiType('hallowelt.ui')

class HalloWelt(BaseClass, UiClass):

    def __init__(self):
        super(HalloWelt, self).__init__()
        self.translator = None

        uic.loadUi(path.join(getcwd(), 'hallowelt.ui'), self)

        self.btnDe.clicked.connect(lambda: self.change_translation('de'))
        self.btnEn.clicked.connect(lambda: self.change_translation('en'))
        self.show()

    def change_translation(self, lang):
        if self.translator: app.removeTranslator(self.translator)
        self.translator = QTranslator(app)
        if self.translator.load(f"hallowelt_{lang}.qm", getcwd()):
            app.installTranslator(self.translator)
            self.retranslateUi(self)


app = QApplication(sys.argv)
translator = QTranslator(app)
if translator.load("hallowelt_de.qm", getcwd()):
    app.installTranslator(translator)
window = HalloWelt()
app.exec()

Für jede Spra­che muss jeweils eine .ts und .qm Datei ange­legt wer­den. Dazu erzeugt man sich eine Pro­jekt­da­tei (hallowelt.pro) und trägt die Quel­len und Zie­le der Über­set­zun­gen ein:

SOURCES += hallowelt.ui hallowelt.py
TRANSLATIONS += hallowelt_de.ts hallowelt_en.ts

Dann kann man sich mit lupdate in einem Rutsch alle .ts-Datei­en anle­gen lassen:

$ /usr/lib/qt6/bin/lupdate hallowelt.pro
Info: creating stash file /home/joerg/PycharmProjects/test/.qmake.stash
Updating 'hallowelt_de.ts'...
    Found 4 source text(s) (4 new and 0 already existing)
Updating 'hallowelt_en.ts'...
    Found 4 source text(s) (4 new and 0 already existing)

Im Linuist kann man ent­we­der jede Spra­che getrennt in ihrer .ts-Datei bear­bei­ten und frei­ge­ben, oder alle .ts-Datei­en gleich­zei­tig öff­nen. Im letz­te­ren Fall wer­den dann alle Über­set­zun­gen gleich­zei­tig ange­zeigt. Nach Abschluss der Arbei­ten kann man dann alle .ts-Datei­en mit „Alles speichern” und alle .qm-Datei­en mit „Alles freigeben” erzeu­gen lassen.

Mit den qm-Datei­en lässt sich dann die Über­set­zung in der lau­fen­den Appli­ka­ti­on umschalten:

Nutzt man ohne­hin den Qt-Trans­la­tor für die Über­set­zung der mit dem Qt-Desi­gner erzeug­ten Ele­men­te, kann man auch alle ande­ren Tex­te in der Python-Appli­ka­ti­on damit über­set­zen. Damit lupdate die Tex­te sicher fin­det (und um etwas Schreib­ar­beit ein­zu­spa­ren), legt man sich die tr-Funk­ti­on des Trans­la­tors am bes­ten auf eine Varia­ble „tr”:

tr = translator.tr
translated_text = tr('Hallo Welt')

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

drei × eins =