qsTr() translating, tr() NOT translating.
-
I'm a bit perplexed. I've read the various docs regarding on-the-fly translations, and have it 'working' -- that is (and I've made the association) my QQmlApplicationEngine instance retranslate() is working on strings wrapped with qsTr() in QML files, but the strings in C++ files, which are subclasses of QObject, are NOT being translated. Note, I've added two menu items to do on-the-fly translation, and these translations are happening "in real time" for the qsTr() strings, but not the tr() strings, so I'm assuming it's not when these strings are loaded to the UX since everything's loaded when the menu functions are performing their QQmlApplicationEngine retranslate(). I've confirmed the translations are done in the .qm file (same file for-which the qsTr() strings are translating), and even tried specifically QObject::tr() and QCoreApplication::translate() in C++, but they're not getting translated.
The association I refer to is using the QQmlApplicationEngine to do the translation, it might be assumed to only translate qsTr() strings, but all the documentation and google searches (disclaimer) I've seen don't suggest this is a limitation. e.g. https://embeddeduse.com/2017/11/12/new-in-qt-5-10-dynamic-language-change-in-qml/ but also https://doc.qt.io/qt-5/i18n-source-translation.html nor have I found any other reference to something specifically used to translate the tr() string on-the-fly.
Cheers, and thanks!
Steve -
I'm a bit perplexed. I've read the various docs regarding on-the-fly translations, and have it 'working' -- that is (and I've made the association) my QQmlApplicationEngine instance retranslate() is working on strings wrapped with qsTr() in QML files, but the strings in C++ files, which are subclasses of QObject, are NOT being translated. Note, I've added two menu items to do on-the-fly translation, and these translations are happening "in real time" for the qsTr() strings, but not the tr() strings, so I'm assuming it's not when these strings are loaded to the UX since everything's loaded when the menu functions are performing their QQmlApplicationEngine retranslate(). I've confirmed the translations are done in the .qm file (same file for-which the qsTr() strings are translating), and even tried specifically QObject::tr() and QCoreApplication::translate() in C++, but they're not getting translated.
The association I refer to is using the QQmlApplicationEngine to do the translation, it might be assumed to only translate qsTr() strings, but all the documentation and google searches (disclaimer) I've seen don't suggest this is a limitation. e.g. https://embeddeduse.com/2017/11/12/new-in-qt-5-10-dynamic-language-change-in-qml/ but also https://doc.qt.io/qt-5/i18n-source-translation.html nor have I found any other reference to something specifically used to translate the tr() string on-the-fly.
Cheers, and thanks!
Steve@steveDM said in qsTr() translating, tr() NOT translating.:
but the strings in C++ files, which are subclasses of QObject, are NOT being translated.
Have you implemented anything in the C++ code to achieve this?
A typical Designer UI will implement a form like this:
#include <QtCore/QVariant> #include <QtWidgets/QApplication> #include <QtWidgets/QLabel> #include <QtWidgets/QWidget> QT_BEGIN_NAMESPACE class Ui_Form { public: QLabel *label; void setupUi(QWidget *Form) { if (Form->objectName().isEmpty()) Form->setObjectName(QString::fromUtf8("Form")); Form->resize(400, 300); label = new QLabel(Form); label->setObjectName(QString::fromUtf8("label")); label->setGeometry(QRect(140, 110, 58, 18)); retranslateUi(Form); QMetaObject::connectSlotsByName(Form); } // setupUi void retranslateUi(QWidget *Form) { Form->setWindowTitle(QCoreApplication::translate("Form", "Form", nullptr)); label->setText(QCoreApplication::translate("Form", "Some translatable text", nullptr)); } // retranslateUi }; namespace Ui { class Form: public Ui_Form {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_UNTITLED_H namespace Ui { class Form: public Ui_Form {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_UNTITLED_H
and you can add something like this in in the widget code to deal with Dynamic Translation:
void MyWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } else QWidget::changeEvent(event); }
and in main():
QTranslator myappTranslator; myappTranslator.load(QLocale(), QLatin1String("myapp"), QLatin1String("_"), QLatin1String(":/i18n")); app.installTranslator(&myappTranslator);
-
Hi Chris -- thank you for replying, and offering some direction.
I first attempted ui translation in a separate Widgets app, all the displayed text is in the .ui files, and it was easy and worked entirely within the mainwindow.cpp file, basically as you describe (we're NOT using the user's locale, just letting them choose a language from a menu, but it looks very similar, see below). The app with the issue is not a QWidget app, we're using QML (only) for the UX. My understanding is these are different methodologies?
As to what's in "C++ land" I have the instantiated QTranslator loading the .qm file, it is NOT empty, then I'm loading it in my QApplication instance, _application, and using the QQmlCoreApplication, s_engine instance to retranslate, as-in (for brevity)
translator.load("myapp_de.qm"); if (!translator.isEmpty()) { qDebug() << "Translating to German..."; if (_application.installTranslator(&translator)) { s_engine->retranslate(); qDebug() << "Translation to German finished."; } }
When I use the above to translate when the app loads, it's the same as when the File menu (in the QML) calls into the C++ above -- the qsTr() text in the QML is translated based on myapp_de.qm, but the tr() text in C++ is not.
Can I use the MyWidget::changeEvent(QEvent *event) code in the QML app (as I said, I thought Widgets and QML were different 'methodologies')?
Cheers!
-
Hi,
One thing you did not confirm is whether you reimplemented the changeEvent function of the classes where you have the tr calls.
-
Hi SGaist! Thank you for helping! I have not,, but today I've discovered there are some tr() translations happening! In the MessageBox triggered from C++, the title, message, button text, etc are all wrapped with tr(), have translations in the .qm file, and these are populating translated in the qml message box. This leads me to suspect the more complicated way in-which the tr() strings that are not translated are getting passed into the UI.
The code uses a e.g. BaseSettingUI Class, which (inherits public QObject, and has a QString label, a QString tooltip Q_PROPERTIES. E.g. (in C++):
class BaseSettingUI : public QObject {
Q_OBJECT
Q_PROPERTY(QString label READ label NOTIFY valueChanged)
Q_PROPERTY(QString tooltip READ tooltip NOTIFY tooltipChanged)
}The constructor sets the label, e.g.
auto distanceSetting = new BaseSettingUI(tr("Number Of Miles"));
and a setToolTip(QString&) sets the tool tip e.g.
distanceSetting->setToolTip("How many miles you want to go.");
There are many of these in a QList, e.g. basicSettings, and basicSettings is passed to a repeater as the model, which nicely creates however many settings are in the QList, and to populate the UX via the Repeater there's qml Class, basically just a RowLayout (id: root), which has a Text member, and that text member is set to root.model.label. e.g
Repeater { model: basicSettings ShowProperties { labelWidth: parameters.labelWidth valueWidth: 120 } }
ShowProperties.qml:
RowLayout { id: root Text { id: label text: root.model.label ToolTip.text: root.model.tooltip } }
The text and ToolTip.text are the strings which are NOT getting translated when calling the QQMLApplicationEngine s_engine.retranslate().
I've tried removing the tr() from the BasicSettingUI's label when instantiating, and instead put the qsTr() around the text: qsTr(root.model.label) in the ShowProperties.qml, which is not working either (FWIW I've also tried the same with the tooltip Text member, as in ToolTip.text: qsTr(root.model.tooltip)
I'm sure this is involved, but this is the basic structure of how I'm dynamically populating the UI, and while the tr() is working when it's just displayed "directly" e.g. in the MessageBox, I suspect (how can I not) the passing of these strings from the multiple instantiated BasicSettings objects in the QList through the Repeater is causing the issue somehow?
Thanks for reading (not quite yet a TedTalk).
Cheers/Gruß to all! -
It might be a typo but your setToolTip call does not contain a call to tr so it won't get translated.
One common pattern to use is to have one function that does all the string setup with tr and call it from both the constructor of the class and the changeEvent method.
-
If you are deriving from QObject and are using the member method
tr()
you are implicitly using the translation context of that class (even if you are callingQObject::tr()
because it will look up the translation context from your derived class). As the last paragraph on this page https://doc.qt.io/qt-6/i18n-source-translation.html#translate-text-that-is-outside-of-a-qobject-subclass states: Calls totr()
will be forwarded toQCoreApplication::translate("translation context", ...)
and you can even call it directly. Translations from another translation context (i.e. from a different class or usingqsTr()
) will never be used for look up. Linguist will just prefill translations from other translation contexts to make it easier and faster to translate your app.As an aside (also from the docs):
In QML, by default, the translation context is the file name.
If you want to use that same translation context in C++ you need to specify it.
-
It might be a typo but your setToolTip call does not contain a call to tr so it won't get translated.
One common pattern to use is to have one function that does all the string setup with tr and call it from both the constructor of the class and the changeEvent method.
@SGaist Yes -- as described, I've added the tr() around the label and tooltip strings in the BaseSetting constructor and setToolTip() methods, as well as the qsTr() around the id: and ToolTip: properties in the RowLayout in QML (the cut-and-paster + agnosticizing of the code removed them) -- but thank you for pointing it out in case that wasn't the case.
Moving the tr() into the Class methods makes a LOT of sense!
-
If you are deriving from QObject and are using the member method
tr()
you are implicitly using the translation context of that class (even if you are callingQObject::tr()
because it will look up the translation context from your derived class). As the last paragraph on this page https://doc.qt.io/qt-6/i18n-source-translation.html#translate-text-that-is-outside-of-a-qobject-subclass states: Calls totr()
will be forwarded toQCoreApplication::translate("translation context", ...)
and you can even call it directly. Translations from another translation context (i.e. from a different class or usingqsTr()
) will never be used for look up. Linguist will just prefill translations from other translation contexts to make it easier and faster to translate your app.As an aside (also from the docs):
In QML, by default, the translation context is the file name.
If you want to use that same translation context in C++ you need to specify it.
@SimonSchroeder Thanks for the details! Are you suggesting that with the tr() setup in the BaseSettings class, then being loaded by the repeater into the QML, the job of doing the translation is really in either the ShopProperty class (or perhaps the Class in-which the ShopProperty class is being incorporated) by the QML?
-
@SimonSchroeder Thanks for the details! Are you suggesting that with the tr() setup in the BaseSettings class, then being loaded by the repeater into the QML, the job of doing the translation is really in either the ShopProperty class (or perhaps the Class in-which the ShopProperty class is being incorporated) by the QML?
@steveDM Every
tr()
call inside your BaseSetting class will look for translations in the context "BaseSetting". If you are usingqsTr()
inside a filemyui.qml
its context will be "myui.qml". So, one cannot find translations of the other. I don't know of any way to specify the default context fortr()
functions. But, the documentation states that you can set the default context inside a QML file usingpragma Tranlator: "BaseSetting"
. In this case both C++ and the QML file would use the same context and thus share their translations. -
@steveDM Every
tr()
call inside your BaseSetting class will look for translations in the context "BaseSetting". If you are usingqsTr()
inside a filemyui.qml
its context will be "myui.qml". So, one cannot find translations of the other. I don't know of any way to specify the default context fortr()
functions. But, the documentation states that you can set the default context inside a QML file usingpragma Tranlator: "BaseSetting"
. In this case both C++ and the QML file would use the same context and thus share their translations.@SimonSchroeder said in qsTr() translating, tr() NOT translating.:
I don't know of any way to specify the default context for tr() functions.
Not directly, only through https://doc.qt.io/qt-6/qcoreapplication.html#translate
-
Hello -- thanks all for your thoughts, and apologies for the delayed update (there was a long overdue vacation in there). I've triaged a solution!
Recall I'm in Qt 5.15.2 (we have a LOT of rendering code, and the move to Qt 6 will be ... digressive),, so e.g. the #pragma in the Qt file isn't an option, BUT (spoiler) controlling the context on the way into and out of the tr/qs file is!
With the various classes being instantiated, I noticed several contexts happening on the "way in" to the ts->qm file. Each UX tab has it's own file, of course, and the settings design is basically: MultiBoolSettingUI, MultiTextSettingUI, MultiIntSettingUI all inherit from BaseSettingUI as their base class, where the common Q_PROPERTIES are. The individual settings are instantiated in e.g. ModelTab.cpp, SettingTab.cpp, etc, each being the context in the ts file for the tr() in the Constructor, e.g.
MultiIntSettingUX availableThicknesses(tr("Thickness:"), ...); /* Leaves context up to lupdate... */
These contexts are, of course, revealed in the ts file via the Linguist app, and is the class doing the instantiating.
All these instances are stored in various QArrays for the various setting UX categories, and loaded via repeaters in the various tab QML files, using a single qml class as their model. I don't know what the translation context was on the "way out" -- probably either the tab's qml class, or the model class (I might look for a way reveal this e.g. in the console.log), but the key is we're accessing the label (and tooltip) from C++, and in the getter I (can only) specify one context regardless of the accessing context (I tried the QML model class, which is when I turned to this forum):
QString label() const { return QCoreApplication::translate("BaseSettingUI", label.toLatin1()); }
Therefore, to use the same context in the various tabs, I make sure to always set that context in the instantiations, which means it's always the same for lupdate regardless of tab/class where the instantiation happens, e.g.:
MultiIntSettingUX availableThicknesses(QCoreApplication::translate("BaseSettingUI", "Thickness:", ...); MultiTextSettingUX availablePatterns(QCoreApplication::translate("BaseSettingUI", "Pattern:", ...);
This means lupdate is instructed to use "BaseSettingUI" as the context "on the way in" for all settings, and the translation in the getter is told to also use "BaseSettingUI" as the context "on the way out" and all's well!
In hindsight this now seems pretty straight-forward, but FWIW from my newbie-to-Qt Translation perspective, this was a bit obscure -- and I think the context can be anything (for the next dev I'll use BaseClassUI as at least a 'hint' for those who miss my commenting) -- all your advice helped lead me to this, and once I saw the different contexts through Linguist the light went on.
Cheers!
-