Zum Inhalt

Übersetzungen

Jeder für Benutzer sichtbare String, den ein Plugin anzeigt, muss übersetzbar sein, mit Katalogen für alle fünfzehn unterstützten Sprachen — en, pl, de, fr, es, pt_PT, pt_BR, nl, sv, nb, da, fi, it, cs, sk —, bevor das Plugin als fertig gilt. Das ist dieselbe Messlatte wie bei Tests.

Kataloge

Jedes Plugin, das irgendeinen String hat, führt einen .ts-Katalog pro Sprache in seinem Ordner Translations/:

Translations/
  MyPlugin_en.ts
  MyPlugin_pl.ts
  MyPlugin_de.ts
  MyPlugin_fr.ts

Binden Sie sie über mte_add_plugin in den Build ein:

mte_add_plugin(NAME MyPlugin
    SOURCES Src/MyPlugin.cpp
    TRANSLATIONS
        Translations/MyPlugin_en.ts
        Translations/MyPlugin_pl.ts
        Translations/MyPlugin_de.ts
        Translations/MyPlugin_fr.ts
)

Der Build führt lrelease aus, um .qm-Dateien bereitzustellen; TranslationManager lädt sie zur Laufzeit anhand des Katalognamens (Plugin-Kataloge unter Plugins/Translations/<Plugin>/).

Strings zur Übersetzung markieren

  • Bevorzugen Sie QObject::tr(...) (Kontext = die Klasse) für normale UI-Strings.
  • Der Helfer tr_() umhüllt QCoreApplication::translate("<Context>", s) für Strings außerhalb eines QObject.

tr_() ist für lupdate unsichtbar

lupdate kann den Kontext innerhalb von tr_() nicht auflösen, daher werden diese Kataloge von Hand gepflegt. Halten Sie den <source>-Text byte-identisch zum Laufzeit-String, sonst greift die Übersetzung stillschweigend nicht.

Einer Sprachumschaltung bei geöffneter Seite folgen

Die Sprache der Benutzeroberfläche lässt sich in Einstellungen ▸ Allgemein ohne Neustart ändern. Qt sendet dann QEvent::LanguageChange an jedes Widget — aber ein Widget-Baum, dessen Strings einmal im aufbauenden Code gesetzt wurden, bleibt bei der alten Sprache, bis der Dialog geschlossen und wieder geöffnet wird.

PluginApi/Qt/Retranslator.h löst das, ohne jeden String zweimal zu schreiben. Es zeichnet auf, wie jeder String entstanden ist, und spielt die Aufzeichnung bei einem Sprachwechsel erneut ab:

#include <PluginApi/Qt/Retranslator.h>

QWidget* MyPage::createWidget(QWidget* parent)
{
    auto* page = new QWidget(parent);
    auto& rt   = MTE::plugin::Retranslator::installOn(page);

    m_watch = new QCheckBox(page);
    rt.add([this] { m_watch->setText(tr_("Watch open files")); });

    m_size = new QSpinBox(page);
    rt.row(form, m_size, [] { return tr_("Font size:"); });   // QFormLayout caption

    return page;
}
  • add() führt sein Lambda sofort aus, die Aufrufstelle liest sich also genau wie die ersetzte „Text setzen"-Zeile, und jeder String steht weiterhin einmal im Quelltext.
  • row() tut dasselbe für eine QFormLayout-Beschriftung, die sonst nicht mehr erreichbar ist, sobald addRow() sie übernommen hat.
  • installOn() ist pro Widget idempotent; der Rekorder ist ein Kind dieses Widgets und stirbt mit ihm. Kann Ihre Seite ein erfasstes Widget überleben, halten Sie es in einem QPointer.

Wie PluginApi/Qt/ISettingsPage.h ist dies ein Opt-in-Qt-Header: binden Sie ihn nur aus Code ein, der ohnehin QWidgets baut (er benötigt Qt6::Widgets). Der Qt-freie Teil von PluginApi bleibt unberührt.

Was nicht übersetzt wird

Zwei Dinge bleiben bewusst in der Sprache, in der ihr Autor sie geschrieben hat:

  • name und description Ihres Plugins aus plugin.json, angezeigt unter Einstellungen ▸ Erweiterungen. Das Manifest wird gelesen, bevor das Plugin geladen wird — genau das erlaubt der Seite, auch deaktivierte oder gegen eine andere API-Version gebaute Plugins aufzulisten — es gibt zu diesem Zeitpunkt also keinen Katalog, in dem man sie nachschlagen könnte.
  • Die Beschriftungen einer deklarativen (Python-)Einstellungsseite oder eines Dock-Panels. Sie sind Daten, die Ihr Worker an den Host sendet, und keine Strings, die der Host gegen einen Katalog auflösen könnte.

Alles, was ein Plugin zur Laufzeit registriert — Befehlstitel, Menüeinträge, Dock-Titel, Beschriftungen der Einstellungsseite — wird auf die oben beschriebene übliche Weise übersetzt.

Drei dieser Strings schlägt der Host nach, nicht Ihr Code, und zwar im gemeinsamen Kontext PluginMenus: jedes Segment eines menuPath, jeder Befehls-title und die category des Befehls. Liefern Sie alle drei als <source>-Einträge im Kontext PluginMenus Ihrer eigenen Kataloge aus. Die Kategorie ist, was der Nutzer in der Spalte Kategorie von Einstellungen ▸ Tastenkürzel liest; fehlt sie im Katalog, zeigt die Spalte das rohe englische Wort neben den übersetzten Kernzeilen. Eine Kategorie, die auch ein Menütitel ist (Search, Encoding), teilt sich einen Eintrag: behalten Sie darin den &-Mnemonic für das Menü, der Host entfernt ihn für die Spalte.

Definition of Done

Die Lokalisierung eines Plugins ist erst fertig, wenn alle fünfzehn .ts-Dateien existieren, eingebunden sind und lrelease 0 unfertige Strings meldet.