Dynamic language loading.
-
wrote on 6 Oct 2024, 12:16 last edited by
Hello everyone,
I've managed to translate my application, which was written in French by default, into English.
The following code in 'main.cpp' allows the English language to be loaded. Everything works perfectly at this stage :
int main(int argc, char *argv[]) { QApplication Proced4XP(argc, argv); Proced4XP.setStyle("macos"); // Loading default language QTranslator translator; QString AppPath = Proced4XP.applicationDirPath()+"/"; if (translator.load(AppPath + "Procedures4XP_en_GB.qm")) { Proced4XP.installTranslator(&translator); } // End loading
However, I'd like to make this loading dynamic, so I've created a specific menu:
// Définition des menus barreMenus = new QMenu; // Menu et submenu pour le choix de la langue barreMenus = menuBar()->addMenu(tr("Préférences")); // Création submenu 'menulang' du menu 'barreMenus' (je crois) QMenu *menuLang = barreMenus->addMenu(tr("Langue")); QAction *menuFrench = new QAction("Français"); menuLang->addAction(menuFrench); QAction *menuEnglish = new QAction("English"); menuLang->addAction(menuEnglish); // Fin de new code
The associated methods are as follows:
// Méthodes gérant la sélection de la langue de l'application void AirportWindow::onMenuFrench_clicked() { QTranslator translator; Proced4XP.removeTranslator(&translator); ui->retranslateUi(this); qDebug() << "Appel de 'onMenuFrench' OK" ; } void AirportWindow::onMenuEnglish_clicked() { QTranslator translator; QString AppPath = Proced4XP.applicationDirPath()+"/"; if (translator.load(AppPath + "Procedures4XP_en_GB.qm")) { Proced4XP.installTranslator(&translator); } ui->retranslateUi(this); qDebug() << "Appel de 'onMenuEnglish' OK" ; }
The menus and methods are correctly connected but the methods seem to have no effect.
What's wrong?Thanks for your help
-
Hello everyone,
I've managed to translate my application, which was written in French by default, into English.
The following code in 'main.cpp' allows the English language to be loaded. Everything works perfectly at this stage :
int main(int argc, char *argv[]) { QApplication Proced4XP(argc, argv); Proced4XP.setStyle("macos"); // Loading default language QTranslator translator; QString AppPath = Proced4XP.applicationDirPath()+"/"; if (translator.load(AppPath + "Procedures4XP_en_GB.qm")) { Proced4XP.installTranslator(&translator); } // End loading
However, I'd like to make this loading dynamic, so I've created a specific menu:
// Définition des menus barreMenus = new QMenu; // Menu et submenu pour le choix de la langue barreMenus = menuBar()->addMenu(tr("Préférences")); // Création submenu 'menulang' du menu 'barreMenus' (je crois) QMenu *menuLang = barreMenus->addMenu(tr("Langue")); QAction *menuFrench = new QAction("Français"); menuLang->addAction(menuFrench); QAction *menuEnglish = new QAction("English"); menuLang->addAction(menuEnglish); // Fin de new code
The associated methods are as follows:
// Méthodes gérant la sélection de la langue de l'application void AirportWindow::onMenuFrench_clicked() { QTranslator translator; Proced4XP.removeTranslator(&translator); ui->retranslateUi(this); qDebug() << "Appel de 'onMenuFrench' OK" ; } void AirportWindow::onMenuEnglish_clicked() { QTranslator translator; QString AppPath = Proced4XP.applicationDirPath()+"/"; if (translator.load(AppPath + "Procedures4XP_en_GB.qm")) { Proced4XP.installTranslator(&translator); } ui->retranslateUi(this); qDebug() << "Appel de 'onMenuEnglish' OK" ; }
The menus and methods are correctly connected but the methods seem to have no effect.
What's wrong?Thanks for your help
@MortyMars said in Dynamic language loading.:
QTranslator translator;
C++ basics - what's the lifetime of this object?
Also don't forget to remove the translator for the previous language.
-
@MortyMars said in Dynamic language loading.:
QTranslator translator;
C++ basics - what's the lifetime of this object?
Also don't forget to remove the translator for the previous language.
wrote on 6 Oct 2024, 20:05 last edited byThanks @Christian-Ehrlicher for your reply,
The ‘translator’ variable only has a limited scope in each of the methods in which it appears, which doesn't guarantee that I'm calling the right instance in different places in the code...
Should I declare ‘translator’ as a global variable??As far as the preloaded ‘translator’ is concerned, I'm thinking of deactivating it using the removeTranslator command :
void AirportWindow::onMenuFrench_clicked() { QTranslator translator; Proced4XP.removeTranslator(&translator);
-
Thanks @Christian-Ehrlicher for your reply,
The ‘translator’ variable only has a limited scope in each of the methods in which it appears, which doesn't guarantee that I'm calling the right instance in different places in the code...
Should I declare ‘translator’ as a global variable??As far as the preloaded ‘translator’ is concerned, I'm thinking of deactivating it using the removeTranslator command :
void AirportWindow::onMenuFrench_clicked() { QTranslator translator; Proced4XP.removeTranslator(&translator);
@MortyMars said in Dynamic language loading.:
The ‘translator’ variable only has a limited scope
So fix it by e.g. creating the translator on the heap. Or make it a member of your class or whatever.
-
@MortyMars said in Dynamic language loading.:
The ‘translator’ variable only has a limited scope
So fix it by e.g. creating the translator on the heap. Or make it a member of your class or whatever.
wrote on 7 Oct 2024, 01:40 last edited by@Christian-Ehrlicher
Thank you !After many attempts, I've ended up creating a global variable ‘Translator’ initialised on the heap.
I have a residual problem that I can't explain.
The code for loading a translation file in ‘main.cpp’ allows the whole application to be translated.
But the same code placed in a method connected to the menus only translates the forms but not the menus (the top bar of the menus)... -
You have to listen to QEvent::LanguageChanged and then re-set your texts accordingly as properly written in the docs: https://doc.qt.io/qt-6/i18n-source-translation.html#prepare-for-dynamic-language-changes
-
You have to listen to QEvent::LanguageChanged and then re-set your texts accordingly as properly written in the docs: https://doc.qt.io/qt-6/i18n-source-translation.html#prepare-for-dynamic-language-changes
wrote on 7 Oct 2024, 11:23 last edited by@Christian-Ehrlicher
Thank you for this new idea.I understand, perhaps incorrectly, that monitoring the ‘LanguageChange’ event allows us to implement an automatic change of the application language via the code ‘ui.retranslateUi(this);’
But knowing that this is already the method I'm calling via the menus :
void AirportWindow::onMenuEnglish_clicked() { if (Translator->load("Procedures4XP_en_GB.qm")) { QApplication::installTranslator(Translator); } ui->retranslateUi(this); qDebug() << "Appel de 'onMenuEnglish' OK" ; }
...I imagine that my menus will be ignored here too...
PS: when I mentioned dynamic language loading, I didn't necessarily mean an automatic change of language, but an update of this language via a menu used while the application is running.
-
Hi,
Beside your Ui done with Designer, you you have any place in your code which use the
tr
function ?
If so, then you need to also re-trigger them.
One simple solution is to have one function that contain all these calls so you can call it from your event handler as well as your class constructor. -
Hi,
Beside your Ui done with Designer, you you have any place in your code which use the
tr
function ?
If so, then you need to also re-trigger them.
One simple solution is to have one function that contain all these calls so you can call it from your event handler as well as your class constructor.wrote on 7 Oct 2024, 22:40 last edited byHi @SGaist
Thanks for your help.
No, apart from my interface (admittedly, with multiple QMainWindows), I don't have any other parts of code that use the tr() function.
Unless the menus are considered separate?What exactly do you mean by ‘re-trigger them’?
-
wrote on 8 Oct 2024, 07:15 last edited by
With the little code you have shown, we can see e.g.
menuBar()->addMenu(tr("Préférences"));
. The text for this menu entry is specified when the menu is created. There needs to be a place likeretranslateMenu()
that calls setTitle()/setText() on together withtr()
on this specific menu entry (and also on all the QActions that you add there). This means, you need to store pointers to every single menu entry to change the text when the translation is changed. -
With the little code you have shown, we can see e.g.
menuBar()->addMenu(tr("Préférences"));
. The text for this menu entry is specified when the menu is created. There needs to be a place likeretranslateMenu()
that calls setTitle()/setText() on together withtr()
on this specific menu entry (and also on all the QActions that you add there). This means, you need to store pointers to every single menu entry to change the text when the translation is changed.wrote on 8 Oct 2024, 23:21 last edited byThank you @SimonSchroeder for your help.
I have to admit that I'm not quite sure what I should do.
An example of code would be welcome... -
wrote on 9 Oct 2024, 07:41 last edited by
@MortyMars said in Dynamic language loading.:
An example of code would be welcome...
class AirportWindow { QMenu *menuPreferences= nullptr; QMenu *menuLang = nullptr; QAction *menuFrench = nullptr; QAction *menuEnglish = nullptr; [...] public slots: void createMenus() { menuPreferences = menuBar()->addMenu(tr("Préférences")); menuLang = menuPreferences->addMenu(tr("Langue")); menuFrench = new QAction("Français"); menuLang->addAction(menuFrench); menuEnglish = new QAction("English"); menuLang->addAction(menuEnglish); } void retranslateMenus() { menuPreferences->setTitle(tr("Préférences")); menuLang->setTitle(tr("Langue")); // in case you actually had actions to translate as well // menuFrench->setText(tr("Français")); // menuEnglish->setText(tr("English")); } };
Right next to your call
ui->retranslateUi(this);
you would add a call toretranslateMenus();
.One question though: You did not actually change the .cpp file generated from a .ui-file? This is not safe because at any point the .cpp file could be regenerated and all your changes would be lost. If you really would want to go down that route, you'd have to edit the
retranslateUi()
function as well (further down inside the same file). -
@MortyMars said in Dynamic language loading.:
An example of code would be welcome...
class AirportWindow { QMenu *menuPreferences= nullptr; QMenu *menuLang = nullptr; QAction *menuFrench = nullptr; QAction *menuEnglish = nullptr; [...] public slots: void createMenus() { menuPreferences = menuBar()->addMenu(tr("Préférences")); menuLang = menuPreferences->addMenu(tr("Langue")); menuFrench = new QAction("Français"); menuLang->addAction(menuFrench); menuEnglish = new QAction("English"); menuLang->addAction(menuEnglish); } void retranslateMenus() { menuPreferences->setTitle(tr("Préférences")); menuLang->setTitle(tr("Langue")); // in case you actually had actions to translate as well // menuFrench->setText(tr("Français")); // menuEnglish->setText(tr("English")); } };
Right next to your call
ui->retranslateUi(this);
you would add a call toretranslateMenus();
.One question though: You did not actually change the .cpp file generated from a .ui-file? This is not safe because at any point the .cpp file could be regenerated and all your changes would be lost. If you really would want to go down that route, you'd have to edit the
retranslateUi()
function as well (further down inside the same file).wrote on 10 Oct 2024, 00:04 last edited byThanks for your efficient help and the example code that I've managed to transpose into my application :
// Méthode pour forcer la traduction des menus void AirportWindow::retranslateMenus() { menuPrefs->setTitle(tr("Préférences")); menuLang->setTitle(tr("Langue")); actionFrench->setText(tr("Français")); actionEnglish->setText(tr("English")); actionAirport->setText(tr("On y est !")); actionFixes->setText(tr("Basculer sur Fixes")); actionNavaids->setText(tr("Basculer sur Navaids")); actionSidStars->setText(tr("Basculer sur SIDs /STARs")); actionApproches->setText(tr("Basculer sur APPCHs")); menuAide->setTitle(tr("Aide")); actionAboutQt->setText(tr("À propos de Qt")); actionAboutApp->setText(tr("À propos de Procedures4XP")); actionAvert->setText(tr("Avertissement")); }
I've still got a few tweaks to make, but overall it's working the way I want it to.
However, I don't know what you mean by :
'You didn't actually change the .cpp file generated from a .ui-file?'Anyway, thank you very much for this quick fix.
1/13