summaryrefslogtreecommitdiffstats
path: root/api/logic
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic')
-rw-r--r--api/logic/CMakeLists.txt5
-rw-r--r--api/logic/trans/TranslationDownloader.cpp51
-rw-r--r--api/logic/trans/TranslationDownloader.h34
-rw-r--r--api/logic/translations/TranslationsModel.cpp315
-rw-r--r--api/logic/translations/TranslationsModel.h61
5 files changed, 378 insertions, 88 deletions
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt
index 02e9afbe..ffecb073 100644
--- a/api/logic/CMakeLists.txt
+++ b/api/logic/CMakeLists.txt
@@ -412,9 +412,8 @@ add_unit_test(JavaVersion
)
set(TRANSLATIONS_SOURCES
- # Translations
- trans/TranslationDownloader.h
- trans/TranslationDownloader.cpp
+ translations/TranslationsModel.h
+ translations/TranslationsModel.cpp
)
set(TOOLS_SOURCES
diff --git a/api/logic/trans/TranslationDownloader.cpp b/api/logic/trans/TranslationDownloader.cpp
deleted file mode 100644
index 61e24c9a..00000000
--- a/api/logic/trans/TranslationDownloader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "TranslationDownloader.h"
-#include "net/NetJob.h"
-#include "net/Download.h"
-#include "net/URLConstants.h"
-#include "Env.h"
-#include <QDebug>
-
-TranslationDownloader::TranslationDownloader()
-{
-}
-void TranslationDownloader::downloadTranslations()
-{
- qDebug() << "Downloading Translations Index...";
- m_index_job.reset(new NetJob("Translations Index"));
- m_index_task = Net::Download::makeByteArray(QUrl("http://files.multimc.org/translations/index"), &m_data);
- m_index_job->addNetAction(m_index_task);
- connect(m_index_job.get(), &NetJob::failed, this, &TranslationDownloader::indexFailed);
- connect(m_index_job.get(), &NetJob::succeeded, this, &TranslationDownloader::indexRecieved);
- m_index_job->start();
-}
-void TranslationDownloader::indexRecieved()
-{
- qDebug() << "Got translations index!";
- m_dl_job.reset(new NetJob("Translations"));
- QList<QByteArray> lines = m_data.split('\n');
- m_data.clear();
- for (const auto line : lines)
- {
- if (!line.isEmpty())
- {
- MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "mmc_" + line);
- entry->setStale(true);
- m_dl_job->addNetAction(Net::Download::makeCached(QUrl(URLConstants::TRANSLATIONS_BASE_URL + line), entry));
- }
- }
- connect(m_dl_job.get(), &NetJob::succeeded, this, &TranslationDownloader::dlGood);
- connect(m_dl_job.get(), &NetJob::failed, this, &TranslationDownloader::dlFailed);
- m_dl_job->start();
-}
-void TranslationDownloader::dlFailed(QString reason)
-{
- qCritical() << "Translations Download Failed:" << reason;
-}
-void TranslationDownloader::dlGood()
-{
- qDebug() << "Got translations!";
-}
-void TranslationDownloader::indexFailed(QString reason)
-{
- qCritical() << "Translations Index Download Failed:" << reason;
-}
diff --git a/api/logic/trans/TranslationDownloader.h b/api/logic/trans/TranslationDownloader.h
deleted file mode 100644
index ad3a648d..00000000
--- a/api/logic/trans/TranslationDownloader.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <QList>
-#include <QUrl>
-#include <memory>
-#include <QObject>
-#include <net/NetJob.h>
-#include "multimc_logic_export.h"
-namespace Net{
-class Download;
-}
-class NetJob;
-
-class MULTIMC_LOGIC_EXPORT TranslationDownloader : public QObject
-{
- Q_OBJECT
-
-public:
- TranslationDownloader();
-
- void downloadTranslations();
-
-private slots:
- void indexRecieved();
- void indexFailed(QString reason);
- void dlFailed(QString reason);
- void dlGood();
-
-private:
- std::shared_ptr<Net::Download> m_index_task;
- NetJobPtr m_dl_job;
- NetJobPtr m_index_job;
- QByteArray m_data;
-};
diff --git a/api/logic/translations/TranslationsModel.cpp b/api/logic/translations/TranslationsModel.cpp
new file mode 100644
index 00000000..1d44484a
--- /dev/null
+++ b/api/logic/translations/TranslationsModel.cpp
@@ -0,0 +1,315 @@
+#include "TranslationsModel.h"
+
+#include <QCoreApplication>
+#include <QTranslator>
+#include <QLocale>
+#include <QDir>
+#include <QLibraryInfo>
+#include <QDebug>
+#include <FileSystem.h>
+#include <net/NetJob.h>
+#include <Env.h>
+#include <net/URLConstants.h>
+
+const static QLatin1Literal defaultLangCode("en");
+
+struct Language
+{
+ QString key;
+ QLocale locale;
+ bool updated;
+};
+
+struct TranslationsModel::Private
+{
+ QDir m_dir;
+
+ // initial state is just english
+ QVector<Language> m_languages = {{defaultLangCode, QLocale(defaultLangCode), false}};
+ QString m_selectedLanguage = defaultLangCode;
+ std::unique_ptr<QTranslator> m_qt_translator;
+ std::unique_ptr<QTranslator> m_app_translator;
+
+ std::shared_ptr<Net::Download> m_index_task;
+ QString m_downloadingTranslation;
+ NetJobPtr m_dl_job;
+ NetJobPtr m_index_job;
+ QString m_nextDownload;
+};
+
+TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent)
+{
+ d.reset(new Private);
+ d->m_dir.setPath(path);
+ loadLocalIndex();
+}
+
+TranslationsModel::~TranslationsModel()
+{
+}
+
+QVariant TranslationsModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+
+ if (row < 0 || row >= d->m_languages.size())
+ return QVariant();
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return d->m_languages[row].locale.nativeLanguageName();
+ case Qt::UserRole:
+ return d->m_languages[row].key;
+ default:
+ return QVariant();
+ }
+}
+
+int TranslationsModel::rowCount(const QModelIndex& parent) const
+{
+ return d->m_languages.size();
+}
+
+Language * TranslationsModel::findLanguage(const QString& key)
+{
+ auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language & lang)
+ {
+ return lang.key == key;
+ });
+ if(found == d->m_languages.end())
+ {
+ return nullptr;
+ }
+ else
+ {
+ return found;
+ }
+}
+
+bool TranslationsModel::selectLanguage(QString key)
+{
+ QString &langCode = key;
+ auto langPtr = findLanguage(key);
+ if(!langPtr)
+ {
+ qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
+ langCode = defaultLangCode;
+ }
+ else
+ {
+ langCode = langPtr->key;
+ }
+
+ // uninstall existing translators if there are any
+ if (d->m_app_translator)
+ {
+ QCoreApplication::removeTranslator(d->m_app_translator.get());
+ d->m_app_translator.reset();
+ }
+ if (d->m_qt_translator)
+ {
+ QCoreApplication::removeTranslator(d->m_qt_translator.get());
+ d->m_qt_translator.reset();
+ }
+
+ /*
+ * FIXME: potential source of crashes:
+ * In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
+ * This function is not reentrant.
+ */
+ QLocale locale(langCode);
+ QLocale::setDefault(locale);
+
+ // if it's the default UI language, finish
+ if(langCode == defaultLangCode)
+ {
+ d->m_selectedLanguage = langCode;
+ return true;
+ }
+
+ // otherwise install new translations
+ bool successful = false;
+ // FIXME: this is likely never present. FIX IT.
+ d->m_qt_translator.reset(new QTranslator());
+ if (d->m_qt_translator->load("qt_" + langCode, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ {
+ qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "...";
+ if (!QCoreApplication::installTranslator(d->m_qt_translator.get()))
+ {
+ qCritical() << "Loading Qt Language File failed.";
+ d->m_qt_translator.reset();
+ }
+ else
+ {
+ successful = true;
+ }
+ }
+ else
+ {
+ d->m_qt_translator.reset();
+ }
+
+ d->m_app_translator.reset(new QTranslator());
+ if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path()))
+ {
+ qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "...";
+ if (!QCoreApplication::installTranslator(d->m_app_translator.get()))
+ {
+ qCritical() << "Loading Application Language File failed.";
+ d->m_app_translator.reset();
+ }
+ else
+ {
+ successful = true;
+ }
+ }
+ else
+ {
+ d->m_app_translator.reset();
+ }
+ d->m_selectedLanguage = langCode;
+ return successful;
+}
+
+QModelIndex TranslationsModel::selectedIndex()
+{
+ auto found = findLanguage(d->m_selectedLanguage);
+ if(found)
+ {
+ // QVector iterator freely converts to pointer to contained type
+ return index(found - d->m_languages.begin(), 0, QModelIndex());
+ }
+ return QModelIndex();
+}
+
+QString TranslationsModel::selectedLanguage()
+{
+ return d->m_selectedLanguage;
+}
+
+void TranslationsModel::downloadIndex()
+{
+ qDebug() << "Downloading Translations Index...";
+ d->m_index_job.reset(new NetJob("Translations Index"));
+ MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "index");
+ d->m_index_task = Net::Download::makeCached(QUrl("http://files.multimc.org/translations/index"), entry);
+ d->m_index_job->addNetAction(d->m_index_task);
+ connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
+ connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexRecieved);
+ d->m_index_job->start();
+}
+
+void TranslationsModel::indexRecieved()
+{
+ qDebug() << "Got translations index!";
+ d->m_index_job.reset();
+ loadLocalIndex();
+ if(d->m_selectedLanguage != defaultLangCode)
+ {
+ downloadTranslation(d->m_selectedLanguage);
+ }
+}
+
+void TranslationsModel::loadLocalIndex()
+{
+ QByteArray data;
+ try
+ {
+ data = FS::read(d->m_dir.absoluteFilePath("index"));
+ }
+ catch (Exception &e)
+ {
+ qCritical() << "Translations Download Failed: index file not readable";
+ return;
+ }
+ QVector<Language> languages;
+ QList<QByteArray> lines = data.split('\n');
+ // add the default english.
+ languages.append({defaultLangCode, QLocale(defaultLangCode), true});
+ for (const auto line : lines)
+ {
+ if(!line.isEmpty())
+ {
+ auto str = QString::fromLatin1(line);
+ str.remove(".qm");
+ languages.append({str, QLocale(str), false});
+ }
+ }
+ beginResetModel();
+ d->m_languages.swap(languages);
+ endResetModel();
+}
+
+void TranslationsModel::updateLanguage(QString key)
+{
+ if(key == defaultLangCode)
+ {
+ qWarning() << "Cannot update builtin language" << key;
+ return;
+ }
+ auto found = findLanguage(key);
+ if(!found)
+ {
+ qWarning() << "Cannot update invalid language" << key;
+ return;
+ }
+ if(!found->updated)
+ {
+ downloadTranslation(key);
+ }
+}
+
+void TranslationsModel::downloadTranslation(QString key)
+{
+ if(d->m_dl_job)
+ {
+ d->m_nextDownload = key;
+ return;
+ }
+ d->m_downloadingTranslation = key;
+ MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "mmc_" + key + ".qm");
+ entry->setStale(true);
+ d->m_dl_job.reset(new NetJob("Translation for " + key));
+ d->m_dl_job->addNetAction(Net::Download::makeCached(QUrl(URLConstants::TRANSLATIONS_BASE_URL + key + ".qm"), entry));
+ connect(d->m_dl_job.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood);
+ connect(d->m_dl_job.get(), &NetJob::failed, this, &TranslationsModel::dlFailed);
+ d->m_dl_job->start();
+}
+
+void TranslationsModel::downloadNext()
+{
+ if(!d->m_nextDownload.isEmpty())
+ {
+ downloadTranslation(d->m_nextDownload);
+ d->m_nextDownload.clear();
+ }
+}
+
+void TranslationsModel::dlFailed(QString reason)
+{
+ qCritical() << "Translations Download Failed:" << reason;
+ d->m_dl_job.reset();
+ downloadNext();
+}
+
+void TranslationsModel::dlGood()
+{
+ qDebug() << "Got translation:" << d->m_downloadingTranslation;
+
+ if(d->m_downloadingTranslation == d->m_selectedLanguage)
+ {
+ selectLanguage(d->m_selectedLanguage);
+ }
+ d->m_dl_job.reset();
+ downloadNext();
+}
+
+void TranslationsModel::indexFailed(QString reason)
+{
+ qCritical() << "Translations Index Download Failed:" << reason;
+ d->m_index_job.reset();
+}
diff --git a/api/logic/translations/TranslationsModel.h b/api/logic/translations/TranslationsModel.h
new file mode 100644
index 00000000..90184b79
--- /dev/null
+++ b/api/logic/translations/TranslationsModel.h
@@ -0,0 +1,61 @@
+/* Copyright 2013-2016 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <memory>
+#include "multimc_logic_export.h"
+
+struct Language;
+
+class MULTIMC_LOGIC_EXPORT TranslationsModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit TranslationsModel(QString path, QObject *parent = 0);
+ virtual ~TranslationsModel();
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ bool selectLanguage(QString key);
+ void updateLanguage(QString key);
+ QModelIndex selectedIndex();
+ QString selectedLanguage();
+
+ void downloadIndex();
+
+private:
+ Language *findLanguage(const QString & key);
+ void loadLocalIndex();
+ void downloadTranslation(QString key);
+ void downloadNext();
+
+ // hide copy constructor
+ TranslationsModel(const TranslationsModel &) = delete;
+ // hide assign op
+ TranslationsModel &operator=(const TranslationsModel &) = delete;
+
+private slots:
+ void indexRecieved();
+ void indexFailed(QString reason);
+ void dlFailed(QString reason);
+ void dlGood();
+
+private: /* data */
+ struct Private;
+ std::unique_ptr<Private> d;
+};