How to do dynamic translation in QML: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
m (Fixed formatting of the code samples by using <syntaxhighlight> instead of <code>)
 
(6 intermediate revisions by 5 users not shown)
Line 1: Line 1:
[[Category:HowTo]]<br />[[Category:Developing_with_Qt::General]]<br />[[Category:Tutorial]]
{{LangSwitch}}
 
[[Category:HowTo]]
[toc align_right="yes" depth="2"]
== Introduction ==
 
= Introduction =


QML relies on the core internationalization capabilities provided by Qt. In the sections below we show how you can load and install the translators on the C++ side and set the needed translation on the QML side.
QML relies on the core internationalization capabilities provided by Qt. In the sections below we show how you can load and install the translators on the C++ side and set the needed translation on the QML side.
Line 11: Line 9:
The example below illustrates the necessary steps to get the dynamic translation to work. In the TranslationTest class we create a property:
The example below illustrates the necessary steps to get the dynamic translation to work. In the TranslationTest class we create a property:


<code>Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)<code>
<code>Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)</code>
 
that simply sets an empty string. This empty string is appended to the text in the QML code. This is a trick to get the whole expression to be reevaluated whenever the property emptyString changes. When not using this trick, QDeclarativeEngine will not know that it should reevaluate the text and do the translation. There is a suggestion for adding a more convenient way to know when the strings should be retranslated.
 
The <tt>selectLanguage()</tt> function checks which language is currently being set, loads and installs the corresponding translation file accordingly. It then emits <tt>languageChanged()</tt>. All QML <tt>Text { }</tt> elements that use <tt>rootItem.emptyString</tt> in their text binding will receive a <tt>NOTIFY</tt> signal causing the <tt>qsTr("...")</tt> to be reevaluated using the new <tt>QTranslator</tt>. Because <tt>rootItem.emptyString</tt> is just "" (an empty string), it will alter the text in your GUI.
 
<tt>TranslationTest::selectLanguage()</tt> is callable from QML as it uses <tt>Q_INVOKABLE</tt>. To be able to call Qt class methods from QML code, the methods must be declared as public slots or conventional methods with the <tt>Q_INVOKABLE</tt> macro. In both cases, Qt methods are available to the Qt Meta-Object system and the methods are callable from QML.
 
<syntaxhighlight lang="cpp">
#include <QtGui>
#include <QtDeclarative>
 
class TranslationTest : public QObject
{
Q_OBJECT
Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)


that simply sets an empty string. This empty string is appended to the text in the QML code. This is a trick to get the whole expression to be reevaluated whenever the property emptyString changes. When not using this trick, QDeclarativeEngine will not know that it should reevaluate the text and do the translation. There is a suggestion in "Jira ":https://bugreports.qt.nokia.com//browse/QTBUG-15602 for for adding a more convenient way to know when the strings should be retranslated.
public:
  TranslationTest() {
  translator1 = new QTranslator(this);
  translator2 = new QTranslator(this);
  }


The selectLanguage() function checks which language is currently being set and loads and installs the corresponding translation file accordingly. It then emits languageChanged(). All QML Text { } elements that use rootItem.emptyString in their text binding will receive a “NOTIFY” signal causing the qsTr(”….”) to be reevaluated using the new QTranslator. Because rootItem.emptyString is just “” (an empty string), it will alter the text in your GUI. This trick is based on the one suggested in the Jira report above and in "this thread":http://developer.qt.nokia.com/forums/viewthread/3307/P15
  QString getEmptyString() {
  return "";
  }


TranslationTest::selectLanguage() is callable from QML as it uses Q_INVOKABLE. To be able to call Qt class methods from QML code, the methods must be declared as public slots or conventional methods with the Q_INVOKABLE macro. In both cases, Qt methods are available to the Qt Meta-Object system and the methods are callable from QML.
Q_INVOKABLE void selectLanguage(QString language) {
  if(language == QString("fr")) {
  translator1->load("t1_fr", ".");
  qApp->installTranslator(translator1);
  }


</code><br />#include <QtGui><br />#include <QtDeclarative>
  if(language == QString("sp")) {
  translator2->load("t1_sp", ".");
  qApp->installTranslator(translator2);
  }


class TranslationTest : public QObject<br />{<br /> Q_OBJECT<br /> Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)<br />public:<br /> TranslationTest() {<br /> translator1 = new QTranslator(this);<br /> translator2 = new QTranslator(this);<br /> }
  if(language == QString("en")) {
  qApp->removeTranslator(translator1);
  qApp->removeTranslator(translator2);
  }


QString getEmptyString() {<br /> return "";<br /> }
  emit languageChanged();
}


