QObject::tr() is not working
-
Hello,
I'm creating an application in which I have used placeholder QStrings wrapped into tr() to have them later replaced by the translation from a qm file. I have gotten to the point where I have a working qm file loaded into a QTranslator and the QTranslator installed into my main application:int main(int argc, char* argv[]) { launcher::gui::Application application(argc, argv); application.connect(&application, SIGNAL(lastWindowClosed()), &application, SLOT(quit())); QTranslator translator; qDebug() << translator.load("lang.en_US.qm"); application.installTranslator(&translator); }
In the class itself, the code looks like this:
namespace launcher { namespace gui { namespace dialog { NewObject::NewObject(..., QWidget* parent) : Base(parent) { ... QLabel* name = new QLabel(tr("object_name")) } } } }
In the documentation, there's a hint that the
Q_OBJECT
macro automatically sets the context for the class correctly, i.e. it definestr(x)
inside the class asqApp->translate("NewObject", x)
.And this seems to be where my translation is broken. The following two lines should - as far as I understand - generate the exact same translated output.
qDebug() << tr("object_name"); qDebug() << QCoreApplication::translate("NewObject", "object_name");
But The outputs are different with the second one being the actual translation from the qm file:
"object_name" "Object Name*"
What am I missing here? Is it something with the namespaces and if so, how and where can I factor them in?
Thanks a lot!
Tobias -
@tobiSF said in QObject::tr() is not working:
Q_OBJECT macro
Just in case, could you please post the NewObject class definition (usually .h file). Remember that:
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
-
Hi Pablo,
here's the relevant part of the class definition:
namespace launcher { namespace gui { namespace dialog { class NewObject : public launcher::gui::dialog::BaseDialog { Q_OBJECT public: NewObject(..., QWidget* parent = nullptr); ... }; } } }
As you see,
NewObject
inherits from a Base class. This Base dialog class inherits fromQDialog
.
And all thesignal
andslot
connections work perfectly fine in the class, so I would assume, theQ_OBJECT
macro is placed correctly. -
Hi,
@tobiSF said in QObject::tr() is not working:
qDebug() << translator.load("lang.en_US.qm");
Might be a silly question but since that's relative path, did you put the file in the same folder as the executable ?
-
Hi @SGaist,
I have a folder called
installation
in which I have various subfolders:bin/ // contains all executables doc/ // contains html help files, the Qt help (myHelp.qhc), and the translation file(s)
Is it necessary to have the translation files in the same folder? Loading into the QTranslater worked with no problem and - as stated - the
qApp->translate()
method works fine when I give the context. I have simplified the code in my initial question but I am using the relative path to the .qm file as../doc
. -
@tobiSF said in QObject::tr() is not working:
Is it necessary to have the translation files in the same folder?
If you use relative paths as @SGaist pointed out then yes.
-
I have tracked the problem down, together with a colleague, to the namespaces around the classes. For some reason,
lupdate
extracts only the class name as context for the translation, not the fully qualified name that includes all namespaces. In my example the context in my *.ts file isNewObject
while it should be
launcher::gui::dialog::NewObject
in the moc source file
moc_NewObject.cpp
the context is listed as the latter.If I overwrite the context in the ts file using the full namespaces, everything works and my class displays the translated texts. So the question now is, how do I make sure,
lupdate
recognizes and factors in the namespaces around my classes. I found some posts online that said to include a translator specific comment:/* TRANSLATOR launcher::gui::dialog::NewObject */
But that was not working either.
When running
lupdate
the strings wrapped intr()
are detected, but there is also this warning:Qualifying with unknown namespace/class ::NewObject
I'm also not using a *.pro file but feed in the source and header files:
lupdate ../src/launcher/gui/dialog/*Object.* -ts translate.ts
-
@tobiSF
just a shot into the dark, but worth a try.
Add theQ_NAMESPACE
macro to your namespaces.
But i would be surprised when lupdate would depend on MOC.I also have the faint feeling that i also already stumbled upon this issue. But i think i didn't track it down to a solution. Also it still was in Qt4.
-
Can you recreate that situation with a minimal project using only one of your classes ?
-
lupdate is not very good in handling complex namespaces. See https://bugreports.qt.io/issues/?jql=status in (Open%2C "In Progress"%2C Reopened%2C Accepted%2C Reported) AND text ~ "lupdate namespace" ORDER BY key DESC
-
@SGaist I have not tried that out yet, will do today and then post it here.
@raven-worx, I'm not sayinglupdate
depends on MOC but the context thatlupdate
extracts is a different one than what is set by theQ_OBJECT
macro. I'll try theQ_NAMESPACE
macro, maybe that'll do the trick.
@Christian-Ehrlicher, is there another tool that is better at this? -
So, here's the small(ish) example program to reproduce the problem. In the full project, the namespaces in the source and header files correspond to the location in (sub)folders of the project.
main.cpp
#include <NewObject.hpp> #include <QtWidgets/QApplication> #include <QtCore/QTranslator> #include <QtCore/QDebug> int main(int argc, char* argv[]) { QApplication application(argc, argv); application.connect(&application, SIGNAL(lastWindowClosed()), &application, SLOT(quit())); QTranslator translator; qDebug() << "Translator loaded: "<< translator.load("test.qm"); application.installTranslator(&translator); launcher::gui::dialog::NewObject object; object.show(); return application.exec(); }
NewObject.hpp
#pragma once #include <QtWidgets/QDialog> namespace launcher { namespace gui { namespace dialog { class NewObject : public QDialog { Q_OBJECT public: NewObject(QWidget* parent = nullptr); }; } } }
NewObject.cpp
#include <NewObject.hpp> #include <QtWidgets/QLabel> #include <QtWidgets/QLineEdit> #include <QtWidgets/QPushButton> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QFormLayout> #include <QtCore/QDebug> namespace launcher { namespace gui { namespace dialog { NewObject::NewObject(QWidget* parent) : QDialog(parent) { QLabel* username_label = new QLabel(tr("username")); QLineEdit* username_edit = new QLineEdit(); QLineEdit* password_edit = new QLineEdit(); password_edit->setEchoMode(QLineEdit::Password); QPushButton* ok = new QPushButton(tr("ok")); connect(ok, &QPushButton::clicked, this, &QDialog::accept); QPushButton* cancel = new QPushButton(tr("cancel")); connect(cancel, &QPushButton::clicked, this, &QDialog::reject); auto button_layout = new QHBoxLayout(); button_layout->addWidget(ok); button_layout->addWidget(cancel); auto layout = new QFormLayout(); layout->addRow(username_label, username_edit); layout->addRow(tr("password"), password_edit); layout->addRow("", button_layout); setLayout(layout); } } } }
CMakeLists.txt
cmake_minimum_required (VERSION 3.9) project (NamespaceTest LANGUAGES CXX) set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD_REQUIRED true) set (CMAKE_INCLUDE_CURRENT_DIR ON) set (CMAKE_AUTOMOC ON) find_package (Qt5 REQUIRED Core Gui Help Widgets ) add_definitions ( ${Qt5Widgets_DEFINITIONS} ) include_directories (${CMAKE_SOURCE_DIR}) set (NamespaceTestSources main.cpp NewObject.cpp ) set (NamespaceTestQtHeaders NewObject.hpp ) add_executable (NamespaceTest ${NamespaceTestSources} ) target_link_libraries (NamespaceTest ${Qt5Core_LIBRARIES} ${Qt5Widgets_LIBRARIES})
To generate the help files, I do
lupdate ./*.cpp -ts test.ts
- Use Qt 5 Linguist to do the actual translations and update the
test.ts
file lrelease -nounfinished test.ts -qm test.qm
- Copy the resulting
test.qm
file into the same directory as the executable
The debug output in the
main.cpp
confirms that the translation file was loaded correctly, but the translations are not applied to the gui because of the context in the ts file being "NewObject" and not "launcher::gui::dialog::NewObject". -
All, the mystery is solved after I asked Qt support about the issue. Turns out, it is associated with the
lupdate
routine not finding the header files for the source files scanned. In order to get the correct and fully namespace qualified context, you'll have to run this (assuming your sources are in a foldersrc
):lupdate ./src -ts test.ts -I ./src
With the
-I
flag, the include path for the header file is handed to lupdate and with this information it produces a correct ts file.Thanks all for trying and your help!