Saltar a contenido

Traducciones

Toda cadena visible para el usuario que muestre un plugin debe ser traducible, con catálogos para los quince idiomas admitidos —en, pl, de, fr, es, pt_PT, pt_BR, nl, sv, nb, da, fi, it, cs, sk— antes de dar el plugin por terminado. El listón es el mismo que el de las pruebas.

Catálogos

Cada plugin que tenga alguna cadena lleva un catálogo .ts por idioma en su carpeta Translations/:

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

Conéctalos a la compilación mediante 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 compilación ejecuta lrelease para preparar los archivos .qm; TranslationManager los carga por nombre de catálogo en tiempo de ejecución (los catálogos de plugin, bajo Plugins/Translations/<Plugin>/).

Marcar cadenas para su traducción

  • Prefiere QObject::tr(...) (contexto = la clase) para las cadenas de interfaz normales.
  • El ayudante tr_() envuelve QCoreApplication::translate("<Context>", s) para las cadenas que están fuera de un QObject.

tr_() es invisible para lupdate

lupdate no puede resolver el contexto dentro de tr_(), así que esos catálogos se mantienen a mano. Mantén el texto de <source> idéntico byte a byte a la cadena en tiempo de ejecución, o la traducción no se aplicará y no lo dirá.

Seguir un cambio de idioma con una página abierta

El usuario puede cambiar el idioma de la interfaz en Preferencias ▸ General sin reiniciar. Qt envía entonces QEvent::LanguageChange a todos los widgets; pero un árbol de widgets cuyas cadenas se fijaron una sola vez, en el código que lo construyó, conserva el idioma antiguo hasta que el diálogo se cierra y se vuelve a abrir.

PluginApi/Qt/Retranslator.h se encarga de eso sin escribir cada cadena dos veces. Registra cómo se produjo cada cadena y reproduce la grabación cuando cambia el idioma:

#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() ejecuta su lambda de inmediato, así que el punto de llamada se lee exactamente igual que la línea de «pon el texto» a la que sustituye, y cada cadena sigue apareciendo una vez en tu código fuente.
  • row() hace lo mismo con el rótulo de un QFormLayout, que de otro modo es inalcanzable una vez que addRow() lo ha consumido.
  • installOn() es idempotente por widget; el grabador es hijo de ese widget y muere con él. Si tu página puede sobrevivir a un widget que capturaste, guárdalo en un QPointer.

Igual que PluginApi/Qt/ISettingsPage.h, esta es una cabecera de Qt opcional: inclúyela solo desde código que ya construye QWidget (necesita Qt6::Widgets). La parte de PluginApi libre de Qt no se ve afectada.

Qué no se traduce

Dos cosas se quedan, por diseño, en el idioma en que su autor las escribió:

  • El name y la description de tu plugin en plugin.json, tal como aparecen en Preferencias ▸ Complementos. El manifiesto se lee antes de cargar el plugin —eso es lo que permite que la página liste plugins desactivados o compilados contra otra versión de la API—, así que en ese momento no hay ningún catálogo en el que buscarlos.
  • Las etiquetas de una página de ajustes o de un panel acoplable declarativos (de Python). Son datos que tu trabajador envía al anfitrión, no cadenas que el anfitrión pueda resolver contra un catálogo.

Todo lo que un plugin registra en tiempo de ejecución —títulos de comandos, entradas de menú, títulos de paneles, rótulos de páginas de ajustes— se traduce de la forma habitual descrita arriba.

Tres de esas cadenas las busca el anfitrión, no tu código, en el contexto compartido PluginMenus: cada segmento de un menuPath, cada title de comando y la category del comando. Entrega las tres como entradas <source> en el contexto PluginMenus de tus propios catálogos. La categoría es lo que el usuario lee en la columna Categoría de Preferencias ▸ Atajos de teclado; cuando falta en el catálogo, la columna muestra la palabra inglesa en crudo junto a las filas traducidas del núcleo. Una categoría que además es un título de menú (Search, Encoding) comparte una sola entrada: conserva en ella el mnemónico & para el menú, el anfitrión lo quita para la columna.

Definición de «terminado»

La localización de un plugin está terminada solo cuando existen los quince archivos .ts, están conectados y lrelease informa de 0 cadenas sin terminar.