在 PySide 上完成 QtQuick 本地化

Complete QtQuick Localization on PySide

我知道 Localization in QtQuick from top to bottom 上发布了几乎相同的问题,但那里的人已经知道如何开始,而且它基于 C++。

在我的问题中,我还必须实时翻译 QML 端的所有字符串,但后端使用 Python (PySide) 而不是 C++。由于我是这部分的新手,我不知道如何以最少的 Python 使用来实现这一点。

根据链接的问题,我目前能够:

但是我不清楚那里描述的进一步步骤。 Python 的 QML 文档非常简约。

非常感谢您提供一些详细的描述或示例。

逻辑类似于在 C++ 中所做的,因为 QML 不会改变任何东西,您只需要调整 python 中的逻辑。

步骤如下:

  1. 使用 lupdate 从 qml 生成 .ts。

    lupdate main.qml -ts i18n_es.ts
    
  2. 使用 Qt Linguist 添加翻译。

  3. 使用 lrelease 将 .ts 编译为 .qm。

    lrelease i18n_es.ts i18n_es.qm 
    
  4. 使用 python 加载 .qm。

├── main.py
├── qml
│   └── main.qml
└── translations
    ├── i18n_es.qm
    ├── i18n_es.ts
    ├── i18n_fr.qm
    └── i18n_fr.ts

main.py

import os
import re
import sys
from pathlib import Path

from PySide2.QtCore import (
    Property,
    QCoreApplication,
    QDir,
    QObject,
    Qt,
    QTranslator,
    QUrl,
    Signal,
    Slot,
)
from PySide2.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide2.QtQml import QQmlApplicationEngine

CURRENT_DIRECTORY = Path(__file__).resolve().parent
QML_DIRECTORY = CURRENT_DIRECTORY / "qml"
TRANSLATIONS_DIR = CURRENT_DIRECTORY / "translations"


class Translator(QObject):
    language_changed = Signal(name="languageChanged")

    def __init__(self, engine, parent=None):
        super().__init__(parent)
        self._engine = engine
        self._languages_model = QStandardItemModel()
        self.load_translations()
        self._translator = QTranslator()

    @Slot(str)
    def set_language(self, language):
        if language != "Default":
            trans_dir = QDir(os.fspath(TRANSLATIONS_DIR))
            filename = trans_dir.filePath(f"i18n_{language}.qm")
            if not self._translator.load(filename):
                print("Failed")
            QGuiApplication.installTranslator(self._translator)
        else:
            QGuiApplication.removeTranslator(self._translator)
        self._engine.retranslate()

    def languages_model(self):
        return self._languages_model

    languages = Property(QObject, fget=languages_model, constant=True)

    def load_translations(self):
        self._languages_model.clear()
        item = QStandardItem("Default")
        self._languages_model.appendRow(item)
        trans_dir = QDir(os.fspath(TRANSLATIONS_DIR))
        for filename in trans_dir.entryList(["*.qm"], QDir.Files, QDir.Name):
            language = re.search(r"i18n_(.*?)\.qm", filename).group(1)
            item = QStandardItem(language)
            self._languages_model.appendRow(item)


def main():
    app = QGuiApplication(sys.argv)

    engine = QQmlApplicationEngine()

    translator = Translator(engine, app)

    engine.rootContext().setContextProperty("translator", translator)
    filename = os.fspath(QML_DIRECTORY / "main.qml")
    url = QUrl.fromLocalFile(filename)

    def handle_object_created(obj, obj_url):
        if obj is None and url == obj_url:
            QCoreApplication.exit(-1)

    engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
    engine.load(url)

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

ApplicationWindow {
    width: 640
    height: 480
    visible: true
    title: qsTr("Title")

    ListModel {
        id: list_model

        ListElement {
            name: QT_TR_NOOP("house")
        }

        ListElement {
            name: QT_TR_NOOP("table")
        }

        ListElement {
            name: QT_TR_NOOP("chair")
        }

    }

    ColumnLayout {
        anchors.fill: parent

        ComboBox {
            model: translator ? translator.languages : null
            textRole: "display"
            Layout.fillWidth: true
            onActivated: function(index) {
                translator.set_language(currentText);
            }
        }

        Button {
            text: qsTr("name")
            Layout.fillWidth: true
        }

        ListView {
            model: list_model
            Layout.fillWidth: true
            Layout.fillHeight: true

            delegate: Text {
                text: qsTr(name)
            }

        }

    }

}

完整示例为here