Anshul Gupta
Anshul Gupta

Reputation: 51

Use qsTr() on text inside json file

I am using qml, where I take some information from json file, and then I display it. The only problem is that I want to do Internationalization. I have tried using qsTr(), but I am not able to figure out how to make it work by taking information from json file. Here is an example:

JSON file:

{"information" : "Sample Text"}

JavaScript file

var read = JSON.parse(file)
var info = qsTr(read.information)

But this doesn't work as I must enter a string directly in the qsTr(). So is there any way I can achieve Internationalization on the info variable?

Upvotes: 3

Views: 2097

Answers (2)

sadeq
sadeq

Reputation: 21

you can type your JSON as a variable inside a js library file and use qsTr() there.

test.js

.pragma library

var someData=[
   {
      "title": qsTr("Example"),
      "path":"qrc:/Example.qml"
   }]

now you import your js file and use it as below

import "qrc:/test.js" as Test;
Item{
Component.onCompleted: console.log(JSON.strigify(TEST.data));
}

Upvotes: 0

Brad van der Laan
Brad van der Laan

Reputation: 353

I ran into this exact same issue when working on a project recently but I was writing a front end for a product which talked to the backend over JSON formatted API calls. Some of the user facing strings were coming from the backend via the JSON calls.

This issue occurred on two projects, in one we just updated the API to allow me to request which language I wanted the strings in and left if up to the backend to properly translate the strings they returned; however, in the other project this was not an option (and its not an option for you since your parsing them out of a file and don't have a backend) so instead we choose to do something that is a bit hokey but it works.


Before I get into it I would recommend a safer approch might be to only store ids (like message codes) in your file and leave the actual strings in your UI code if your going to do translations as its easier to maintain if you end up modifying strings later on (which I'll explain below). If that isn't an option for you then try the following.


The qsTr() function ( or the tr() function in C++ ) takes a string and performs a look up in a translation resource (*.qm) file using the string provided as the key in order to find the translated version. This can work with string literals or string variables.

Both of the following will work at run time assuming translations exists in the *.qm file:

Text { text: qsTr("Hello World"); }
Text {
    property string unTranslatedString: "Hello World"
    text: qsTr( unTranslatedString )
}

The issue is that the tools lupdate and lrelease used to generate the *.ts and *.qm files used by Qt for internationalisation won't understand the string variable. lupdate statically combs over your code file line by line and looks for any strings encased in qsTr() or tr() methods and extracts them into the *.ts file, which is an XML file that can be used by translators to generate a list of translations for each translatable string found in the application. This is done on uncompiled code, so it can't understand variables as it won't be able to know what string is stored in the variable at run time. This is why the documentation says that you can only use string literals with qsTr() and tr() and why lupdate will give you warnings when it hits a qsTr()/tr() statement which is using a variable vs. a string literal; however, qsTr()/tr() will use strings stored in a variable to look up translations.

Your code example above will work the issue is that because lupdate can't make heads or tails of it your string "Sample Text" won't be in your translation resource file so qsTr() won't find a translated version of it and default to "Sample Text". If however you simply made sure that your *.qm file contained the translation for the string "Sample Text" with the correct context it will work.

To achieve this you simply need to add the following anywhere in your application (either in your QML or C++ code):

QT_TR_NOOP("Sample Text");

Its a macro which takes a string but does not do a translation look up, it will return the untranslated string. If you simply put it somewhere in your project lupdate will pick up the string, it scans for qsTr(), tr(), QT_TR_NOOP() and other similar functions/macros, and put it in the *.ts file and all strings in the *.ts file will be added to your *.qm files when you run lrelease. The QT_TR_NOOP() macro does not even need to be executed by your application at run time as its only being used to ensure that your dynamic strings are added to your translation files at pre-compile time.

-- Side note another idea since your reading from a file is that you could write a script or manually injet your strings into the *.ts file after you run lupdate just make sure your contexts match, more on contexts below.

One thing to keep in mind is that Qt's translation system uses contexts to make sure that if you have the same word on two different views but on view one it has a different meaning then on view two you can provide different translation for the two words in some languages based on their context. When you use qsTr()/tr() a context is defined automatically using the class name. So if your QML code is in one file and you put the QT_TR_NOOP() macro in another file they will have different contexts which will result in your code not working, they are the same strings but in different contexts. To explicitly set the context for a given string use QT_TRANSLATE_NOOP() and qsTranslate().

Example:

QT_TRANSLATE_NOOP("MyDynamicStringsContext", "Sample Text")

var read = JSON.parse(file)
var info = qsTranslate("MyDynamicStringsContext", read.information)

Now because this is all done statically sadly you can't use a variable for the context string either, it must be a string literal, but what this does is make sure that the string "Sample Text" shows up in your *.ts file, that your translators can provide translations for it and you can generate a *.qm file with lrelease which will contain the translated string for "Sample Text" with the key "Sample Text" and the context "MyDynamicStringsContext" in order for qsTranslate() to find it at run time.

The draw backs is that this is a bit hokey as I said before. Its vulnerable to changes in the string done on the other side of the JSON call breaking the translations; that is if the other end changes the capitalisation (yes this is case sensitive) or adds a punctuation or even a trailing space the strings won't match and qsTranslate() will fail to find the translated string. It also means you need to know all the possible strings ahead of time so you can make sure you have translations for them. So the example would look more like this:

QT_TRANSLATE_NOOP("MyDynamicStringsContext", "Sample Text")
QT_TRANSLATE_NOOP("MyDynamicStringsContext", "Sample Text ")
QT_TRANSLATE_NOOP("MyDynamicStringsContext", "sample text")
QT_TRANSLATE_NOOP("MyDynamicStringsContext", "Other Possible Sample Text")


var read = JSON.parse(file)
var info = qsTranslate("MyDynamicStringsContext", read.information)

Oh and lastly lupdate will scream at you each time it hits a qsTranlate() statement which wraps a variable as it won't know what to do with it and will ignore it. Which is ok as you know you have it covered but you could end up drowning in warning messages and miss some legit issues.

So its fragile but it is an option if you have a reasonable amount of control over both ends but can't push the burned of translating the strings over to the backend.

I hope that helps you and answers your question

Until next time think imaginatively and design creatively

Upvotes: 5

Related Questions