Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Dynamic retranslation without reimplementing the changeEvent in every window/widget
Forum Updated to NodeBB v4.3 + New Features

Dynamic retranslation without reimplementing the changeEvent in every window/widget

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 4 Posters 1.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • VRoninV VRonin

    Macro time!

    #define MAKE_TRANSLATABLE(BaseClass) \
        protected: \
        void changeEvent(QEvent * event) override{ \
            if (event->type() == QEvent::LanguageChange) \
                ui->retranslateUi(this); \
            BaseClass::changeEvent(event); \
        }
    #define MAKE_TRANSLATABLE_F(BaseClass, TranslateMethod) \
        protected: \
        void changeEvent(QEvent * event) override{ \
            if (event->type() == QEvent::LanguageChange) \
                TranslateMethod(); \
            BaseClass::changeEvent(event); \
        }
    
    class TranslatableWidget : public QWidget{
        MAKE_TRANSLATABLE(QWidget)
    };
    
    class TranslatableCustomWidget : public QWidget{
        MAKE_TRANSLATABLE_F(QWidget,customRetranslateMethod)
    protected:
        void customRetranslateMethod();
    };
    
    C Offline
    C Offline
    Christian Woznik
    wrote on last edited by
    #7

    @VRonin I guess that is an option to not always write it. I am just not sure how I feel about it yet as I am normally trying to avoid macros. I feel it makes it hard to maintain the code but I guess in this instance it is a valid usecase.

    Thanks.

    1 Reply Last reply
    0
    • J.HilkJ J.Hilk

      @Christian-Woznik actually, if all your widgets are in a parent child relation, than calling ui->retranslateUi(this); on your top parent widget should recursively retranslate all children as well.

      exception are of cause all strings you have set inside your code "manually". Those have to be called again

      C Offline
      C Offline
      Christian Woznik
      wrote on last edited by Christian Woznik
      #8

      @J-Hilk are you sure? I have my mainWindow containing a stackedWidget. I just took a look inside the generated ui_mainwindow.h but the retranslateUI function does not call the translate function for the individual pages so how would it call it recursively? I am not even sure how it would be possible.

      The only thing the function does it call QCoreApplication::translate for all of the set texts.

          void retranslateUi(QMainWindow *MainWindow)
          {
              MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "abc1", nullptr));
              lbMachineType->setText(QCoreApplication::translate("MainWindow", "abc2", nullptr));
              lbManufacturer->setText(QCoreApplication::translate("MainWindow", "abc3", nullptr));
              lbManufacturerGraphic->setText(QString());
              pbLang->setText(QString());
              lbPageNR->setText("1000");
              pbLeft->setText(QString());
              pbRight->setText(QString());
              pbSettings->setText(QString());
          } // retranslateUi
      

      @JonB
      As you can see it needs to access all the ui elements individually to set the text. So it is unavoidable to have access to the ui instance of each widget.

      J.HilkJ 1 Reply Last reply
      0
      • C Christian Woznik

        @J-Hilk are you sure? I have my mainWindow containing a stackedWidget. I just took a look inside the generated ui_mainwindow.h but the retranslateUI function does not call the translate function for the individual pages so how would it call it recursively? I am not even sure how it would be possible.

        The only thing the function does it call QCoreApplication::translate for all of the set texts.

            void retranslateUi(QMainWindow *MainWindow)
            {
                MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "abc1", nullptr));
                lbMachineType->setText(QCoreApplication::translate("MainWindow", "abc2", nullptr));
                lbManufacturer->setText(QCoreApplication::translate("MainWindow", "abc3", nullptr));
                lbManufacturerGraphic->setText(QString());
                pbLang->setText(QString());
                lbPageNR->setText("1000");
                pbLeft->setText(QString());
                pbRight->setText(QString());
                pbSettings->setText(QString());
            } // retranslateUi
        

        @JonB
        As you can see it needs to access all the ui elements individually to set the text. So it is unavoidable to have access to the ui instance of each widget.

        J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by
        #9

        @Christian-Woznik said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

        are you sure?

        No, I was going of on memory and it seems I was wrong :(


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        1 Reply Last reply
        0
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #10

          The "base class method" also falls short if you are subclassing a different class e.g. QDialog vs QWidget

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          C 1 Reply Last reply
          0
          • VRoninV VRonin

            The "base class method" also falls short if you are subclassing a different class e.g. QDialog vs QWidget

            C Offline
            C Offline
            Christian Woznik
            wrote on last edited by
            #11

            @VRonin
            Yeah I agree. It would require subclassing for each type. So QMainWindow, QWidget and QDialog in my situation. This is one of the reasons why I do not like it. I guess in the end the macro alternative is the best way.

            1 Reply Last reply
            0
            • JonBJ JonB

              @Christian-Woznik said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

              ui->translate(*QWidget) in each of the widgets

              If you say so. What does ui->translate() do that actually requires the ui instance/object to do its work?

              In any case, if @VRonin says to do it with a macro I'm sure he's right! Though how that helps you not have to do it (and subclass) against every different type of widget you use I don't see....?

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #12

              @Christian-Woznik

              @JonB said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

              If you say so. What does ui->translate() do that actually requires the ui instance/object to do its work?

              ?

              I find it hard to believe/understand what you need to do which cannot be done by visiting each widget individually via QWidgetList QApplication::allWidgets(), without needing ui/private variables, like I said.

              C 1 Reply Last reply
              0
              • JonBJ JonB

                @Christian-Woznik

                @JonB said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

                If you say so. What does ui->translate() do that actually requires the ui instance/object to do its work?

                ?

                I find it hard to believe/understand what you need to do which cannot be done by visiting each widget individually via QWidgetList QApplication::allWidgets(), without needing ui/private variables, like I said.

                C Offline
                C Offline
                Christian Woznik
                wrote on last edited by
                #13

                @JonB Well then tell me how. The UI object is private inside the individual widget / window class. There is no public function by default that I could use to invoke the retranslate. I mean QT's documentation says you have to do it via the event.

                I am also not happy with that solution but using macros does the trick and avoids writing the same code dozents of times. If you know a better alternative I would gladly take it.

                1 Reply Last reply
                0
                • C Christian Woznik

                  @JonB Unfortunately that does not help me. I need to call ui->translate(*QWidget) in each of the widgets. And of course the UI is only defined as private inside the individual widgets / windows.
                  I would either have to set the UI elements to public, an idea I do not like at all, to then update it or I have to do the same with a virtual public function I would call during the iteration.

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #14

                  @Christian-Woznik said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

                  @JonB Unfortunately that does not help me. I need to call ui->translate(*QWidget) in each of the widgets

                  That's what you wrote. Turns out there is no such ui->translate(), and you meant ui->retranslateUi(). I am not a mind reader and didn't know that.

                  Now that I look at that implementation from a designer-generated ui_....h file I can see that it not only visits each widget --- which could be done via QApplication::allWidgets() instead without needing to access any ui private variable --- but it also holds the the original-language text for each widget which it calls QApplication::translate() on. It's not how I would have implemented it :) though I can see it saves space and is convenient if you're not doing any translating. But it stops us from doing a "global retranslate all widgets to new language", which seems a shame.

                  Given that I can now see why ui->retranslateUi() must actually be called again when language changes. And that must be done for each top-level/designed widget-type you have. And you have to define a separate class like TranslatableWidget for every one of them and use that instead of the class you would have had in Designer, such as via the macros and classes in the example above. Nasty :(

                  Only you/ @VRonin know whether this might be achieved via a template in C++ rather than a macro, if you prefer that.

                  If you want to avoid the need to subclass to use @VRonin's approach --- which is what really bothers me --- you could presumably have each of your top-level/designed UI widgets define a slot which calls its private ui->retranslateUi(). And then have a single application-wide capture of QEvent::LanguageChange which emits a signal that you connect to each of these slots when you call ui->setupUi() in each top-level/designed widget. That is probably what I would do, as I don't want to have to sub-class each one just to make it translatable. @VRonin is welcome to comment/criticise if I have said anything erroneous.

                  C VRoninV 2 Replies Last reply
                  0
                  • JonBJ JonB

                    @Christian-Woznik said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

                    @JonB Unfortunately that does not help me. I need to call ui->translate(*QWidget) in each of the widgets

                    That's what you wrote. Turns out there is no such ui->translate(), and you meant ui->retranslateUi(). I am not a mind reader and didn't know that.

                    Now that I look at that implementation from a designer-generated ui_....h file I can see that it not only visits each widget --- which could be done via QApplication::allWidgets() instead without needing to access any ui private variable --- but it also holds the the original-language text for each widget which it calls QApplication::translate() on. It's not how I would have implemented it :) though I can see it saves space and is convenient if you're not doing any translating. But it stops us from doing a "global retranslate all widgets to new language", which seems a shame.

                    Given that I can now see why ui->retranslateUi() must actually be called again when language changes. And that must be done for each top-level/designed widget-type you have. And you have to define a separate class like TranslatableWidget for every one of them and use that instead of the class you would have had in Designer, such as via the macros and classes in the example above. Nasty :(

                    Only you/ @VRonin know whether this might be achieved via a template in C++ rather than a macro, if you prefer that.

                    If you want to avoid the need to subclass to use @VRonin's approach --- which is what really bothers me --- you could presumably have each of your top-level/designed UI widgets define a slot which calls its private ui->retranslateUi(). And then have a single application-wide capture of QEvent::LanguageChange which emits a signal that you connect to each of these slots when you call ui->setupUi() in each top-level/designed widget. That is probably what I would do, as I don't want to have to sub-class each one just to make it translatable. @VRonin is welcome to comment/criticise if I have said anything erroneous.

                    C Offline
                    C Offline
                    Christian Woznik
                    wrote on last edited by
                    #15

                    @JonB Well sorry for the confusion. I just wrote it wrongly there. But in my original post its correct as it was actual testcode and it compiled.

                    As for the global capture. I do not see where the subclassing is bad? I anyways have to do it for each window or custom dialog I want to show. I mean that is exactly what QT is doing when you add a new window, it creates a subclass from the QMainWindow, QWidget or QDialog class. And there I can just add the macro, it works fine. It is just that the usage of macros at my workplace is not really welcome but

                    I also would not know how to do it your way without subclassing as well? Or am I missing something there? Even the basic mainWindow is a subclass of QMainWindow.

                    1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Christian-Woznik said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

                      @JonB Unfortunately that does not help me. I need to call ui->translate(*QWidget) in each of the widgets

                      That's what you wrote. Turns out there is no such ui->translate(), and you meant ui->retranslateUi(). I am not a mind reader and didn't know that.

                      Now that I look at that implementation from a designer-generated ui_....h file I can see that it not only visits each widget --- which could be done via QApplication::allWidgets() instead without needing to access any ui private variable --- but it also holds the the original-language text for each widget which it calls QApplication::translate() on. It's not how I would have implemented it :) though I can see it saves space and is convenient if you're not doing any translating. But it stops us from doing a "global retranslate all widgets to new language", which seems a shame.

                      Given that I can now see why ui->retranslateUi() must actually be called again when language changes. And that must be done for each top-level/designed widget-type you have. And you have to define a separate class like TranslatableWidget for every one of them and use that instead of the class you would have had in Designer, such as via the macros and classes in the example above. Nasty :(

                      Only you/ @VRonin know whether this might be achieved via a template in C++ rather than a macro, if you prefer that.

                      If you want to avoid the need to subclass to use @VRonin's approach --- which is what really bothers me --- you could presumably have each of your top-level/designed UI widgets define a slot which calls its private ui->retranslateUi(). And then have a single application-wide capture of QEvent::LanguageChange which emits a signal that you connect to each of these slots when you call ui->setupUi() in each top-level/designed widget. That is probably what I would do, as I don't want to have to sub-class each one just to make it translatable. @VRonin is welcome to comment/criticise if I have said anything erroneous.

                      VRoninV Offline
                      VRoninV Offline
                      VRonin
                      wrote on last edited by
                      #16

                      @JonB said in Dynamic retranslation without reimplementing the changeEvent in every window/widget:

                      Only you/ @VRonin know whether this might be achieved via a template in C++ rather than a macro, if you prefer that.

                      You can use templates:

                      template <class T>
                      class TranstatableW : public T{
                      static_assert(std::is_base_of<QWidget,T>::value,"Template argument must be a QWidget");
                      public:
                      using T::T;
                      protected:
                          void changeEvent(QEvent * event) override{
                              if (event->type() == QEvent::LanguageChange)
                                  retranslate();
                              BaseClass::changeEvent(event);
                          }
                      virtual void retranslate() = 0;
                      };
                      

                      now all you need is to replace class MyWidget : public QWidget with class MyWidget : public TranstatableW<QWidget> but you'll always have to provide an overload for retranslate so probably more code

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      1 Reply Last reply
                      2

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved