Aller au contenu

Traductions

Chaque chaîne visible par l'utilisateur qu'un plugin affiche doit être traduisible, avec des catalogues pour les quinze langues prises en charge — en, pl, de, fr, es, pt_PT, pt_BR, nl, sv, nb, da, fi, it, cs, sk — avant que le plugin ne soit considéré comme terminé. C'est la même exigence que pour les tests.

Catalogues

Chaque plugin qui possède la moindre chaîne porte un catalogue .ts par langue dans son dossier Translations/ :

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

Raccordez-les à la compilation via mte_add_plugin :

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
)

La compilation exécute lrelease pour préparer les fichiers .qm ; TranslationManager les charge par nom de catalogue à l'exécution (les catalogues des plugins sous Plugins/Translations/<Plugin>/).

Marquer les chaînes pour la traduction

  • Préférez QObject::tr(...) (contexte = la classe) pour les chaînes UI ordinaires.
  • L'assistant tr_() enveloppe QCoreApplication::translate("<Context>", s) pour les chaînes situées hors d'un QObject.

tr_() est invisible pour lupdate

lupdate ne peut pas résoudre le contexte à l'intérieur de tr_(), ces catalogues sont donc maintenus à la main. Gardez le texte <source> identique octet pour octet à la chaîne d'exécution, sinon la traduction ne s'appliquera silencieusement pas.

Suivre un changement de langue alors qu'une page est ouverte

L'utilisateur peut changer la langue de l'interface dans Préférences ▸ Général sans redémarrer. Qt envoie alors QEvent::LanguageChange à chaque widget — mais un arbre de widgets dont les chaînes ont été définies une seule fois, dans le code qui l'a construit, reste dans l'ancienne langue jusqu'à ce que la boîte de dialogue soit fermée puis rouverte.

PluginApi/Qt/Retranslator.h règle cela sans écrire chaque chaîne deux fois. Il enregistre comment chaque chaîne a été produite et rejoue l'enregistrement au changement de langue :

#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() exécute sa lambda immédiatement : le site d'appel se lit exactement comme la ligne « définir le texte » qu'il remplace, et chaque chaîne n'apparaît toujours qu'une fois dans votre source.
  • row() fait de même pour une légende de QFormLayout, autrement inaccessible une fois qu'addRow() l'a consommée.
  • installOn() est idempotent par widget ; l'enregistreur est un enfant de ce widget et meurt avec lui. Si votre page peut survivre à un widget capturé, conservez-le dans un QPointer.

Comme PluginApi/Qt/ISettingsPage.h, c'est un en-tête Qt optionnel : ne l'incluez que depuis du code qui construit déjà des QWidget (il requiert Qt6::Widgets). La partie de PluginApi sans Qt reste intacte.

Ce qui n'est pas traduit

Deux choses restent, à dessein, dans la langue dans laquelle leur auteur les a écrites :

  • Les champs name et description de votre extension dans plugin.json, affichés dans Préférences ▸ Extensions. Le manifeste est lu avant que l'extension ne soit chargée — c'est précisément ce qui permet à la page de lister aussi les extensions désactivées ou compilées contre une autre version de l'API — il n'existe donc à ce moment aucun catalogue où les rechercher.
  • Les libellés d'une page de réglages ou d'un panneau déclaratif (Python). Ce sont des données que votre worker envoie à l'hôte, et non des chaînes que l'hôte pourrait résoudre dans un catalogue.

Tout ce qu'une extension enregistre à l'exécution — titres de commandes, entrées de menu, titres de panneaux, légendes de page de réglages — est traduit de la manière habituelle décrite ci-dessus.

Trois de ces chaînes sont recherchées par l'hôte, et non par votre code, dans le contexte partagé PluginMenus : chaque segment d'un menuPath, chaque title de commande et la category de la commande. Livrez les trois comme des entrées <source> dans le contexte PluginMenus de vos propres catalogues. La catégorie est ce que l'utilisateur lit dans la colonne Catégorie de Préférences ▸ Raccourcis ; quand elle manque au catalogue, la colonne affiche le mot anglais brut à côté des lignes du cœur traduites. Une catégorie qui est aussi un titre de menu (Search, Encoding) partage une seule entrée : conservez-y le mnémonique & pour le menu, l'hôte le retire pour la colonne.

Définition de « terminé »

La localisation d'un plugin n'est terminée que lorsque les quinze fichiers .ts existent, sont raccordés, et que lrelease rapporte 0 chaîne non finalisée.