How to add dynamic UI translation using QM files?
-
I have trouble understanding how translation works and in particular in the context of dynamic loading during runtime. The code below is work in progress but should provide enough to start:
CMakeLists.txt
cmake_minimum_required(VERSION 3.13) project(ExampleGetStarted LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE OFF) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) if(CMAKE_VERSION VERSION_LESS "3.7.0") set(CMAKE_INCLUDE_CURRENT_DIR ON) endif() find_package(Qt5 COMPONENTS Widgets Xml XmlPatterns Concurrent UiTools LinguistTools REQUIRED ) #find_package(Qt5XmlPatterns REQUIRED) # TODO https://stackoverflow.com/questions/51217734/how-to-add-qt-translations-to-cmake set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc) set(TS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/resources/translations) qt5_create_translation(TS_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_DIR}/en.ts ${TS_DIR}/de.ts) #qt5_add_translation(TS_FILES_FINISHED ${TS_DIR}/en.ts ${TS_DIR}/de.ts) # TODO Convert configure_file to custom post-build action. QM files are initially not available #configure_file(${QM_FILES} ${CMAKE_BINARY_DIR} COPYONLY) #qt5_add_translation(QM_FILES ${TS_FILES}) add_executable(example_get_started example_get_started.cpp ${RESOURCES} ${TS_FILES} ) target_link_libraries(example_get_started Qt5::Widgets Qt5::Xml Qt5::XmlPatterns Qt5::Concurrent Qt5::UiTools #Qt5::LinguistTools ) # Copy translations to binary directory where executable can access thoses foreach(TS_FILE ${TS_FILES}) add_custom_command( TARGET example_get_started POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${TS_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMENT "Copied ${TS_FILE} to binary directory" ) endforeach(TS_FILE)
UI form
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>widget_form</class> <widget class="QWidget" name="widget_form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>860</width> <height>527</height> </rect> </property> <property name="windowTitle"> <string>Get Started</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPushButton" name="btn_import_file"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/import);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_check_file"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/check);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_delete_file"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/delete);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_add_file"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/add);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QComboBox" name="cb_change_lang"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true"/> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPlainTextEdit" name="pte_file_view"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>1</horstretch> <verstretch>1</verstretch> </sizepolicy> </property> </widget> </item> <item> <widget class="Line" name="vline_right"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QPushButton" name="btn_csv"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/csv);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_code"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/code);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_json"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/json);</string> </property> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QPushButton" name="btn_xml"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/xml);</string> </property> <property name="text"> <string/> </property> </widget> </item> </layout> </item> </layout> </item> </layout> </widget> <resources> <include location="../resources.qrc"/> </resources> <connections/> </ui>
QRC file
<RCC> <qresource prefix="icons/files"> <file alias="xml">icons/file-formats/icons8-xml-96.png</file> <file alias="json">icons/file-formats/icons8-json-96.png</file> <file alias="csv">icons/file-formats/icons8-csv-96.png</file> <file alias="code">icons/file-formats/icons8-code-96.png</file> </qresource> <qresource prefix="icons/files"> <file alias="import">icons/file-ops/icons8-import-96.png</file> <file alias="delete">icons/file-ops/icons8-delete-file-96.png</file> <file alias="check">icons/file-ops/icons8-check-file-96.png</file> <file alias="add">icons/file-ops/icons8-add-file-96.png</file> </qresource> <qresource prefix="icons/flags"> <file alias="fr">icons/flags/icons8-france-80.png</file> <file alias="de">icons/flags/icons8-germany-80.png</file> <file alias="gb">icons/flags/icons8-great-britain-80.png</file> <file alias="ru">icons/flags/icons8-russian-federation-80.png</file> </qresource> <qresource prefix="forms"> <file alias="main">ui/widget.ui</file> </qresource> </RCC>
CPP file
#include <vector> #include <utility> #include <iostream> #include <QApplication> #include <QtWidgets> #include <QtUiTools> #include <QFile> #include <QTranslator> QScopedPointer<QTranslator> translator(new QTranslator()); static QWidget *loadUiFile(QWidget *parent) { QFile file(":/forms/main"); file.open(QIODevice::ReadOnly); QUiLoader loader; return loader.load(&file, parent); } static void retranslate(QString lang_code) { std::cout << "Translating to " << lang_code.toStdString() << std::endl; std::cout << "Looking into directory \"" << QApplication::applicationDirPath().toStdString() << "\"" << std::endl; if (lang_code == QString("fr")) { if (translator->load("fr", QApplication::applicationDirPath())) { QApplication::instance()->installTranslator(translator.data()); std::cout << "Switched to " << lang_code.toStdString() << std::endl; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else if (lang_code == QString("de")) { if (translator->load("de", QApplication::applicationDirPath())) { QApplication::instance()->installTranslator(translator.data()); std::cout << "Switched to " << lang_code.toStdString() << std::endl; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else if (lang_code == QString("ru")) { if (translator->load("ru", QApplication::applicationDirPath())) { QApplication::instance()->installTranslator(translator.data()); std::cout << "Switched to " << lang_code.toStdString() << std::endl; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else { QApplication::instance()->removeTranslator(translator.data()); } } int main(int argc, char *argv[]) { QApplication app(argc, argv); // Translations need to be created before widgets they affect /* if (!translator->load("en_GB.qm")) std::cout << "Unable to load translation file \"en_GB\"" << std::endl; app.installTranslator(translator.data()); if (!translator->load("de_DE.qm")) std::cout << "Unable to load translation file \"de_DE\"" << std::endl; app.installTranslator(translator.data()); std::cout << translator->language().toStdString() << std::endl; */ QWidget *widget = loadUiFile(nullptr); widget->findChild<QPushButton *>("btn_add_file")->setToolTip(QObject::tr("Create a new file")); widget->findChild<QPushButton *>("btn_check_file")->setToolTip(QObject::tr("Check the opened file's contents for errors")); widget->findChild<QPushButton *>("btn_delete_file")->setToolTip(QObject::tr("Deletes the opened file's contents")); widget->findChild<QPushButton *>("btn_import_file")->setToolTip(QObject::tr("Import existing file")); QComboBox *cb_change_lang = widget->findChild<QComboBox *>("cb_change_lang"); std::vector<std::pair<QString, QIcon>> langs = { std::pair<QString, QIcon>(QObject::tr("English (en)"), QIcon(":/icons/flags/gb")), std::pair<QString, QIcon>(QObject::tr("Deutsch (de)"), QIcon(":/icons/flags/de")), std::pair<QString, QIcon>(QObject::tr("Français (fr)"), QIcon(":/icons/flags/fr")), std::pair<QString, QIcon>(QObject::tr("Руский (ru)"), QIcon(":/icons/flags/ru"))}; auto lang_idx = 0; for (auto lang : langs) { cb_change_lang->insertItem(lang_idx, lang.first); cb_change_lang->setItemIcon(lang_idx, lang.second); lang_idx++; } QRegularExpression regex("\\(([^()]+)\\)"); QObject::connect(cb_change_lang, qOverload<int>(&QComboBox::currentIndexChanged), [=](int idx) { retranslate(regex.match(langs.at(cb_change_lang->currentIndex()).first).captured(1)); }); std::cout << regex.match(langs.at(cb_change_lang->currentIndex()).first).captured(1).toStdString() << std::endl; widget->show(); return app.exec(); }
Basically I would like to (during runtime) translate the UI using a provided QM file by simply choosing a different item in the combo box.
I also tried adding a separate translator for each language and for any given combo box item I would remove all translators and install the new one. In addition I also tried loading all QM files in the respective translators before I create my widget, e.g:
... int main(int argc, char *argv[]) { QApplication app(argc, argv); // Translations need to be created before widgets they affect /* if (!translator->load("en_GB.qm")) std::cout << "Unable to load translation file \"en_GB\"" << std::endl; app.installTranslator(translator.data()); if (!translator->load("de_DE.qm")) std::cout << "Unable to load translation file \"de_DE\"" << std::endl; app.installTranslator(translator.data()); std::cout << translator->language().toStdString() << std::endl; */ QLocale en_any(QLocale::Language::AnyLanguage, QLocale::Country::AnyCountry); QLocale de(QLocale::Language::German, QLocale::Country::Germany); QLocale fr(QLocale::Language::French, QLocale::Country::France); QLocale ru(QLocale::Language::Russian, QLocale::Country::RussianFederation); translatorEn->load(en_any, QString("en.qm"), QString(), QApplication::applicationDirPath()); std::cout << "Loaded translation for " << translatorEn->language().toStdString() << std::endl; translatorDe->load(de, QString("de.qm"), QString(), QApplication::applicationDirPath()); std::cout << "Loaded translation for " << translatorDe->language().toStdString() << std::endl; translatorFr->load(fr, QString("fr.qm"), QString(), QApplication::applicationDirPath()); std::cout << "Loaded translation for " << translatorFr->language().toStdString() << std::endl; translatorRu->load(ru, QString("ru.qm"), QString(), QApplication::applicationDirPath()); std::cout << "Loaded translation for " << translatorRu->language().toStdString() << std::endl; QLocale::setDefault(de); QWidget *widget = loadUiFile(nullptr); ... return app.exec(); }
-
So what's the actual problem?
Create a combobox, on selection of the corresponding item load the qm file with QTranslator and install it. Then wait for the LanguageChange event and do the re-translation of your widget as explained here. -
@Christian-Ehrlicher I saw that article. However I don't get the part with the change event. Do I have to
setText()
of every single widget in my UI manually? Especially in my case where I'm using an UI file and adding only bare minimum logic to it this looks too much. I'm guessing in my case I just need to install an event filter where I do that but I was thinking that the loading of a QM and installation of a new translator would automatically update my UI. -
If you use a ui file then you can call retranslateUi() from the generated class. The rest has to be done by yourself since noone can know what to do except you.
-
Hi,
If memory serves well, since you are using Designer, calling
ui->retranslateUi(this);
in reaction to the language change event should do the work. -
-
@Christian-Ehrlicher said in How to add dynamic UI translation using QM files?:
retranslateUi
Ok, so I removed the
QUiLoader
and decided to go with the way I'm more familiar with in regards to using a UI form in source code. I've adapter my existing code to look like this:QScopedPointer<QTranslator> translator(new QTranslator()); class MainWidget : public QWidget { Q_OBJECT Ui::widget_form *ui; const std::vector<std::pair<QString, QIcon>> langs = { std::pair<QString, QIcon>(QObject::tr("English (en)"), QIcon(":/icons/flags/gb")), std::pair<QString, QIcon>(QObject::tr("Deutsch (de)"), QIcon(":/icons/flags/de")), std::pair<QString, QIcon>(QObject::tr("Français (fr)"), QIcon(":/icons/flags/fr")), std::pair<QString, QIcon>(QObject::tr("Руский (ru)"), QIcon(":/icons/flags/ru")) }; QString lang_curr = "en"; public: MainWidget(QWidget* parent=nullptr) : QWidget(parent), ui(new Ui::widget_form) { ui->setupUi(this); ui->btn_add_file->setToolTip(QObject::tr("Create a new file")); ui->btn_check_file->setToolTip(QObject::tr("Check the opened file's contents for errors")); ui->btn_delete_file->setToolTip(QObject::tr("Deletes the opened file's contents")); ui->btn_import_file->setToolTip(QObject::tr("Import existing file")); auto lang_idx = 0; for (auto lang : langs) { ui->cb_change_lang->insertItem(lang_idx, lang.first); ui->cb_change_lang->setItemIcon(lang_idx, lang.second); lang_idx++; } QRegularExpression regex("\\(([^()]+)\\)"); QObject::connect(ui->cb_change_lang, qOverload<int>(&QComboBox::currentIndexChanged), [=](int idx) { retranslate(regex.match(langs.at(ui->cb_change_lang->currentIndex()).first).captured(1)); }); } ~MainWidget() { delete ui; } protected: void changeEvent(QEvent *event) override { if (event->type() == QEvent::LanguageChange) { std::cout << "Event filter for language" << std::endl; ui->retranslateUi(this); } return QWidget::changeEvent(event); } private: void retranslate(QString lang_code) { std::cout << "Translating to " << lang_code.toStdString() << std::endl; std::cout << "Looking into directory \"" << QApplication::applicationDirPath().toStdString() << "\"" << std::endl; if (lang_code == QString("fr")) { if (translator->load("fr", QApplication::applicationDirPath())) { QApplication::instance()->removeTranslator(translator.data()); QApplication::instance()->installTranslator(translator.data()); lang_curr = lang_code; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else if (lang_code == QString("de")) { if (translator->load("de", QApplication::applicationDirPath())) { QApplication::instance()->removeTranslator(translator.data()); QApplication::instance()->installTranslator(translator.data()); lang_curr = lang_code; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else if (lang_code == QString("ru")) { if (translator->load("ru", QApplication::applicationDirPath())) { QApplication::instance()->removeTranslator(translator.data()); QApplication::instance()->installTranslator(translator.data()); lang_curr = lang_code; } else std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl; } else { QApplication::instance()->removeTranslator(translator.data()); QApplication::instance()->installTranslator(translator.data()); lang_curr = lang_code; } } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWidget mw; mw.show(); return app.exec(); }
And here is an example for a TS file (in this case German):
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="de"> <context> <name>QObject</name> <message> <location filename="../../example_get_started.cpp" line="111"/> <source>English (en)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="112"/> <source>Deutsch (de)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="113"/> <source>Français (fr)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="114"/> <source>Руский (ru)</source> <translation></translation> </message> <message> <source>This is a test</source> <translation type="vanished">Das ist ein Test</translation> </message> <message> <location filename="../../example_get_started.cpp" line="125"/> <source>Create a new file</source> <translation>Neues Datei erstellen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="126"/> <source>Check the opened file's contents for errors</source> <translation>Geöffnetes Datei auf Fehler überprüfen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="127"/> <source>Delete the opened file's contents</source> <translation>Inhalt des geöffnetes Datei löschen</translation> </message> <message> <source>Deletes the opened file's contents</source> <translation type="vanished">Inhalt des geöffnetes Datei löschen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="128"/> <source>Import existing file</source> <translation>Existierendes Datei hinzufügen</translation> </message> </context> <context> <name>widget_form</name> <message> <location filename="../ui/widget.ui" line="14"/> <source>Get Started</source> <translation></translation> </message> </context> </TS>
I'm not sure if the loading is in the right spot. Yet again I don't get the result I'm expecting even when using
retranslateUi()
.I noticed the following inside the
retranslateUi()
function of the generated header file:void retranslateUi(QWidget *widget_form) { widget_form->setWindowTitle(QCoreApplication::translate("widget_form", "Get Started", nullptr)); btn_import_file->setText(QString()); btn_check_file->setText(QString()); btn_delete_file->setText(QString()); btn_add_file->setText(QString()); btn_csv->setText(QString()); btn_code->setText(QString()); btn_json->setText(QString()); btn_xml->setText(QString()); } // retranslateUi
What I decided to do is to enter the tooltip of each button also in the UI form. A little bit redundant but worth a shot. I added the missing translations in Linguist (for the UI form) and then reconfigured and rebuilt the project. The header now looks like this:
void retranslateUi(QWidget *widget_form) { widget_form->setWindowTitle(QCoreApplication::translate("widget_form", "Get Started", nullptr)); #if QT_CONFIG(tooltip) btn_import_file->setToolTip(QCoreApplication::translate("widget_form", "Create a new file", nullptr)); #endif // QT_CONFIG(tooltip) btn_import_file->setText(QString()); #if QT_CONFIG(tooltip) btn_check_file->setToolTip(QCoreApplication::translate("widget_form", "Check the opened file's contents for errors", nullptr)); #endif // QT_CONFIG(tooltip) btn_check_file->setText(QString()); #if QT_CONFIG(tooltip) btn_delete_file->setToolTip(QCoreApplication::translate("widget_form", "Delete the opened file's contents", nullptr)); #endif // QT_CONFIG(tooltip) btn_delete_file->setText(QString()); #if QT_CONFIG(tooltip) btn_add_file->setToolTip(QCoreApplication::translate("widget_form", "Import existing file", nullptr)); #endif // QT_CONFIG(tooltip) btn_add_file->setText(QString()); btn_csv->setText(QString()); btn_code->setText(QString()); btn_json->setText(QString()); btn_xml->setText(QString()); } // retranslateUi
and the TS file looks like this:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="de"> <context> <name>QObject</name> <message> <location filename="../../example_get_started.cpp" line="111"/> <source>English (en)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="112"/> <source>Deutsch (de)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="113"/> <source>Français (fr)</source> <translation></translation> </message> <message> <location filename="../../example_get_started.cpp" line="114"/> <source>Руский (ru)</source> <translation></translation> </message> <message> <source>This is a test</source> <translation type="vanished">Das ist ein Test</translation> </message> <message> <location filename="../../example_get_started.cpp" line="125"/> <source>Create a new file</source> <translation>Neues Datei erstellen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="126"/> <source>Check the opened file's contents for errors</source> <translation>Geöffnetes Datei auf Fehler überprüfen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="127"/> <source>Delete the opened file's contents</source> <translation>Inhalt des geöffnetes Datei löschen</translation> </message> <message> <source>Deletes the opened file's contents</source> <translation type="vanished">Inhalt des geöffnetes Datei löschen</translation> </message> <message> <location filename="../../example_get_started.cpp" line="128"/> <source>Import existing file</source> <translation>Existierendes Datei hinzufügen</translation> </message> </context> <context> <name>widget_form</name> <message> <location filename="../ui/widget.ui" line="14"/> <source>Get Started</source> <translation></translation> </message> <message> <location filename="../ui/widget.ui" line="28"/> <source>Create a new file</source> <translation>Neues Datei erstellen</translation> </message> <message> <location filename="../ui/widget.ui" line="47"/> <source>Check the opened file's contents for errors</source> <translation>Geöffnetes Datei auf Fehler überprüfen</translation> </message> <message> <location filename="../ui/widget.ui" line="66"/> <source>Delete the opened file's contents</source> <translation>Löscht des geöffneten Dateis löschen</translation> </message> <message> <location filename="../ui/widget.ui" line="85"/> <source>Import existing file</source> <translation>Existierendes Datei hinzufügen</translation> </message> <message> <source>This is a test</source> <translation type="vanished">Das ist ein Test</translation> </message> </context> </TS>
It's working fine now. I do wonder though if this is me doing something wrong or is it indeed intended to type all the text twice. If I can remove the e.g. tooltips in my source code that would be perfect since (correct me if I'm assuming wrong) a translator will normally have no access to the source code (since they are not a developer, duh :D). Note that from the very beginning I have marked all tooltips as translatable in my UI fomr file. However I cannot find anything resembling such setting inside the XML file. For example one of my buttons with a translatable tooltip looks like this in XML form:
<widget class="QPushButton" name="btn_delete_file"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Delete the opened file's contents</string> </property> <property name="styleSheet"> <string notr="true">image: url(:/icons/files/delete);</string> </property> <property name="text"> <string/> </property> </widget>
but as you can see in the Designer translation is on:
Is this a bug?
UPDATE: Apparently a property
notr
is added to a text component of a widget if not translatable. If translatable the property is just not include (apparently under the hood this is the equivalent ofnotr="false"
).