November 17, 2011

maxmotor maxmotor
Lab Rat
69 posts

Another “change qt application language at runtime” thread.

Page  
1

Hello Qt devs!

I have tried following this guide [developer.qt.nokia.com] in order to achieve my goal. I’m having a hard time figuring it out though.

A short explanation of my Qt application:
I have like a base template (bt) which is inherited by all my ui’s (Some have another class in between which also is a template adding navigation buttons).
In the bt, I implement these function, borrowed from the link in the top of the post (with some changes):

  1. void BasicSetup::languageChanged(QString language) {
  2.  loadLanguage(language);
  3. }
  4.  
  5. void BasicSetup::switchTranslator(QTranslator& translator, const QString& filename) {
  6.  // remove the old translator
  7.  qApp->removeTranslator(&translator);
  8.  
  9.  // load the new translator
  10.  if (translator.load(filename))
  11.   qApp->installTranslator(&translator);
  12. }
  13.  
  14. void BasicSetup::loadLanguage(const QString& rLanguage) {
  15.  
  16.  QString m_currLang = "eng.qm"; //this is hardcoded for testing...
  17.  QTranslator m_translator;
  18.  QTranslator m_translatorQt;
  19.  
  20.  if (m_currLang != rLanguage) {
  21.   m_currLang = rLanguage;
  22.   QLocale locale = QLocale(m_currLang);
  23.   QLocale::setDefault(locale);
  24.   QString languageName = QLocale::languageToString(locale.language());
  25.   switchTranslator(m_translator, rLanguage);
  26.   switchTranslator(m_translatorQt, rLanguage);
  27.  }
  28. }
  29.  
  30. void BasicSetup::changeEvent(QEvent* event)
  31. {
  32.     if (event->type() == QEvent::LanguageChange)
  33.     {
  34.         // retranslate designer form
  35.         ui.retranslateUi(this);
  36.  
  37.         // retranslate other widgets which weren't added in designer
  38.         //retranslate(); //Only testing ui designer text
  39.     }
  40.  
  41.     // remember to call base class implementation
  42.     QWidget::changeEvent(event);
  43. }

I was then hoping to be able to call:

  1. languageChanged("dan.qm");

from one of the child ui’s. – in order to change the entire application language.
Sadly this is not working out for me. The label text which I overwrite in code does go back to its “default value” which it has in the designer. (E.x. “Textlabel” which is the default when dragging in a textlabel)
What am I missing?

Thank you for your time!

49 replies

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

Ok, there is a concurrent between two things: the automatical way (with static value) and the programatic way.

I propose that you add a function:

  1. virtual void BasicSetup::translationChanged()  {}

Of course to each time you need a contextual handling of the translation reimplement this function:

  1. virtual void MyComplexUi::translationChanged()  
  2. {
  3.   myLabelInUI->setText(is_loaded ? tr("loaded") : tr("empty"));
  4. }

And of course empty the label to avoid an unnecessary double set of the value.

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

I forget to bind it …

  1. void BasicSetup::changeEvent(QEvent* event)
  2. {
  3.     if (event->type() == QEvent::LanguageChange)
  4.     {
  5.         // retranslate designer form
  6.         ui.retranslateUi(this);
  7.  
  8.         // not automatally retranslation
  9.         translationChanged();
  10.     }
  11.  
  12.     // remember to call base class implementation
  13.     QWidget::changeEvent(event);
  14. }

November 17, 2011

maxmotor maxmotor
Lab Rat
69 posts

But how should this change the fact that I don’t get the translation from my .qm file?

I’m confused…

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

I’m using myself a similar mechanism, the tr() will use the values of your new loaded dictionary.

The values not set in designer will simply not appear in the retranslateUI(), you should simply clear the content of your label in the designer and use alternatively the translationChanged() to handle the non-static cases.

November 17, 2011

maxmotor maxmotor
Lab Rat
69 posts

Clearing the content of my label in the designer does not change anything it seems… The label just becomes empty when when trying to change language.

Also, I’m sure what the translationChanged() function does? Doesn’t it just overwrite whatever is in the label with “loaded” or “empty”?

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

Your code works perfectly, if you put in designer Textlabel as ‘text’ entry, the retranslatedUI() will simply find the equivalent in the string map (of your translator) and replace it and if none found he reset to the original value.

Which means probably: it’s missing the translation of your text?

November 17, 2011

maxmotor maxmotor
Lab Rat
69 posts

Oh but the label has dynamic content.