Q_INVOKABLE void selectLanguage(QString language) {<br /> if(language QString("fr")) {
  signals:
    translator1->load("t1_fr", ".");
  void languageChanged();
    qApp->installTranslator(translator1);
  }
  if(language  QString("sp")) {<br /> translator2->load("t1_sp", ".");<br /> qApp->installTranslator(translator2);<br /> }<br /> if(language == QString("en")){<br /> qApp->removeTranslator(translator1);<br /> qApp->removeTranslator(translator2);<br /> }<br /> emit languageChanged();<br /> }<br />signals:<br /> void languageChanged();<br />private:<br /> QTranslator *translator1;<br /> QTranslator *translator2;<br />};


int main(int argc, char '''argv[])<br />{<br /> QApplication app(argc, argv);<br /> TranslationTest myObj;<br /> QDeclarativeView view;<br /> view.rootContext()->setContextProperty("rootItem", (QObject''')&amp;myObj);<br /> view.setSource(QUrl::fromLocalFile("main.qml"));<br /> view.show();<br /> return app.exec();<br />}
private:
  QTranslator *translator1;
  QTranslator *translator2;
};
 
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
TranslationTest myObj;
QDeclarativeView view;
view.rootContext()->setContextProperty("rootItem", (QObject*)&myObj);
view.setSource(QUrl::fromLocalFile("main.qml"));
view.show();
return app.exec();
}


#include "main.moc"
#include "main.moc"
 
</syntaxhighlight>
<code>


== QML implementation ==
== QML implementation ==
Line 41: Line 80:
A convenient way to switch between languages is by using buttons, so we create a Button.qml component as follows:
A convenient way to switch between languages is by using buttons, so we create a Button.qml component as follows:


</code><br />import QtQuick 1.0
<syntaxhighlight language="qml">
import QtQuick 1.0


Rectangle {<br /> id: root;<br /> property string label;
Rectangle {
id: root;
property string label;


signal clicked()
signal clicked()


width: 120<br /> height: 30<br /> color: mouse.pressed ? "lightgray" : "white"
width: 120
height: 30
color: mouse.pressed ? "lightgray" : "white"


radius: 4<br /> border.width: 1<br /> border.color: "gray"
radius: 4
border.width: 1
border.color: "gray"


MouseArea {<br /> id: mouse<br /> anchors.fill: parent<br /> onClicked: root.clicked();<br /> }
MouseArea {
  id: mouse
  anchors.fill: parent
  onClicked: root.clicked();
}


Text {<br /> anchors.centerIn: parent<br /> text: root.label<br /> }<br />}<br /><code>
Text {
  anchors.centerIn: parent
  text: root.label
}
}
</syntaxhighlight>


Button.qml is then used in the main.qml file below to create 3 buttons that allow us to switch between English, French and Spanish. Each time a button is pressed, it causes the selectLanguage() of the C++ class to be called with the appropriate language string. This will then trigger the text to be reevaluated and hence translated.
Button.qml is then used in the main.qml file below to create 3 buttons that allow us to switch between English, French and Spanish. Each time a button is pressed, it causes the selectLanguage() of the C++ class to be called with the appropriate language string. This will then trigger the text to be reevaluated and hence translated.


</code><br />import QtQuick 1.0
<syntaxhighlight language="qml">
 
