QtLinguist: Define a single context - qt

How do I define a single context in a QtLinguist .ts file, rather than having one context per file?
I need this because I have identical strings showing up in different files for which I want the same translation (and no duplicates)

From C++, you can explicitly specify the translation context on a string-by-string basis by using the static function QCoreApplication::translate(const char* context, const char* text) instead of the traditional QObject::tr(const char* text) (see this doc for more details: http://doc.qt.io/qt-5/qcoreapplication.html#translate)
e.g.
menuItem->setText(QCoreApplication::translate("UniversalContext", "Exit"));
Similarly, you can specify the context in QML using the qsTranslate(context, text) function, a la
Text { text: qsTranslate("UniversalContext", "hello") }
These will all get picked up by lupdate as belonging to the same context (duplicate strings will only show up once in your .ts file)
If you have a lot of strings and it gets painful to specify the context on every call to translate/qsTranslate, you should be fairly simple to create a 1-arg macro (c++) or a js function (qml) as a wrapper.

Related

QObject::tr() not translating dynamically generated strings

QObject::tr("%1").arg(_value);
Here _value is of QString type, which is dynamically generated. Is the above way correct to translate dynamically generated strings as in my code it doesn't seem to work.
There are two steps:
1. Make Qt extract the strings for translation.
This means using one of
tr() in a QObject subclass
QCoreApplication::translate()
QT_TR_NOOP / QT_TRANSLATE_NOOP
lupdate will extract the strings passed to those functions/macros, and make them available to linguist for translation.
2. Performing the translation (i.e. the "lookup")
This is again done by tr() and QCoreApplication::translate(). So for instance:
// marking the strings for extraction
static const char *strings[] = {
QT_TRANSLATE_NOOP("MyContext", "hello"),
QT_TRANSLATE_NOOP("MyContext", "world");
};
// performing the translation at runtime
qApp->translate("MyContext", strings[0]);
There's a ton of documentation about the whole process, see here.
You perhaps meant to do:
QObject::trUtf8(QString("%1").arg(_value).toUtf8(), "dynamic1");
You must ensure that your translation file contains all values that _value can take with the dynamic1 for the disambiguation value, iff you wish to disambiguate them, that is.
Of course, the _value must be selected from a fixed list of strings anyway - since tr isn't a human translator, it simply does a lookup of the string in a translation list.
So, you should really do this:
QString value;
select (variant) {
case VarA: value = QObject::tr("foo"); break;
case VarB: value = QObject::tr("bar"); break;
...
}
That way the relevant strings will be included in the translation list.
You're trying to translate _value in the wrong place. As stated in other answers, QObject::tr() can't guess by itself how to translate anything. It works only on fixed strings. You should mark constants you're setting _value to for translation, not _value itself.

What's the Nicest Way to do This? (Qt and Enum style arguments)

In Qt, it is common to see something similar to the following:
QSettings obj3(QSettings::SystemScope, "MySoft", "Star Runner");
The important bit is the QSettings::SystemScope, which is an enum.
I want to have a settings provider (pay no attention to the previous example here, it has nothing to do with the following), with a get/set property.
Settings.set(Settings::refreshRate)
The refreshRate has to link to a key (string), and a default value (variant).
Should I make an enum and two dicts for the key and default values, or make a struct and a whole bunch of variables that encapsulate the settings I need? Should I try something else?
Thanks!
Edit!
This is what I did.
// Interface
class Settings {
public:
static QVariant get(Setting setting);
static void set(Setting setting, QVariant value);
const static Setting serverRefreshRate;
const static Setting serverReportTimeout;
};
// Implementation
const Setting Settings::serverRefreshRate = { "server/refreshRate", 10000 };
const Setting Settings::serverReportTimeout = { "server/reportTimeout", 1000 };
Well I guess since you're using enum which most likely will be easily castable to numbers from to 0 to N-1 I guess just storing variants and strings in two vectors or one vector of pairs would work just fine.
There's also another question though -- how to initialize all of that and how you will be adding new settings to it. I can suggest two methods - first one writing a bunch of function calls with arguments: enum, string, variant. Thus way though if programmer adds another value to enum he can forget to call initializing function. The other way is to create function (or maybe two) which will do switch on all enum values (without default case) and will return pair of string and variant. You can turn on the compiler warning about all enum values being processed in switch and thus way control if you forget to implement some of them in that function. And then initialize your structures using loop on all of enum values. These initializing functions should be called somewhere near the beginning of your program (before reading settings initially).
Well, that's my thoughts on it, you are free to try some different ways though.

qt QTranslator reuse

I noticed that the Qt documentation is not very verbose on some of the aspects of the translations. I was fooling around with it trying to figure out their behaviour using trial & error. The ultimate goal is to get the translation changed on runtime but I am very confused as to what extent the QTranslator object can be re-used.
Consider this (where 'a' is the main instance of the application):
QTranslator translator;
translator.load("mytranslation_cz");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator)
Now the translator was removed from the application but what happened to the translator object?
In my tests when above code was followed by this again
translator.load("mytranslation_fr");
a.installTranslation(&translator);
it did not do anything in main() and it crashed the application when called from one of the widgets (using pointer to main app).
Therefore I am suspecting that I would need to create one QTranslator object per translation I want to load in the application and that I cannot reuse the QTranslator object. Am I right in this assumption?
And as a side question. Assuming the QTranslator object is untouched by the removeTranslation(), is it possible to simply install it later again like this?
QTranslator translator;
QTranslator translator1;
translator.load("mytranslation_cz");
translator1.load("mytranslation_fr");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator);
a.installTranslation(&translator1);
(...)
a.removeTranslation(&translator1);
a.installTranslation(&trasnlator); //Will this work?
Thanks for any clarification as I am somewhat confused as to what happens to the QTranslation objects when installing and removing translations from the application and especially if the QTranslation object can be reused for multiple translations somehow on runtime?
QTranslator::load basically in simple sense can be considered as a function that opens a given .qm file, reads in all the translated values and loads it in for a specific language.
Now in general operation you would not want to reuse this for many languages as by "reusing" (even if its allowed) your adding the overhead of parsing this given .qm file for every language every time you switch your UI language, which is just basically an overhead you don't need.
Your assumption of creating a QTranslator for each language is correct. As for your side question, Yes you can also re-use it. Thats the benefit of having individual QTranslator objects per translation. Just call qApp->removeTranslator() with the current translation and then qApp->installTranslator() with the new one. This way you are reusing the loaded translations as and when you please.
The way we structure this is by sub-classing QApplication and adding 2 functions
void Application::CreateTranslators() {
// translators_ is a QMap<QString, QTranslator*>
if (!translators_.isEmpty())
return;
QStringList languages;
languages << "en" << "ar" << "zh";
foreach(QString language, languages) {
QTranslator* translator = new QTranslator(instance());
translator->load(language);
translators_.insert(language, translator);
}
}
Now this function is called at the very start of the application.
2nd function is as following
void Application::SwitchLanguage(QString language) {
// current_translator_ is a QTranslator*
if (current_translator_)
removeTranslator(current_translator_);
current_translator_ = translators_.value(language, nullptr);
if (current_translator_)
installTranslator(current_translator_);
}
That's it. Using the second function you can switch your language at run-time as you please.
Couple things you'll also need to be aware of is changing QTranslator at run-time will update all translations from your .ui file strings marked as translatable automatically, however those set from code will not be. To get that you will have to override QWidget::changeEvent() and then check if the event is of type QEvent::LanguageChange and then set the required strings for that QWidget accordingly (Don't forget the tr() while doing so)