The text is set when I create an instance of my ui. It takes a parameter called “title”. It’s this variable which should be translated and showed in the label.

  1. TemplateUi::TemplateUi(QString title, QWidget *parent) :
  2.   QWidget(parent) {
  3. ...
  4. }

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

move this code from the c’tr to the ‘famous’ routine:

void translationChanged() { mylabel->setText(tr(“title”)); }

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

what you dream is that Qt detects the ‘title’ with something like:

  1. mylabel->setText(QApplication::translate(mylabel->text()));

Now remarks:

  • how he could find the translation if the text() is chinese?
  • how you will untranslate text like: tr(“It will reboot in %1, please save your work!”).arg(time) ?

November 17, 2011

maxmotor maxmotor
Lab Rat
69 posts

I want to translate my gui to danish. My default language is english.
I expect that Qt looks up my title (“Main menu”) i my “dan.qm” file, and exchange it with the danish equivalent (“Hovedmenu”).

When creating my screen/ui object, I do this:

  1. MenuList mainMenuScreen = new MenuList(tr("Main menu"), &mainMenuListVector);

The first parameter is the title.

November 17, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

This all should be done in 3 parts:

a) Application

  1. class MyApp
  2. {
  3.   QTranslator m_translator;
  4.  
  5. public:
  6.   MyApp()
  7.   {
  8.     installTranslator(&m_translator);
  9.   }
  10.   MyApp::changeTranslation()
  11.   {
  12.      m_translator.load(QFileDialog::getOpenFileName());//here complete ()
  13.   }
  14. };

b) make automatical response to LanguageChange message

  1. class MyWidget : public QWidget, public Ui::MyWidget
  2. {
  3. QLable* m_mainMenuScreen;
  4.  
  5. public:
  6.   MyWidget(QWidget* parent)
  7.   {
  8.      setupUI(this);
  9.  
  10.     m_mainMenuScreen = new MenuList(this, &mainMenuListVector); // don't care here with i18n
  11.   }
  12.  
  13. protected:
  14.   virtual void translationChanged()  
  15.   {
  16.      m_mainMenuScreen->setText(tr("Main menu"));
  17.   }
  18.  
  19.   virtual void changeEvent(QEvent* event)
  20.   {
  21.     if (event->type() == QEvent::LanguageChange)
  22.     {
  23.         // retranslate designer form
  24.         ui.retranslateUi(this);
  25.  
  26.         // not automatally retranslation
  27.         translationChanged();
  28.     }
  29.  
  30.     // remember to call base class implementation
  31.     QWidget::changeEvent(event);
  32.   }
  33. };

C) Now the goal is to make all this simplier, because of course it’s not confortable to have to create manually for each widget all the routine. An idea is to inherit your own interface that you construct too in the constructor, this interface could install an event filter, here my quick and dirty and fast solution, not simply copy, check more the ideas:

  1. template <class T>
  2. class MyTranslation : public QObject
  3. {
  4.     T* m_t;
  5.  
  6. public:
  7.     MyTranslation() : m_t(0) {}
  8.     ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }
  9.  
  10.     void installTranslator(T* t) { m_t = t; m_t->QWidget::installEventFilter(this); }
  11.  
  12. protected:
  13.     virtual void translationChanged() {}
  14.  
  15.     bool eventFilter(QObject *obj, QEvent *event)
  16.     {  
  17.         if (m_t && event->type() == QEvent::LanguageChange)
  18.         {
  19.             // retranslate designer form
  20.             m_t->retranslateUi(this);
  21.  
  22.             // not automatally retranslation
  23.             translationChanged();
  24.         }
  25.         return false;
  26.     }
  27. };
  28.  
  29. class MyWidget : public QWidget, public Ui::MyWidget, public MyTranslation<MyWidget>
  30. {
  31.     QLabel* m_mainMenuScreen;
  32.  
  33. public:
  34.     MyWidget(QWidget* parent) : QWidget(parent)
  35.     {
  36.         // construct
  37.         setupUI(this);
  38.         m_mainMenuScreen = new QLabel(this); // don't care here with i18n
  39.  
  40.         installTranslator(this);
  41.     }
  42.  
  43. protected:
  44.     virtual void translationChanged()
  45.     {
  46.         m_mainMenuScreen->setText(QWidget::tr("Main menu"));
  47.     }
  48. };

November 18, 2011

maxmotor maxmotor
Lab Rat
69 posts

Thanks a lot for your replies BilbonSacquet.

I’m afraid I’m rather thickheaded…

