Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Another "change qt application language at runtime" thread.



  • 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.

    @TemplateUi::TemplateUi(QString title, QWidget *parent) :
    QWidget(parent) {
    ...
    }@



  • move this code from the c'tr to the 'famous' routine:

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



  • what you dream is that Qt detects the 'title' with something like: @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) ?


  • 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:

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

    The first parameter is the title.



  • This all should be done in 3 parts:

    a) Application
    @
    class MyApp
    {
    QTranslator m_translator;

    public:
    MyApp()
    {
    installTranslator(&m_translator);
    }
    MyApp::changeTranslation()
    {
    m_translator.load(QFileDialog::getOpenFileName());//here complete ()
    }
    };
    @

    b) make automatical response to LanguageChange message
    @
    class MyWidget : public QWidget, public Ui::MyWidget
    {
    QLable* m_mainMenuScreen;

    public:
    MyWidget(QWidget* parent)
    {
    setupUI(this);

    m_mainMenuScreen = new MenuList(this, &mainMenuListVector); // don't care here with i18n
    

    }

    protected:
    virtual void translationChanged()
    {
    m_mainMenuScreen->setText(tr("Main menu"));
    }

    virtual void changeEvent(QEvent* event)
    {
    if (event->type() == QEvent::LanguageChange)
    {
    // retranslate designer form
    ui.retranslateUi(this);

        // not automatally retranslation
        translationChanged();
    }
    
    // remember to call base class implementation
    QWidget::changeEvent(event);
    

    }
    };
    @

    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:

    @
    template <class T>
    class MyTranslation : public QObject
    {
    T* m_t;

    public:
    MyTranslation() : m_t(0) {}
    ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }

    void installTranslator(T* t) { m_t = t; m_t->QWidget::installEventFilter(this); }
    

    protected:
    virtual void translationChanged() {}

    bool eventFilter(QObject *obj, QEvent *event)
    {  
        if (m_t && event->type() == QEvent::LanguageChange)
        {
            // retranslate designer form
            m_t->retranslateUi(this);
    
            // not automatally retranslation
            translationChanged();
        }
        return false;
    }
    

    };

    class MyWidget : public QWidget, public Ui::MyWidget, public MyTranslation<MyWidget>
    {
    QLabel* m_mainMenuScreen;

    public:
    MyWidget(QWidget* parent) : QWidget(parent)
    {
    // construct
    setupUI(this);
    m_mainMenuScreen = new QLabel(this); // don't care here with i18n

        installTranslator(this);
    }
    

    protected:
    virtual void translationChanged()
    {
    m_mainMenuScreen->setText(QWidget::tr("Main menu"));
    }
    };
    @



  • 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!



  • 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



  • Okay I'm trying to work my way through it.

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

    @template <class T>
    class MyTranslation : public QObject
    {
    T* m_t; //<------------- "Type 'T' could not be resolved"

    public:
    MyTranslation() : m_t(0) {}
    ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }
    ...@

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



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

    Translation template - MyTranslator.h:
    @
    #include <QtCore/QObject>

    template <class T>
    class MyTranslation : public QObject
    {
    T* m_t;

    public:
    MyTranslation() : m_t(0) {}
    ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }

    void installTranslator(T* t) { m_t = t; m_t->QWidget::installEventFilter(this); }
    

    protected:
    virtual void translationChanged() {}

    bool eventFilter(QObject *obj, QEvent *event)
    { 
        if (m_t && event->type() == QEvent::LanguageChange)
        {
            // retranslate designer form
            m_t->retranslateUi(m_t);
    
            // not automatally retranslation
            translationChanged();
        }
        return false;
    }
    

    };
    @

    The widget - MyWidget.h:

    @
    #include <QtGui/QWidget>

    #include <ui_MyWidget.h>
    #include <MyTranslator.h>

    class MyWidget: public QWidget, public Ui::MyWidget, public MyTranslation<MyWidget>
    {
    Q_OBJECT

    public:
        MyWidget(QWidget* parent = 0);
        virtual ~MyWidget();
    
    protected:
        virtual void translationChanged();
    

    };
    @

    The MyWidget.cpp:
    @
    #include "MyWidget.h"

    MyWidget::MyWidget(QWidget* parent) : QWidget(parent)
    {
    setupUi(this);
    installTranslator(this);
    }

    MyWidget::~MyWidget() {}

    void MyWidget::translationChanged()
    {
    qWarning() << "translator changed";
    }
    @

    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.



  • Thank you, but I still get the error in the start when writing:

    @T* m_t;@



  • hmm .. which compiler are you using ?



  • mingw



  • you could try to replace 'template <class T>' by 'template <typename T>'



  • No change :(



  • and for no warnings compilation add: Q_UNUSED(obj); into the enventFilter()



  • But the error happens before this? I have only written:

    @template <class T>
    class MyTranslation : public QObject
    {
    T* m_t;

    public:
    MyTranslation() : m_t(0) {}
    ~MyTranslation() { if (m_t) m_t->QWidget::removeEventFilter(this); m_t = 0; }
    };@

    And it gives the error...



  • I have found some help in internet, seems that your interface (eclipse) not the compiler the problem.

    "here some indication":stackoverflow.com/questions/6503551/opencv-type-iplimage-could-not-be-resolved



  • Thank you :)

    I wrote the code in Qt Creator instead of Eclipse, and no errors appear in the MyTranslation.h file.

    I am now trying to set up the "MyWidget.h" file.

    First of all I can't inherit from Ui::MyWidget. Can this have something to do with MyWidget being a base class for all my ui's?

    Secondly, if I leave out Ui::MyWidget, but inherit from MyTranslation<MyWidget>, my ui's seems to get errors, when it comes to the signal and slot connections made.

    "reference to 'connect' is ambiguous"



  • If you couldn't inherits from the Ui::MyWidget you should adapt the code in the handler to find the restranslateUI(), or move the retranslateUI() call in the translationChanged() function.

    yes, because you have 2x QObject inheritence. Use at the place the on_awidget_asignal() slots or in this case use QWidget::connect().



  • I'm sorry, I don't quite understand what you are saying? :(



  • Sorry, regarding the way I do multiple inheritence, you could find help in the Qt documentation: "Using a Design UI File in Your Application", pay a look to the last chapter:"Automatic Connections".



  • The link you gave me says

    @QMetaObject::connectSlotsByName(this);@

    Gives me this error: “'QObject' is an ambiguous base of 'MyWidget'”

    • But you use QWidget:: Is there a reason for this?


  • You inherit twice from QObject:

    @
    class MyTranslation : public QObject
    class MyWidget: public QWidget, public Ui::MyWidget, public MyTranslation<MyWidget>
    @

    So you inherit from QObject via QWidget (which is a QObject subclass) and via MyTranslation. This is not supported!



  • BilbonSacquet you must have met this problem then?



  • It's supported, you should simply take care of the conflict. But we could do it through delegations (I take in consideration all what we say):

    @
    #include <QtCore/QObject>

    template <class T>
    class MyTranslator : public QObject
    {
    T* m_t;

    public:
    MyTranslator(T* widget)
    : m_t(widget)
    {
    m_t->installEventFilter(this);
    }

    ~MyTranslator() 
    { 
        m_t->removeEventFilter(this); 
    }
    
    bool eventFilter(QObject *obj, QEvent *event)
    {
        Q_UNUSED(obj);
    
        if (m_t && event->type() == QEvent::LanguageChange)
        { 
            // not automatally retranslation
            m_t->T:translationChanged();
        }
        return false;
    }
    

    };

    #include <QtGui/QWidget>

    #include <ui_MyWidget.h>
    #include <MyTranslator.h>

    class MyWidget: public QWidget
    {
    Q_OBJECT

    public:
        MyWidget(QWidget* parent = 0)
        : QWidget(parent), 
          m_translator(this)
        {
            ui.setupUi(this);
        }
    
        virtual ~MyWidget() {}
    
        virtual void translationChanged()
        {
            ui.restranslateUi();
    
            qWarning() << "translator changed";
        }
    
    private:
        MyTranslator<MyWidget> m_translator;
        Ui::MyWidget ui;
    

    };
    @



  • The "docs of moc":http://doc.qt.nokia.com/4.7/moc.html#multiple-inheritance-requires-qobject-to-be-first state it clearly:

    bq. Multiple Inheritance Requires QObject to Be First
    If you are using multiple inheritance, moc assumes that the first inherited class is a subclass of QObject. Also, be sure that only the first inherited class is a QObject.
    [emphasis by me, Volker]



  • But now you don't inherit from MyTranslator, in the code you just postet?



  • Yes, I do both variation (just for you)!

    The inherited and the delegated! Both are correct ... just now choose the one you want :).



  • Even the multiple inherited is correct because (to take the sitation of Volker) the first inherited IS a QObject (QWidget is a QObject)!! :)



  • [quote author="BilbonSacquet" date="1321629111"]Even the multiple inherited is correct because (to take the sitation of Volker) the first inherited IS a QObject (QWidget is a QObject)!! :)[/quote]

    Don't pick what you want to have and leave out the annoying details that make your design fail!

    bq. Also, be sure that only the first inherited class is a QObject.



  • Both was tested and works! :)

    But I give you right, it's never good to multiple inherits QObject. So that's why I rewrite it with a delegation.

    But in fact to achieve some automatical translation mechanism (without to hack the current implementation) we need in QObject a callback like in widget for paint event with paintEvent().



  • working != officially supported

    And the state of "working" may vary between platforms and compilers.

    And I don't get the purpose of the template class at all. It calls retranslateUi in the templates argument, so it has to be implemented in all the widgets. Where's the big difference and the coder's savings to implementing changeEvent? On will have to touche every widget class eventually, so why not make it the "standard" way?



  • You have right not much, I have tried to make something lighter through template (like he suggests) and to have not to write too much code. :)

    For the main problem, you should retranslate anyway yourself the 'dynamical' strings.



  • There is no significant difference in implementing changeEvent() or translationChanged() - both contain basically the same code regarding the translation (calling ui.retranslateUi() and possibly some manual code). There's a minimal overhead with the event handling in changeEvent().

    Just a matter of KISS - no more complex than needed :)



  • I want to thank you for helping me out! I am very grateful.

    I've decided to follow Volkers advice and do it the "official" way. I'm sorry BilbonSacquet - I am really grateful for your work!

    I'm trying to follow the example from the link I give in #0 again. I have a few questions though.
    How/where is the "language name" added to the actual Ui, enabling the user to choose between available languages? I'm not sure what the component in the example is, but I'm gonna use a qtablewidget to hold my language names. Is this done the same way as in the example?

    Thank you so much for your time!



  • In the wiki article, the available languages are determined by the available translation files (.qm) in method createLanguageMenu. You can go this way too, or implement your own algorithm, of course.



  • I'm now able to change the language on the static strings made in my designer.

    I'm not sure how to change it for all the strings written in code. I thought it would be done the same way as the designer strings. When changing the language, the dynamic string changes to the content of the string in the designer. This is not intended of course - instead it should get the translation from the .qm file.

    I'm also a bit confused concerning a line in the wiki article:

    @void MainWindow::loadLanguage(const QString& rLanguage)
    {
    if(m_currLang != rLanguage)
    {
    m_currLang = rLanguage;
    QLocale locale = QLocale(m_currLang);
    QLocale::setDefault(locale);
    QString languageName = QLocale::languageToString(locale.language());
    switchTranslator(m_translator, QString("TranslationExample_%1.qm").arg(rLanguage));
    switchTranslator(m_translatorQt, QString("qt_%1.qm").arg(rLanguage)); <------------
    ui.statusBar->showMessage(tr("Current Language changed to %1").arg(languageName));
    }
    }@

    The line marked with an arrow is the one confusing me. My guess is that it has something to do with my mentioned problem (Change language for dynamic strings)?

    Thank you for your time!

    EDIT:
    I of course wrap my strings in tr().

    When the changeEvent is called with a LanguageChange event, @ui.retranslateUi(this);@ is "run". When looking into this function, it seems that the application indeed just takes the string written in the designer.

    How do I get the string from my .qm file instead?

    EDIT2:
    I just wanted to add that my application is made using a model-view-controller pattern. All my Ui's are initialized in the controller class. Upon initialization the Ui's get a title. I don't know if this info helps?

    Please, I'm totally locked here :(



  • The strings in ui.retranslateUi() are taken from the .qm file.
    Of course, they contain only the strings in the form itself. Everything else - i.e. the strings used programmatically in your code - must be retranslated in the event too.

    So, if you have somewhere in your C++ code a line like this:

    @
    ui->infoLabel->setText(tr("Let's go this way"));
    @

    You will have to add to the event handler that very same line too.

    There is no automatic retranslation in Qt! You must do this manually on a language change. ui.retransalateUi() does exactly this - it's just for convenience that the code is generated by uic.

    The line marked with an arrow just loads the Qt translations. If you omit that, the system messages (like in file dialogs etc.) are not translated.

    Additionally, I strongly recommend reading thoroughly through the docs of the translation and internationalization system in order to understand what's going on and how Qt computes the translated strings:

    • "Internationalization with Qt":/doc/qt-4.7/internationalization.html
    • "Qt Linguist for Programmers":/doc/qt-4.7/linguist-programmers.html
    • [[Doc:QTranslator]] API docs
    • "Writing Source Code for Translation":/doc/qt-4.7/i18n-source-translation.html
    • The "Hello tr() example":/doc/qt-4.7/linguist-hellotr.html
    • The "I18N example":/doc/qt-4.7/tools-i18n.html


  • Thank you for the reply.

    I just wanna illustrate my scenario:

    From menuList.cpp
    @MenuList::MenuList(QString title, , QWidget *parent) : QWidget (parent) {
    ui.setupUi(this);
    ui->infoLabel->setText(title);
    }@

    From controller.cpp
    @void Controller::initializeScreens() {
    mainMenuScreen = new MenuList(tr("Main menu"), ...);
    settingsMenuScreen = new MenuList(tr("Settings"), ...);
    }@

    I tried adding
    @ui->infoLabel->setText(title);@
    to the event handler (in the MenuList class) like you said, but I get nothing.

    Does it have something to do with me inserting 'title' instead of an actual string (like "textstring here")?

    I've already looked at many of the links you provided, but I will look again.

    Thanks!



  • you must handle the language change in that class, that has the tr("xxx") for the string. You do not do it in the class that gets the already translated string. If the latter was true, you wouldn't have a ui.retranslateUi, but the QLabel would retranslate it's contents.


Log in to reply