From 68a5837a65706c6208131eee384e237762b3f833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 28 Jan 2022 12:11:04 -0500 Subject: [PATCH] settings: add a settings to change the language of the app Move installTranslator into the settings manager and add a settings in the SystemSettings to be able to dynamically change the language of jami-qt. https://git.jami.net/savoirfairelinux/jami-project/-/issues/1342 Change-Id: I4f720fa50d5e313356dbdf1b8acb4e98d74401e4 --- src/appsettingsmanager.cpp | 75 +++++++++++++++++++ src/appsettingsmanager.h | 11 ++- src/constant/JamiStrings.qml | 1 + src/mainapplication.cpp | 58 ++------------ src/mainapplication.h | 4 +- .../components/SystemSettings.qml | 29 +++++++ src/utilsadapter.cpp | 39 +++++++++- src/utilsadapter.h | 1 + 8 files changed, 164 insertions(+), 54 deletions(-) diff --git a/src/appsettingsmanager.cpp b/src/appsettingsmanager.cpp index 614a027c8..b115dd3e1 100644 --- a/src/appsettingsmanager.cpp +++ b/src/appsettingsmanager.cpp @@ -20,6 +20,11 @@ #include "appsettingsmanager.h" +#include <QCoreApplication> +#include <QLibraryInfo> + +#include <locale.h> + const QString defaultDownloadPath = QStandardPaths::writableLocation( QStandardPaths::DownloadLocation); @@ -51,3 +56,73 @@ AppSettingsManager::setValue(const Settings::Key key, const QVariant& value) { settings_->setValue(Settings::toString(key), value); } + +void +AppSettingsManager::loadTranslations() +{ +#if defined(Q_OS_LINUX) && defined(JAMI_INSTALL_PREFIX) + QString appDir = JAMI_INSTALL_PREFIX; +#else + QString appDir = qApp->applicationDirPath() + QDir::separator() + "share"; +#endif + + // Remove previously installed translators + for (auto* tr : installedTr_) + qApp->removeTranslator(tr); + installedTr_.clear(); + + auto pref = getValue(Settings::Key::LANG).toString(); + + QString locale_name = pref == "SYSTEM" ? QLocale::system().name() : pref; + qDebug() << QString("Using locale: %1").arg(locale_name); + QString locale_lang = locale_name.split('_')[0]; + + QTranslator* qtTranslator_lang = new QTranslator(qApp); + QTranslator* qtTranslator_name = new QTranslator(qApp); + if (locale_name != locale_lang) { + if (qtTranslator_lang->load("qt_" + locale_lang, + QLibraryInfo::path(QLibraryInfo::TranslationsPath))) + qApp->installTranslator(qtTranslator_lang); + installedTr_.append(qtTranslator_lang); + } + + if (qtTranslator_name->load("qt_" + locale_name, + QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { + qApp->installTranslator(qtTranslator_name); + installedTr_.append(qtTranslator_name); + } + + QTranslator* lrcTranslator_lang = new QTranslator(qApp); + QTranslator* lrcTranslator_name = new QTranslator(qApp); + if (locale_name != locale_lang) { + if (lrcTranslator_lang->load(appDir + QDir::separator() + "libringclient" + QDir::separator() + + "translations" + QDir::separator() + "lrc_" + locale_lang)) { + qApp->installTranslator(lrcTranslator_lang); + installedTr_.append(lrcTranslator_lang); + } + } + if (lrcTranslator_name->load(appDir + QDir::separator() + "libringclient" + QDir::separator() + + "translations" + QDir::separator() + "lrc_" + locale_name)) { + qApp->installTranslator(lrcTranslator_name); + installedTr_.append(lrcTranslator_name); + } + + QTranslator* mainTranslator_lang = new QTranslator(qApp); + QTranslator* mainTranslator_name = new QTranslator(qApp); + if (locale_name != locale_lang) { + if (mainTranslator_lang->load(appDir + QDir::separator() + "ring" + QDir::separator() + + "translations" + QDir::separator() + "ring_client_windows_" + + locale_lang)) { + qApp->installTranslator(mainTranslator_lang); + installedTr_.append(mainTranslator_lang); + } + } + if (mainTranslator_name->load(appDir + QDir::separator() + "ring" + QDir::separator() + + "translations" + QDir::separator() + "ring_client_windows_" + + locale_name)) { + qApp->installTranslator(mainTranslator_name); + installedTr_.append(mainTranslator_name); + } + + Q_EMIT retranslate(); +} \ No newline at end of file diff --git a/src/appsettingsmanager.h b/src/appsettingsmanager.h index f316d9b74..0ea1c6bba 100644 --- a/src/appsettingsmanager.h +++ b/src/appsettingsmanager.h @@ -28,6 +28,8 @@ #include <QStandardPaths> #include <QWindow> // for QWindow::AutomaticVisibility +#include <QTranslator> + extern const QString defaultDownloadPath; // clang-format off @@ -47,7 +49,8 @@ extern const QString defaultDownloadPath; X(ShowChatviewHorizontally, true) \ X(NeverShowMeAgain, false) \ X(WindowGeometry, QRectF(qQNaN(), qQNaN(), 0., 0.)) \ - X(WindowState, QWindow::AutomaticVisibility) + X(WindowState, QWindow::AutomaticVisibility) \ + X(LANG, "SYSTEM") /* * A class to expose settings keys in both c++ and QML. @@ -102,6 +105,12 @@ public: Q_INVOKABLE QVariant getValue(const Settings::Key key); Q_INVOKABLE void setValue(const Settings::Key key, const QVariant& value); + void loadTranslations(); + +Q_SIGNALS: + void retranslate(); + private: QSettings* settings_; + QVector<QTranslator*> installedTr_ {}; }; diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml index 2262e59a3..8080aca88 100644 --- a/src/constant/JamiStrings.qml +++ b/src/constant/JamiStrings.qml @@ -376,6 +376,7 @@ Item { property string enableTypingIndicator: qsTr("Enable typing indicators") property string displayHyperlinkPreviews: qsTr("Display hyperlink previews in the chatview") property string chatviewPositionInCall: qsTr("Chatview's position in calls") + property string language: qsTr("User interface language") property string bottomOpt: qsTr("Bottom") property string rightOpt: qsTr("Right") diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp index d515cd8fc..099781954 100644 --- a/src/mainapplication.cpp +++ b/src/mainapplication.cpp @@ -148,6 +148,10 @@ MainApplication::MainApplication(int& argc, char** argv) : QApplication(argc, argv) { QObject::connect(this, &QApplication::aboutToQuit, [this] { cleanup(); }); + QObject::connect(settingsManager_.get(), + &AppSettingsManager::retranslate, + this, + &MainApplication::retranslate); } MainApplication::~MainApplication() @@ -181,7 +185,7 @@ MainApplication::init() } Utils::removeOldVersions(); - loadTranslations(); + settingsManager_->loadTranslations(); setApplicationFont(); #if defined _MSC_VER @@ -262,57 +266,9 @@ MainApplication::restoreApp() } void -MainApplication::loadTranslations() +MainApplication::retranslate() { -#if defined(Q_OS_LINUX) && defined(JAMI_INSTALL_PREFIX) - QString appDir = JAMI_INSTALL_PREFIX; -#else - QString appDir = qApp->applicationDirPath() + QDir::separator() + "share"; -#endif - - QString locale_name = QLocale::system().name(); - QString locale_lang = locale_name.split('_')[0]; - - QTranslator* qtTranslator_lang = new QTranslator(this); - QTranslator* qtTranslator_name = new QTranslator(this); - if (locale_name != locale_lang) { - if (qtTranslator_lang->load("qt_" + locale_lang, - QLibraryInfo::path(QLibraryInfo::TranslationsPath))) - installTranslator(qtTranslator_lang); - } - - if (qtTranslator_name->load("qt_" + locale_name, - QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { - installTranslator(qtTranslator_name); - } - - QTranslator* lrcTranslator_lang = new QTranslator(this); - QTranslator* lrcTranslator_name = new QTranslator(this); - if (locale_name != locale_lang) { - if (lrcTranslator_lang->load(appDir + QDir::separator() + "libringclient" + QDir::separator() - + "translations" + QDir::separator() + "lrc_" + locale_lang)) { - installTranslator(lrcTranslator_lang); - } - } - if (lrcTranslator_name->load(appDir + QDir::separator() + "libringclient" + QDir::separator() - + "translations" + QDir::separator() + "lrc_" + locale_name)) { - installTranslator(lrcTranslator_name); - } - - QTranslator* mainTranslator_lang = new QTranslator(this); - QTranslator* mainTranslator_name = new QTranslator(this); - if (locale_name != locale_lang) { - if (mainTranslator_lang->load(appDir + QDir::separator() + "ring" + QDir::separator() - + "translations" + QDir::separator() + "ring_client_windows_" - + locale_lang)) { - installTranslator(mainTranslator_lang); - } - } - if (mainTranslator_name->load(appDir + QDir::separator() + "ring" + QDir::separator() - + "translations" + QDir::separator() + "ring_client_windows_" - + locale_name)) { - installTranslator(mainTranslator_name); - } + engine_->retranslate(); } void diff --git a/src/mainapplication.h b/src/mainapplication.h index 53a1ceb07..62eb26f1d 100644 --- a/src/mainapplication.h +++ b/src/mainapplication.h @@ -72,7 +72,6 @@ private: void vsConsoleDebug(); void fileDebug(QFile* debugFile); - void loadTranslations(); void initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon); const QVariantMap parseArguments(); void setApplicationFont(); @@ -80,6 +79,9 @@ private: void initSystray(); void cleanup(); +public Q_SLOTS: + void retranslate(); + private: QScopedPointer<QFile> debugFile_; QScopedPointer<QQmlApplicationEngine> engine_; diff --git a/src/settingsview/components/SystemSettings.qml b/src/settingsview/components/SystemSettings.qml index 952d24fdd..1e48934ff 100644 --- a/src/settingsview/components/SystemSettings.qml +++ b/src/settingsview/components/SystemSettings.qml @@ -160,4 +160,33 @@ ColumnLayout { onClicked: downloadPathDialog.open() } } + + SettingsComboBox { + id: langComboBoxSetting + + Layout.fillWidth: true + Layout.preferredHeight: JamiTheme.preferredFieldHeight + Layout.leftMargin: JamiTheme.preferredMarginSize + + labelText: JamiStrings.language + fontPointSize: JamiTheme.settingsFontSize + comboModel: ListModel { + Component.onCompleted: { + var supported = UtilsAdapter.supportedLang(); + var keys = Object.keys(supported); + var currentKey = UtilsAdapter.getAppValue(Settings.Key.LANG); + for (var i = 0 ; i < keys.length ; ++i) { + append({ textDisplay: supported[keys[i]], id: keys[i] }) + if (keys[i] == currentKey) + langComboBoxSetting.modelIndex = i + } + } + } + widthOfComboBox: itemWidth + role: "textDisplay" + + onActivated: { + UtilsAdapter.setAppValue(Settings.Key.LANG, comboModel.get(modelIndex).id) + } + } } diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp index 46614a23b..274a90cc4 100644 --- a/src/utilsadapter.cpp +++ b/src/utilsadapter.cpp @@ -33,6 +33,7 @@ #include <QApplication> #include <QClipboard> #include <QFileInfo> +#include <QRegExp> UtilsAdapter::UtilsAdapter(AppSettingsManager* settingsManager, SystemTray* systemTray, @@ -338,6 +339,9 @@ void UtilsAdapter::setAppValue(const Settings::Key key, const QVariant& value) { settingsManager_->setValue(key, value); + // If we change the lang preference, reload the translations + if (key == Settings::Key::LANG) + settingsManager_->loadTranslations(); } QString @@ -411,6 +415,39 @@ UtilsAdapter::clearInteractionsCache(const QString& accountId, const QString& co auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); auto& convModel = accInfo.conversationModel; convModel->clearInteractionsCache(convId); - } catch (...) {} + } catch (...) { + } + } +} + +QVariantMap +UtilsAdapter::supportedLang() +{ +#if defined(Q_OS_LINUX) && defined(JAMI_INSTALL_PREFIX) + QString appDir = JAMI_INSTALL_PREFIX; +#else + QString appDir = qApp->applicationDirPath() + QDir::separator() + "share"; +#endif + auto trDir = QDir(appDir + QDir::separator() + "ring" + QDir::separator() + "translations"); + QStringList trFiles = trDir.entryList(QStringList() << "ring_client_windows_*.qm", QDir::Files); + QVariantMap result; + result["SYSTEM"] = tr("System"); + // Get available locales + QRegExp regex("ring_client_windows_(.*).qm"); + QSet<QString> nativeNames; + for (const auto& f : trFiles) { + auto match = regex.indexIn(f); + if (regex.capturedTexts().size() == 2) { + const auto& l = regex.capturedTexts()[1]; + auto nativeName = QLocale(l).nativeLanguageName(); + if (nativeName.isEmpty()) // If a locale doesn't have any nativeLanguageName, ignore it. + continue; + // Avoid to show potential duplicates. + if (!nativeNames.contains(nativeName)) { + result[l] = nativeName; + nativeNames.insert(nativeName); + } + } } + return result; } diff --git a/src/utilsadapter.h b/src/utilsadapter.h index 2b5f60d4d..a1a5eb1d8 100644 --- a/src/utilsadapter.h +++ b/src/utilsadapter.h @@ -90,6 +90,7 @@ public: Q_INVOKABLE void setDownloadPath(QString dir); Q_INVOKABLE void monitor(const bool& continuous); Q_INVOKABLE void clearInteractionsCache(const QString& accountId, const QString& convUid); + Q_INVOKABLE QVariantMap supportedLang(); Q_SIGNALS: void debugMessageReceived(const QString& message); -- GitLab