You make me very confused. In your previous post – the goal for me is to make it like shown in c), right? a) and b) are what? Just to show the difference?

So in c) you tell me that all my screen widgets, should inherit from this MyTranslation interface? And why inherit from Ui::MyWidget? I just don’t get it.

And what about the EventFilter – what is that for?

My brain is soon gonna pop out of my skull.

Can you, or anyone else, explain it to me in other words? Or just spell it out more.

I’m sorry :(

But thank you so much for your time!

November 18, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

ok, I go perhaps too fast, I’m stressed right now with work and I will to help the directest way possible. But that’s my bad.

That’s true a) b) is just to explain the steps … and c) the solution.

Yes, all widget which needs translation should inherits this class, that’s necessary to take the message QEvent::LanguageChange and to make ‘automatically’ callback the translation functions. The event filter is necessary because the template MyTranslation doesn’t inherit of the widget (it’s just an QObject) function like event().

The message LanguageChange cycle is now:
app -> MyWidget::event() -> MyTranslator::eventFilter() -> MyWidget::Ui::MyWidget::retranslateUI() , MyWidget::translationChanged()
(if the function is redefined otherwise MyTranslator::translationChanged())

Now why I inherits from Ui::MyClass, you will get the information from the Qt help. But mainly it makes all simplier in the handle of your widgets: same interface, you have all your children direct, you could use the on_mywidget_signal() slots without to connect/disconnct anything.
What I need is that all the widget has the function retranslateUi() accessible in the interface to be called by the template.

I hope that I have helped you with the explanation and not make all darker like in the Morian mines :D

November 18, 2011

maxmotor maxmotor
Lab Rat
69 posts

Okay I’m trying to work my way through it.

I’m writing the MyTranslation class now, but I get some errors:

  1. template <class T>
  2. class MyTranslation : public QObject
  3. {
  4.     T* m_t;    //<------------- "Type 'T' could not be resolved"
  5.  
  6. public:
  7.     MyTranslation() : m_t(0) {}
  8.     ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }
  9.  ...

Do I need to include something in order to do this?

November 18, 2011

BilbonSacquet BilbonSacquet
Lab Rat
75 posts

Now I have verified/compiled the prototype given, I had done an error m_t->retranslateUi(his m_t);

Translation template – MyTranslator.h:

  1. #include <QtCore/QObject>
  2.  
  3. template <class T>
  4. class MyTranslation : public QObject
  5. {
  6.     T* m_t;
  7.  
  8. public:
  9.     MyTranslation() : m_t(0) {}
  10.     ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }
  11.  
  12.     void installTranslator(T* t) { m_t = t; m_t->QWidget::installEventFilter(this); }
  13.  
  14. protected:
  15.     virtual void translationChanged() {}
  16.  
  17.     bool eventFilter(QObject *obj, QEvent *event)
  18.     {
  19.         if (m_t && event->type() == QEvent::LanguageChange)
  20.         {
  21.             // retranslate designer form
  22.             m_t->retranslateUi(m_t);
  23.  
  24.             // not automatally retranslation
  25.             translationChanged();
  26.         }
  27.         return false;
  28.     }
  29. };

The widget – MyWidget.h:

  1. #include <QtGui/QWidget>
  2.  
  3. #include <ui_MyWidget.h>
  4. #include <MyTranslator.h>
  5.  
  6. class MyWidget: public QWidget, public Ui::MyWidget, public MyTranslation<MyWidget>
  7. {
  8.     Q_OBJECT
  9.  
  10.     public:
  11.         MyWidget(QWidget* parent = 0);
  12.         virtual ~MyWidget();
  13.  
  14.     protected:
  15.         virtual void translationChanged();
  16. };

The MyWidget.cpp:

  1. #include "MyWidget.h"
  2.  
  3.  
  4. MyWidget::MyWidget(QWidget* parent) : QWidget(parent)
  5. {
  6.     setupUi(this);
  7.     installTranslator(this);
  8. }
  9.  
  10. MyWidget::~MyWidget() {}
  11.  
  12. void MyWidget::translationChanged()
  13. {
  14.     qWarning() << "translator changed";
  15. }

And of course you need a MyWidget.ui with class MyWidget :) use the default widget. To make it faster I integrate on my current project, and redo a adapted copy/paste. It could have typos but should be ok now.

Page  
1

  ‹‹ Caching QIcon      QWidget with QWidgetPages and how to get QListWidget information from another QWidgetPage ››

You must log in to post a reply. Not a member yet? Register here!