import QtQuick 1.0
Rectangle {<br /> width: 340; height: 150


Column {<br /> anchors.fill: parent; spacing: 20
Rectangle {
width: 340; height: 150


Text {<br /> text: qsTr("Hello") + rootItem.emptyString<br /> font.pointSize: 25; anchors.horizontalCenter: parent.horizontalCenter<br /> }<br /> }
Column {
  anchors.fill: parent; spacing: 20


Row {<br /> anchors.verticalCenter: parent.verticalCenter<br /> Button { label: "English"; onClicked: rootItem.selectLanguage("en"); }<br /> Button { label: "Spanish"; onClicked: rootItem.selectLanguage("sp"); }<br /> Button { label: "French"; onClicked: rootItem.selectLanguage("fr"); }<br /> }<br />}
  Text {
  text: qsTr("Hello") + rootItem.emptyString
  font.pointSize: 25; anchors.horizontalCenter: parent.horizontalCenter
  }
}
Row {
  anchors.verticalCenter: parent.verticalCenter
  Button { label: "English"; onClicked: rootItem.selectLanguage("en"); }
  Button { label: "Spanish"; onClicked: rootItem.selectLanguage("sp"); }
  Button { label: "French"; onClicked: rootItem.selectLanguage("fr"); }
}
}
</syntaxhighlight>


<code>
== The Project File ==


== The project file ==
In the .pro file we need to list the translation files under the <tt>TRANSLATIONS</tt> variable.


In the .pro file we need to list the translation files under the "TRANSLATIONS":http://doc.qt.nokia.com/latest/qmake-variable-reference.html#translations variable.
<syntaxhighlight language="qmake">
TEMPLATE = app
TARGET =
INCLUDEPATH += .


</code><br />TEMPLATE = app<br />TARGET =<br />INCLUDEPATH ''= .
# Input
<br /># Input<br />SOURCES''= main.cpp<br />QT+= declarative<br />TRANSLATIONS = t1_fr.ts t1_sp.ts
SOURCES += main.cpp
QT+= declarative
TRANSLATIONS = t1_fr.ts t1_sp.ts


OTHER_FILES += Button.qml
OTHER_FILES += Button.qml
 
</syntaxhighlight>
<code>


== lupdate and lrelease ==
== lupdate and lrelease ==


We can now create the translation files by running "lupdate":http://doc.qt.nokia.com/latest/linguist-manager.html#lupdate on the .qml file. lupdate will generate the first set of TS translation source files with all the user-visible text but no translations. In our example, we do:
We can now create the translation files by running <tt>lupdate</tt> on the <tt>.qml</tt> file. <tt>lupdate</tt> will generate the first set of TS translation source files with all the user-visible text but no translations. In our example, we do:


</code>lupdate main.qml -ts t1_fr.ts t1_sp.ts</code>
# lupdate main.qml -ts t1_fr.ts t1_sp.ts


to create the translation files. These files can now be opened in "Linguist":http://doc.qt.nokia.com/latest/linguist-manual.html and translated there. Finally, when the application is finished, you need to run "lrelease":http://doc.qt.nokia.com/latest/linguist-manager.html#lrelease on the .ts files to read the .ts files and produce the .qm files used by the application at runtime. For example:
to create the translation files. These files can now be opened in Linguist and translated there. Finally, when the application is finished, you need to run <tt>lrelease</tt> on the <tt>.ts</tt> files to read the <tt>.ts</tt> files and produce the <tt>.qm</tt> files used by the application at runtime. For example:


<code>lrelease t1_fr.ts<br />lrelease t1_sp.ts</code>
# lrelease t1_fr.ts
# lrelease t1_sp.ts


When running the example at this point, it should now switch between English, French and Spanish when clicking the respective buttons.
When running the example at this point, it should now switch between English, French and Spanish when clicking the respective buttons.
== Further reading ==

Latest revision as of 07:30, 9 May 2022

En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh

Introduction

QML relies on the core internationalization capabilities provided by Qt. In the sections below we show how you can load and install the translators on the C++ side and set the needed translation on the QML side.

What to do on the C++ side

The example below illustrates the necessary steps to get the dynamic translation to work. In the TranslationTest class we create a property:

Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)

that simply sets an empty string. This empty string is appended to the text in the QML code. This is a trick to get the whole expression to be reevaluated whenever the property emptyString changes. When not using this trick, QDeclarativeEngine will not know that it should reevaluate the text and do the translation. There is a suggestion for adding a more convenient way to know when the strings should be retranslated.