In Qt, how to setCodecForCStrings globally (in header)?

I'm developing a bunch of Qt applications in C++ and they all use some modules (translation units) for common functionality that use Qt as well.
Whenever I convert a C string (implicit conversion) or C++ string object (fromStdString()) to a QString object, I expect the original data to be UTF-8 encoded and vice versa (toStdString()).
Since the default is Latin-1, I have to set the codec "manually" (in the init procedure of every one of my programs) to UTF-8:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
Not all of my modules have an init procedure. The ones containing a class do (I can put this line in the class constructor), but some modules just contain a namespace with lots of functions. So there's no place for setCodecForCStrings(). Whenever I convert from/to a QString implicitly (from within one of my modules), I rely on the codec being already set by the init procedure of the main program, which seems to be a rather bad solution.
Is there a reliable way to set the codec to UTF-8 in my modules, or will I just have to be very careful not to use implicit conversions at all (in my modules at least) and write something like std::string(q.toUtf8().constData()) instead of q.toStdString()?
This can be done using a class definition for an automatically instantiated singleton-similar class having some init code in its constructor:
class ModuleInit {
static ModuleInit *instance;
public:
ModuleInit() {
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
}
};
ModuleInit * ModuleInit::instance = new ModuleInit(); // put this line in .cpp!
Putting this code anywhere into any project will set the text codec to UTF-8. You can, however, overwrite the text codec in the main() method, since the code above is executed even before the main() method.
With "anywhere" I of course mean places where it is syntactically allowed to. Put the class definition in some header file, and the instantiation in the appropriate .cpp file.
You can even put the whole code in one .cpp file, say moduleinit.cpp which you compile and link, but never explicitly use (there is no header file you could include...). This will also avoid accidental instantiations except the very first one and you will not have any problems with duplicate class names.
Note that you can't set the codec for C-strings in one particular file. Setting the codec using QTextCodec::setCodecForCString will set it application-wide!

qt tr() in static variable

I have problem concerning translations in qt. All translations in my porject work fine, but one, which is in a static variable of a class. Corresponding part of code looks as follows
The header file is similar to this:
typedef struct {
int type;
QString problematicString;
} info;
MyClass::QObject_Descendant
{
Q_OBJECT;
//some functions like constructor, destructor... etc.
....
static info myClassInfo;//class that makes problems
}
and in implementation file I initialize the variable as follows:
info MyClass::myClassInfo={
1,
tr("something to be translated")
};
And whatever I do (trying with QT_TR_NOOP, then tr() and others) I cannot get myClassInfo.problematicString translated. The weirdest thing is that the text "something to be translated"
appears in *.ts file.
If someone has any hints, please share them with me. Thanks in advance.
Chris.
Static variables are instantiated (and thus, constructor code run) before your int main function is run. The translation code is set up in the QApplication constructor (I believe), which isn't run until your int main function has been entered. Thus, you are trying to get the translation of a string before the code to support it has been initialized.
To avoid this, you could either accept that the given string isn't translated and explicitly translate it every time it is used, or use the Construct on First Use idiom instead of a static member variable.

Resources