diff --git a/ricochet.pro b/ricochet.pro index 04109748..9d866d35 100644 --- a/ricochet.pro +++ b/ricochet.pro @@ -156,7 +156,8 @@ SOURCES += src/main.cpp \ src/utils/Settings.cpp \ src/utils/PendingOperation.cpp \ src/ui/AudioNotification.cpp \ - src/utils/AudioPlayer.cpp + src/utils/AudioPlayer.cpp \ + src/ui/LanguagesModel.cpp HEADERS += src/ui/MainWindow.h \ src/ui/ContactsModel.h \ @@ -187,7 +188,8 @@ HEADERS += src/ui/MainWindow.h \ src/utils/Settings.h \ src/utils/PendingOperation.h \ src/ui/AudioNotification.h \ - src/utils/AudioPlayer.h + src/utils/AudioPlayer.h \ + src/ui/LanguagesModel.h SOURCES += src/protocol/Channel.cpp \ src/protocol/ControlChannel.cpp \ diff --git a/src/main.cpp b/src/main.cpp index 460dc32b..ddc27d7f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,7 +59,6 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); a.setApplicationVersion(QLatin1String("1.1.0")); a.setOrganizationName(QStringLiteral("Ricochet")); - initTranslation(); #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) a.setWindowIcon(QIcon(QStringLiteral(":/icons/ricochet.svg"))); @@ -76,6 +75,8 @@ int main(int argc, char *argv[]) } QScopedPointer lockFile(lock); + initTranslation(); + /* Initialize OpenSSL's allocator */ CRYPTO_malloc_init(); @@ -310,6 +311,16 @@ static void initTranslation() qDebug() << "Forcing locale" << locale << "from environment" << locale.uiLanguages(); } + SettingsObject settings; + QString settingsLanguage(settings.read("ui.language").toString()); + + if (!settingsLanguage.isEmpty()) { + locale = settingsLanguage; + } else { + //write an empty string to get "System default" language selected automatically in preferences + settings.write(QStringLiteral("ui.language"), QString()); + } + ok = translator->load(locale, QStringLiteral("ricochet"), QStringLiteral("_"), appPath); if (!ok) ok = translator->load(locale, QStringLiteral("ricochet"), QStringLiteral("_"), resPath); diff --git a/src/ui/LanguagesModel.cpp b/src/ui/LanguagesModel.cpp new file mode 100644 index 00000000..c7fa2c95 --- /dev/null +++ b/src/ui/LanguagesModel.cpp @@ -0,0 +1,80 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the names of the copyright owners nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "LanguagesModel.h" +#include +#include +#include + +LanguagesModel::LanguagesModel(QObject* parent) + : QAbstractListModel(parent) +{ + // create the list of languages based on present translation files in ":/lang" folder + QDir languagesFolder(QStringLiteral(":/lang")); + languages.append(LanguageEntry(tr("System default"), QString())); + foreach (const QString& translationFile, languagesFolder.entryList()) { + QString localeID = translationFile; + localeID.remove(QLatin1String("ricochet_")).remove(QLatin1String(".qm")); + QString nativeName = QLocale(localeID).nativeLanguageName(); + languages.append(LanguageEntry(nativeName, localeID)); + } +} + +int LanguagesModel::rowCount(const QModelIndex &) const +{ + return languages.length(); +} + +QVariant LanguagesModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= 0 && index.row() < languages.length()) { + switch( role ) { + case NameRole: + return languages[index.row()].nativeName; + case LocaleIDRole: + return languages[index.row()].localeID; + default: + return QVariant(); + } + } else { + return QVariant(); + } +} + +QHash LanguagesModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "nativeName"; + roles[LocaleIDRole] = "localeID"; + return roles; +} + diff --git a/src/ui/LanguagesModel.h b/src/ui/LanguagesModel.h new file mode 100644 index 00000000..5e9a3f7a --- /dev/null +++ b/src/ui/LanguagesModel.h @@ -0,0 +1,65 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the names of the copyright owners nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LANGUAGESMODEL_H +#define LANGUAGESMODEL_H + +#include + +class LanguagesModel : public QAbstractListModel +{ +public: + enum { + NameRole = Qt::UserRole, + LocaleIDRole + }; + + LanguagesModel(QObject* parent = 0); + + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QHash roleNames() const; + +private: + struct LanguageEntry + { + QString nativeName; + QString localeID; + + LanguageEntry(const QString& name, const QString& localeID) + : nativeName(name), localeID(localeID) {} + }; + + QList languages; +}; + +#endif // LANGUAGESMODEL_H diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index e4f0a844..3105f067 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -45,6 +45,7 @@ #include "utils/Settings.h" #include "utils/PendingOperation.h" #include "AudioNotification.h" +#include "ui/LanguagesModel.h" #include #include #include @@ -82,6 +83,7 @@ MainWindow::MainWindow(QObject *parent) qmlRegisterType("im.ricochet", 1, 0, "ContactIDValidator"); qmlRegisterType("im.ricochet", 1, 0, "Settings"); qmlRegisterSingletonType("im.ricochet", 1, 0, "LinkedText", linkedtext_singleton); + qmlRegisterType("im.ricochet", 1, 0, "LanguagesModel"); qRegisterMetaType(); } diff --git a/src/ui/qml/LanguagePreferences.qml b/src/ui/qml/LanguagePreferences.qml new file mode 100644 index 00000000..000008ff --- /dev/null +++ b/src/ui/qml/LanguagePreferences.qml @@ -0,0 +1,62 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import im.ricochet 1.0 + +ColumnLayout { + anchors { + fill: parent + margins: 8 + } + + property string previousLanguage: uiSettings.data.language + + ExclusiveGroup { + id: languageGroup + } + + Item { height: 8 } + + Label { + Layout.fillWidth: true + text: qsTr("Select Language") + } + + Item { height: 10 } + + GridLayout { + columns: 2 + + Repeater { + model: LanguagesModel { } + delegate: RadioButton { + id: languageSelection + Layout.fillWidth: true + text: nativeName + checked: localeID === uiSettings.data.language + exclusiveGroup: languageGroup + onCheckedChanged: { + if (checked && previousLanguage !== localeID) { + restartNotification.visible = true + uiSettings.write("language", localeID) + } + } + } + } + } + + Item { height: 15 } + + Label { + id: restartNotification + text: qsTr("Restart Ricochet to apply changes") + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + visible: false + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } +} diff --git a/src/ui/qml/PreferencesDialog.qml b/src/ui/qml/PreferencesDialog.qml index daea42fb..95fc3c72 100644 --- a/src/ui/qml/PreferencesDialog.qml +++ b/src/ui/qml/PreferencesDialog.qml @@ -42,6 +42,11 @@ ApplicationWindow { source: Qt.resolvedUrl("GeneralPreferences.qml") } + Tab { + title: qsTr("Language") + source: Qt.resolvedUrl("LanguagePreferences.qml") + } + Tab { title: qsTr("Contacts") source: Qt.resolvedUrl("ContactPreferences.qml") diff --git a/src/ui/qml/qml.qrc b/src/ui/qml/qml.qrc index 9ba39668..e5543955 100644 --- a/src/ui/qml/qml.qrc +++ b/src/ui/qml/qml.qrc @@ -33,6 +33,7 @@ MainToolBar.qml UnreadCountBadge.qml GeneralPreferences.qml + LanguagePreferences.qml ../../../LICENSE