The selectLanguage() function checks which language is currently being set, loads and installs the corresponding translation file accordingly. It then emits languageChanged(). All QML Text { } elements that use rootItem.emptyString in their text binding will receive a NOTIFY signal causing the qsTr("...") to be reevaluated using the new QTranslator. Because rootItem.emptyString is just "" (an empty string), it will alter the text in your GUI.

TranslationTest::selectLanguage() is callable from QML as it uses Q_INVOKABLE. To be able to call Qt class methods from QML code, the methods must be declared as public slots or conventional methods with the Q_INVOKABLE macro. In both cases, Qt methods are available to the Qt Meta-Object system and the methods are callable from QML.

#include <QtGui>
#include <QtDeclarative>

class TranslationTest : public QObject
{
 Q_OBJECT
 Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)

 public:
  TranslationTest() {
   translator1 = new QTranslator(this);
   translator2 = new QTranslator(this);
  }

  QString getEmptyString() {
   return "";
  }

 Q_INVOKABLE void selectLanguage(QString language) {
  if(language == QString("fr")) {
   translator1->load("t1_fr", ".");
   qApp->installTranslator(translator1);
  }

  if(language == QString("sp")) {
   translator2->load("t1_sp", ".");
   qApp->installTranslator(translator2);
  }

  if(language == QString("en")) {
   qApp->removeTranslator(translator1);
   qApp->removeTranslator(translator2);
  }

  emit languageChanged();
 }

 signals:
  void languageChanged();

 private:
  QTranslator *translator1;
  QTranslator *translator2;
};

int main(int argc, char *argv[]) {
 QApplication app(argc, argv);
 TranslationTest myObj;
 QDeclarativeView view;
 view.rootContext()->setContextProperty("rootItem", (QObject*)&myObj);
 view.setSource(QUrl::fromLocalFile("main.qml"));
 view.show();
 return app.exec();
}

#include "main.moc"

QML implementation

A convenient way to switch between languages is by using buttons, so we create a Button.qml component as follows:

import QtQuick 1.0

Rectangle {
 id: root;
 property string label;

 signal clicked()

 width: 120
 height: 30
 color: mouse.pressed ? "lightgray" : "white"

 radius: 4
 border.width: 1
 border.color: "gray"

 MouseArea {
  id: mouse
  anchors.fill: parent
  onClicked: root.clicked();
 }

 Text {
  anchors.centerIn: parent
  text: root.label
 }
}

Button.qml is then used in the main.qml file below to create 3 buttons that allow us to switch between English, French and Spanish. Each time a button is pressed, it causes the selectLanguage() of the C++ class to be called with the appropriate language string. This will then trigger the text to be reevaluated and hence translated.

import QtQuick 1.0

Rectangle {
 width: 340; height: 150

 Column {
  anchors.fill: parent; spacing: 20

  Text {
   text: qsTr("Hello") + rootItem.emptyString
   font.pointSize: 25; anchors.horizontalCenter: parent.horizontalCenter
  }
 }
 
 Row {
  anchors.verticalCenter: parent.verticalCenter
  Button { label: "English"; onClicked: rootItem.selectLanguage("en"); }
  Button { label: "Spanish"; onClicked: rootItem.selectLanguage("sp"); }
  Button { label: "French"; onClicked: rootItem.selectLanguage("fr"); }
 }
}

The Project File

In the .pro file we need to list the translation files under the TRANSLATIONS variable.

TEMPLATE = app
TARGET =
INCLUDEPATH += .

# Input
SOURCES += main.cpp
QT+= declarative
TRANSLATIONS = t1_fr.ts t1_sp.ts

OTHER_FILES += Button.qml

lupdate and lrelease

We can now create the translation files by running lupdate on the .qml file. lupdate will generate the first set of TS translation source files with all the user-visible text but no translations. In our example, we do:

# lupdate main.qml -ts t1_fr.ts t1_sp.ts

to create the translation files. These files can now be opened in Linguist and translated there. Finally, when the application is finished, you need to run lrelease on the .ts files to read the .ts files and produce the .qm files used by the application at runtime. For example:

# lrelease t1_fr.ts
# lrelease t1_sp.ts

When running the example at this point, it should now switch between English, French and Spanish when clicking the respective buttons.