diff --git a/linphone-desktop/assets/languages/en.ts b/linphone-desktop/assets/languages/en.ts index 361c3c0e1..71311234a 100644 --- a/linphone-desktop/assets/languages/en.ts +++ b/linphone-desktop/assets/languages/en.ts @@ -859,6 +859,10 @@ Server url not configured. languagesLabel Language + + systemLocale + System locale + SettingsWindow diff --git a/linphone-desktop/assets/languages/fr.ts b/linphone-desktop/assets/languages/fr.ts index 90e117183..810e2f75c 100644 --- a/linphone-desktop/assets/languages/fr.ts +++ b/linphone-desktop/assets/languages/fr.ts @@ -869,6 +869,10 @@ Url du serveur non configurée. languagesLabel Langue + + systemLocale + Locale du système + SettingsWindow diff --git a/linphone-desktop/src/app/App.cpp b/linphone-desktop/src/app/App.cpp index 7775f2e00..9319b3ca7 100644 --- a/linphone-desktop/src/app/App.cpp +++ b/linphone-desktop/src/app/App.cpp @@ -29,18 +29,20 @@ #include "../components/settings/SettingsModel.hpp" #include "../components/smart-search-bar/SmartSearchBarModel.hpp" #include "../components/timeline/TimelineModel.hpp" +#include "../utils.hpp" #include "App.hpp" +#include "DefaultTranslator.hpp" #include "Logger.hpp" +#include #include #include -#include -#include -#include #include #include +#define DEFAULT_LOCALE "en" + #define LANGUAGES_PATH ":/languages/" #define WINDOW_ICON_PATH ":/assets/images/linphone_logo.svg" @@ -53,45 +55,32 @@ App *App::m_instance = nullptr; +inline bool installLocale (App &app, QTranslator &translator, const QLocale &locale) { + return translator.load(locale, LANGUAGES_PATH) && app.installTranslator(&translator); +} + App::App (int &argc, char **argv) : QApplication(argc, argv) { - this->setApplicationVersion("4.0"); + setApplicationVersion("4.0"); - if (m_english_translator.load(QLocale(QLocale::English), LANGUAGES_PATH)) - installTranslator(&m_english_translator); - else - qWarning("Unable to install english translator."); + // List available locales. + for (const auto &locale : QDir(LANGUAGES_PATH).entryList()) + m_available_locales << QLocale(locale); - // Try to use default locale. - QLocale current_locale = QLocale::system(); + m_translator = new DefaultTranslator(this); - if (m_default_translator.load(current_locale, LANGUAGES_PATH)) { - installTranslator(&m_default_translator); - m_locale = current_locale.name(); - } else { - qWarning() << QStringLiteral("Unable to found translations for locale: %1.") - .arg(current_locale.name()); + // Try to use system locale. + QLocale sys_locale = QLocale::system(); + if (installLocale(*this, *m_translator, sys_locale)) { + m_locale = sys_locale.name(); + qInfo() << QStringLiteral("Use system locale: %1").arg(m_locale); + return; } -} -// ----------------------------------------------------------------------------- - -QQuickWindow *App::getCallsWindow () const { - return m_calls_window; -} - -QQuickWindow *App::getMainWindow () const { - QQmlApplicationEngine &engine = const_cast(m_engine); - return qobject_cast(engine.rootObjects().at(0)); -} - -QQuickWindow *App::getSettingsWindow () const { - return m_settings_window; -} - -// ----------------------------------------------------------------------------- - -bool App::hasFocus () const { - return getMainWindow()->isActive() || m_calls_window->isActive(); + // Use english. + m_locale = DEFAULT_LOCALE; + if (!installLocale(*this, *m_translator, QLocale(m_locale))) + qFatal("Unable to install default translator."); + qInfo() << QStringLiteral("Use default locale: %1").arg(m_locale); } // ----------------------------------------------------------------------------- @@ -118,9 +107,30 @@ void App::initContentApp () { // Init core. CoreManager::init(m_parser.value("config")); qInfo() << "Core manager initialized."; - qInfo() << "Activated selectors:" << QQmlFileSelector::get(&m_engine)->selector()->allSelectors(); + // Try to use preferred locale. + { + QString locale = getConfigLocale(); + + if (!locale.isEmpty()) { + DefaultTranslator *translator = new DefaultTranslator(this); + + if (installLocale(*this, *translator, QLocale(locale))) { + // Use config. + m_translator->deleteLater(); + m_translator = translator; + m_locale = locale; + + qInfo() << QStringLiteral("Use preferred locale: %1").arg(locale); + } else { + // Reset config. + setConfigLocale(""); + translator->deleteLater(); + } + } + } + // Register types ans make sub windows. registerTypes(); createSubWindows(); @@ -177,6 +187,27 @@ void App::parseArgs () { // ----------------------------------------------------------------------------- +QQuickWindow *App::getCallsWindow () const { + return m_calls_window; +} + +QQuickWindow *App::getMainWindow () const { + QQmlApplicationEngine &engine = const_cast(m_engine); + return qobject_cast(engine.rootObjects().at(0)); +} + +QQuickWindow *App::getSettingsWindow () const { + return m_settings_window; +} + +// ----------------------------------------------------------------------------- + +bool App::hasFocus () const { + return getMainWindow()->isActive() || m_calls_window->isActive(); +} + +// ----------------------------------------------------------------------------- + void App::registerTypes () { qInfo() << "Registering types..."; @@ -323,6 +354,28 @@ void App::setTrayIcon () { // ----------------------------------------------------------------------------- +QString App::getConfigLocale () const { + return ::Utils::linphoneStringToQString( + CoreManager::getInstance()->getCore()->getConfig()->getString( + SettingsModel::UI_SECTION, "locale", "" + ) + ); +} + +void App::setConfigLocale (const QString &locale) { + CoreManager::getInstance()->getCore()->getConfig()->setString( + SettingsModel::UI_SECTION, "locale", ::Utils::qStringToLinphoneString(locale) + ); + + emit configLocaleChanged(locale); +} + +QString App::getLocale () const { + return m_locale; +} + +// ----------------------------------------------------------------------------- + void App::quit () { if (m_parser.isSet("selftest")) { cout << tr("selftestResult").toStdString() << endl; diff --git a/linphone-desktop/src/app/App.hpp b/linphone-desktop/src/app/App.hpp index 14cfe8522..92ee4ee83 100644 --- a/linphone-desktop/src/app/App.hpp +++ b/linphone-desktop/src/app/App.hpp @@ -25,7 +25,6 @@ #include "../components/notifier/Notifier.hpp" #include "AvatarProvider.hpp" -#include "DefaultTranslator.hpp" #include "ThumbnailProvider.hpp" #include @@ -37,10 +36,19 @@ // ============================================================================= +class DefaultTranslator; + class App : public QApplication { Q_OBJECT; + Q_PROPERTY(QString configLocale READ getConfigLocale WRITE setConfigLocale NOTIFY configLocaleChanged); + Q_PROPERTY(QString locale READ getLocale CONSTANT); + Q_PROPERTY(QVariantList availableLocales READ getAvailableLocales CONSTANT); + public: + void initContentApp (); + void parseArgs (); + QQmlEngine *getEngine () { return &m_engine; } @@ -54,16 +62,8 @@ public: bool hasFocus () const; - void initContentApp (); - Q_INVOKABLE QQuickWindow *getSettingsWindow () const; - Q_INVOKABLE QString locale () const { - return m_locale; - } - - void parseArgs (); - static void create (int &argc, char **argv) { if (!m_instance) { // Instance must be exists before content. @@ -78,6 +78,9 @@ public: public slots: void quit (); +signals: + void configLocaleChanged (const QString &locale); + private: App (int &argc, char **argv); ~App () = default; @@ -86,6 +89,15 @@ private: void createSubWindows (); void setTrayIcon (); + QString getConfigLocale () const; + void setConfigLocale (const QString &locale); + + QString getLocale () const; + + QVariantList getAvailableLocales () const { + return m_available_locales; + } + QCommandLineParser m_parser; QQmlApplicationEngine m_engine; QQmlFileSelector *m_file_selector = nullptr; @@ -93,11 +105,13 @@ private: AvatarProvider m_avatar_provider; ThumbnailProvider m_thumbnail_provider; - DefaultTranslator m_default_translator; - QTranslator m_english_translator; + + DefaultTranslator *m_translator = nullptr; Notifier *m_notifier = nullptr; - QString m_locale = "en"; + + QVariantList m_available_locales; + QString m_locale; QQuickWindow *m_calls_window = nullptr; QQuickWindow *m_settings_window = nullptr; diff --git a/linphone-desktop/src/app/DefaultTranslator.cpp b/linphone-desktop/src/app/DefaultTranslator.cpp index f6604fe1e..df77ffa35 100644 --- a/linphone-desktop/src/app/DefaultTranslator.cpp +++ b/linphone-desktop/src/app/DefaultTranslator.cpp @@ -27,7 +27,7 @@ // ============================================================================= -DefaultTranslator::DefaultTranslator () { +DefaultTranslator::DefaultTranslator (QObject *parent) : QTranslator(parent) { QDirIterator it(":", QDirIterator::Subdirectories); while (it.hasNext()) { QFileInfo info(it.next()); diff --git a/linphone-desktop/src/app/DefaultTranslator.hpp b/linphone-desktop/src/app/DefaultTranslator.hpp index 7ee1387ed..ba239c01a 100644 --- a/linphone-desktop/src/app/DefaultTranslator.hpp +++ b/linphone-desktop/src/app/DefaultTranslator.hpp @@ -30,7 +30,7 @@ class DefaultTranslator : public QTranslator { public: - DefaultTranslator (); + DefaultTranslator (QObject *parent = Q_NULLPTR); ~DefaultTranslator () = default; QString translate ( diff --git a/linphone-desktop/src/components/settings/SettingsModel.hpp b/linphone-desktop/src/components/settings/SettingsModel.hpp index be95f538d..a13968171 100644 --- a/linphone-desktop/src/components/settings/SettingsModel.hpp +++ b/linphone-desktop/src/components/settings/SettingsModel.hpp @@ -37,6 +37,8 @@ class SettingsModel : public QObject { public: SettingsModel (QObject *parent = Q_NULLPTR); + static const std::string UI_SECTION; + signals: void autoAnswerStatusChanged (bool status); void fileTransferUrlChanged (const QString &url); @@ -49,8 +51,6 @@ private: void setFileTransferUrl (const QString &url); std::shared_ptr m_config; - - static const std::string UI_SECTION; }; #endif // SETTINGS_MODEL_H_ diff --git a/linphone-desktop/src/main.cpp b/linphone-desktop/src/main.cpp index 11175a1f2..2bfbce435 100644 --- a/linphone-desktop/src/main.cpp +++ b/linphone-desktop/src/main.cpp @@ -43,9 +43,11 @@ int main (int argc, char *argv[]) { */ App::create(argc, argv); - App::getInstance()->parseArgs(); - App::getInstance()->initContentApp(); + + App *app = App::getInstance(); + app->parseArgs(); + app->initContentApp(); // Run! - return App::getInstance()->exec(); + return app->exec(); } diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml index 438c68d94..0419ff70c 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml @@ -150,7 +150,7 @@ Rectangle { // Cast section to integer because Qt converts the // sectionDate in string!!! text: new Date(section).toLocaleDateString( - Qt.locale(App.locale()) + Qt.locale(App.locale) ) } } @@ -237,7 +237,7 @@ Rectangle { color: ChatStyle.entry.time.color font.pointSize: ChatStyle.entry.time.fontSize text: $chatEntry.timestamp.toLocaleString( - Qt.locale(App.locale()), + Qt.locale(App.locale), 'hh:mm' ) verticalAlignment: Text.AlignVCenter diff --git a/linphone-desktop/ui/modules/Linphone/Timeline.qml b/linphone-desktop/ui/modules/Linphone/Timeline.qml index c9c56f0ad..a312a201e 100644 --- a/linphone-desktop/ui/modules/Linphone/Timeline.qml +++ b/linphone-desktop/ui/modules/Linphone/Timeline.qml @@ -160,7 +160,7 @@ ColumnLayout { anchors.fill: parent sourceComponent: TooltipArea { text: $timelineEntry.timestamp.toLocaleString( - Qt.locale(App.locale()), + Qt.locale(App.locale), Locale.ShortFormat ) } diff --git a/linphone-desktop/ui/scripts/Utils/utils.js b/linphone-desktop/ui/scripts/Utils/utils.js index 3289056be..d79a05e60 100644 --- a/linphone-desktop/ui/scripts/Utils/utils.js +++ b/linphone-desktop/ui/scripts/Utils/utils.js @@ -230,7 +230,7 @@ function _indexFinder (array, cb, context) { var length = array.length for (var i = 0; i < length; i++) { - if (cb(array[index], index, array)) { + if (cb(array[i], i, array)) { return i } } @@ -274,6 +274,12 @@ function basename (str) { // ----------------------------------------------------------------------------- +function capitalizeFirstLetter (str) { + return str.charAt(0).toUpperCase() + str.slice(1) +} + +// ----------------------------------------------------------------------------- + function dirname (str) { var str2 = str var length = str2.length - 1 @@ -342,6 +348,15 @@ function find (obj, cb, context) { // ----------------------------------------------------------------------------- +function findIndex (array, cb, context) { + cb = _computeOptimizedCb(cb, context) + + var key = _indexFinder(array, cb, context) + return key != null && key !== -1 ? key : null +} + +// ----------------------------------------------------------------------------- + function formatElapsedTime (seconds) { seconds = parseInt(seconds, 10) diff --git a/linphone-desktop/ui/views/App/Settings/SettingsUi.qml b/linphone-desktop/ui/views/App/Settings/SettingsUi.qml index a32a9b16f..a34586b3b 100644 --- a/linphone-desktop/ui/views/App/Settings/SettingsUi.qml +++ b/linphone-desktop/ui/views/App/Settings/SettingsUi.qml @@ -1,6 +1,8 @@ import QtQuick 2.7 import Common 1.0 +import Linphone 1.0 +import Utils 1.0 import App.Styles 1.0 @@ -24,10 +26,49 @@ TabContainer { label: qsTr('languagesLabel') ComboBox { - model: ListModel { - ListElement { key: 'English'; value: 0 } - ListElement { key: 'Français'; value: 1 } + function _getAvailableLocales () { + var locales = [] + + App.availableLocales.forEach(function (locale) { + locales.push({ + key: Utils.capitalizeFirstLetter(locale.nativeLanguageName), + value: locale.name + }) + }) + + return locales.sort(function (a, b) { + return a > b + }) } + + model: ListModel {} + + Component.onCompleted: { + var locales = _getAvailableLocales() + + model.append({ + key: qsTr('systemLocale'), + value: '' + }) + locales.forEach(function (locale) { + model.append(locale) + }) + + var locale = App.configLocale + if (!locale.length) { + currentIndex = 0 + return + } + + var value = Qt.locale(locale).name + var index = Utils.findIndex(locales, function (locale) { + return locale.value === value + }) + + currentIndex = index != null ? index + 1 : 0 + } + + onActivated: App.configLocale = model.get(index).value } } }