diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 59d9ec3a8..ebbd7cdb6 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -36,13 +36,9 @@ #include "TranslationsModel.h" -#include #include -#include -#include -#include -#include -#include +#include +#include #include "BuildConfig.h" #include "FileSystem.h" @@ -55,18 +51,26 @@ #include "Application.h" #include "settings/SettingsObject.h" -const static QLatin1String defaultLangCode("en_US"); +static constexpr QLatin1String g_defaultLangCode("en_US"); -enum class FileType { NONE, QM, PO }; +namespace { +enum class FileType : std::uint8_t { None, Qm, Po }; + +QString getSystemLocaleName() +{ + return QLocale::system().name(); +} + +QString getSystemLanguage() +{ + return getSystemLocaleName().split('_').front(); +} +} // namespace struct Language { - Language() { updated = true; } - Language(const QString& _key) - { - key = _key; - locale = QLocale(key); - updated = (key == defaultLangCode); - } + Language() : updated(true) {} + + explicit Language(QString _key) : key(std::move(_key)), updated(key == g_defaultLangCode) { locale = QLocale(key); } QString languageName() const { @@ -98,12 +102,12 @@ struct Language { float percentTranslated() const { if (total == 0) { - return 100.0f; + return 100.0F; } - return 100.0f * float(translated) / float(total); + return 100.0F * static_cast(translated) / static_cast(total); } - void setTranslationStats(unsigned _translated, unsigned _untranslated, unsigned _fuzzy) + void setTranslationStats(const unsigned _translated, const unsigned _untranslated, const unsigned _fuzzy) { translated = _translated; untranslated = _untranslated; @@ -115,18 +119,18 @@ struct Language { bool isIdenticalTo(const Language& other) const { - return (key == other.key && file_name == other.file_name && file_size == other.file_size && file_sha1 == other.file_sha1 && + return (key == other.key && fileName == other.fileName && fileSize == other.fileSize && fileSha1 == other.fileSha1 && translated == other.translated && fuzzy == other.fuzzy && total == other.fuzzy && localFileType == other.localFileType); } - Language& apply(Language& other) + Language& apply(const Language& other) { if (!isOfSameNameAs(other)) { return *this; } - file_name = other.file_name; - file_size = other.file_size; - file_sha1 = other.file_sha1; + fileName = other.fileName; + fileSize = other.fileSize; + fileSha1 = other.fileSha1; translated = other.translated; fuzzy = other.fuzzy; total = other.total; @@ -138,46 +142,42 @@ struct Language { QLocale locale; bool updated; - QString file_name = QString(); - std::size_t file_size = 0; - QString file_sha1 = QString(); + QString fileName = QString(); + std::size_t fileSize = 0; + QString fileSha1 = QString(); unsigned translated = 0; unsigned untranslated = 0; unsigned fuzzy = 0; unsigned total = 0; - FileType localFileType = FileType::NONE; + FileType localFileType = FileType::None; }; struct TranslationsModel::Private { QDir m_dir; // initial state is just english - QList m_languages = { Language(defaultLangCode) }; + QList m_languages = { Language(g_defaultLangCode) }; - QString m_selectedLanguage = defaultLangCode; - std::unique_ptr m_qt_translator; - std::unique_ptr m_app_translator; + QString m_selectedLanguage = g_defaultLangCode; + std::unique_ptr m_qtTranslator; + std::unique_ptr m_appTranslator; - Net::Download* m_index_task; + Net::Download* m_indexTask = nullptr; QString m_downloadingTranslation; - NetJob::Ptr m_dl_job; - NetJob::Ptr m_index_job; + NetJob::Ptr m_downloadJob; + NetJob::Ptr m_indexJob; QString m_nextDownload; - std::unique_ptr m_po_translator; - QFileSystemWatcher* watcher; + QFileSystemWatcher* watcher = nullptr; - const QString m_system_locale = QLocale::system().name(); - const QString m_system_language = m_system_locale.split('_').front(); - - bool no_language_set = false; + bool m_noLanguageSet = false; }; -TranslationsModel::TranslationsModel(QString path, QObject* parent) : QAbstractListModel(parent) +TranslationsModel::TranslationsModel(const QString& path, QObject* parent) : QAbstractListModel(parent) { - d.reset(new Private); + d = std::make_unique(); d->m_dir.setPath(path); FS::ensureFolderPathExists(path); reloadLocalFiles(); @@ -187,12 +187,12 @@ TranslationsModel::TranslationsModel(QString path, QObject* parent) : QAbstractL d->watcher->addPath(d->m_dir.canonicalPath()); } -TranslationsModel::~TranslationsModel() {} +TranslationsModel::~TranslationsModel() = default; void TranslationsModel::translationDirChanged(const QString& path) { qDebug() << "Dir changed:" << path; - if (!d->no_language_set) { + if (!d->m_noLanguageSet) { reloadLocalFiles(); } selectLanguage(selectedLanguage()); @@ -201,24 +201,22 @@ void TranslationsModel::translationDirChanged(const QString& path) void TranslationsModel::indexReceived() { qDebug() << "Got translations index!"; - d->m_index_job.reset(); + d->m_indexJob.reset(); - if (d->no_language_set) { + if (d->m_noLanguageSet) { reloadLocalFiles(); - auto language = d->m_system_locale; + auto language = getSystemLocaleName(); if (!findLanguageAsOptional(language).has_value()) { - language = d->m_system_language; + language = getSystemLanguage(); } selectLanguage(language); - if (selectedLanguage() != defaultLangCode) { + if (selectedLanguage() != g_defaultLangCode) { updateLanguage(selectedLanguage()); } APPLICATION->settings()->set("Language", selectedLanguage()); - d->no_language_set = false; - } - - else if (d->m_selectedLanguage != defaultLangCode) { + d->m_noLanguageSet = false; + } else if (d->m_selectedLanguage != g_defaultLangCode) { downloadTranslation(d->m_selectedLanguage); } } @@ -235,27 +233,27 @@ void readIndex(const QString& path, QMap& languages) } try { - auto toplevel_doc = Json::requireDocument(data); - auto doc = Json::requireObject(toplevel_doc); - auto file_type = Json::requireString(doc, "file_type"); - if (file_type != "MMC-TRANSLATION-INDEX") { - qCritical() << "Translations Download Failed: index file is of unknown file type" << file_type; + auto toplevelDoc = Json::requireDocument(data); + auto doc = Json::requireObject(toplevelDoc); + auto fileType = Json::requireString(doc, "file_type"); + if (fileType != "MMC-TRANSLATION-INDEX") { + qCritical() << "Translations Download Failed: index file is of unknown file type" << fileType; return; } auto version = Json::requireInteger(doc, "version"); if (version > 2) { - qCritical() << "Translations Download Failed: index file is of unknown format version" << file_type; + qCritical() << "Translations Download Failed: index file is of unknown format version" << fileType; return; } auto langObjs = Json::requireObject(doc, "languages"); - for (auto iter = langObjs.begin(); iter != langObjs.end(); iter++) { + for (auto iter = langObjs.begin(); iter != langObjs.end(); ++iter) { Language lang(iter.key()); auto langObj = Json::requireObject(iter.value()); lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(), langObj["fuzzy"].toInt()); - lang.file_name = Json::requireString(langObj, "file"); - lang.file_sha1 = Json::requireString(langObj, "sha1"); - lang.file_size = Json::requireInteger(langObj, "size"); + lang.fileName = Json::requireString(langObj, "file"); + lang.fileSha1 = Json::requireString(langObj, "sha1"); + lang.fileSize = Json::requireInteger(langObj, "size"); languages.insert(lang.key, lang); } @@ -267,20 +265,20 @@ void readIndex(const QString& path, QMap& languages) void TranslationsModel::reloadLocalFiles() { - QMap languages = { { defaultLangCode, Language(defaultLangCode) } }; + QMap languages = { { g_defaultLangCode, Language(g_defaultLangCode) } }; readIndex(d->m_dir.absoluteFilePath("index_v2.json"), languages); auto entries = d->m_dir.entryInfoList({ "mmc_*.qm", "*.po" }, QDir::Files | QDir::NoDotAndDotDot); for (auto& entry : entries) { auto completeSuffix = entry.completeSuffix(); QString langCode; - FileType fileType = FileType::NONE; + FileType fileType = FileType::None; if (completeSuffix == "qm") { langCode = entry.baseName().remove(0, 4); - fileType = FileType::QM; + fileType = FileType::Qm; } else if (completeSuffix == "po") { langCode = entry.baseName(); - fileType = FileType::PO; + fileType = FileType::Po; } else { continue; } @@ -288,13 +286,14 @@ void TranslationsModel::reloadLocalFiles() auto langIter = languages.find(langCode); if (langIter != languages.end()) { auto& language = *langIter; - if (int(fileType) > int(language.localFileType)) { + // TODO: use std::to_underlying in C++23 + if (static_cast(fileType) > static_cast(language.localFileType)) { language.localFileType = fileType; } } else { - if (fileType == FileType::PO) { + if (fileType == FileType::Po) { Language localFound(langCode); - localFound.localFileType = FileType::PO; + localFound.localFileType = FileType::Po; languages.insert(langCode, localFound); } } @@ -314,7 +313,7 @@ void TranslationsModel::reloadLocalFiles() emit dataChanged(index(row), index(row)); languages.remove(language.key); } - iter++; + ++iter; } else { beginRemoveRows(QModelIndex(), row, row); iter = d->m_languages.erase(iter); @@ -329,34 +328,38 @@ void TranslationsModel::reloadLocalFiles() for (auto& language : languages) { d->m_languages.append(language); } - std::sort(d->m_languages.begin(), d->m_languages.end(), [this](const Language& a, const Language& b) { + + const auto comp = [systemLocale = getSystemLocaleName(), systemLanguage = getSystemLanguage()](const Language& a, const Language& b) { if (a.key != b.key) { - if (a.key == d->m_system_locale || a.key == d->m_system_language) { + if (a.key == systemLocale || a.key == systemLanguage) { return true; } - if (b.key == d->m_system_locale || b.key == d->m_system_language) { + if (b.key == systemLocale || b.key == systemLanguage) { return false; } } return a.languageName().toLower() < b.languageName().toLower(); - }); + }; + std::ranges::sort(d->m_languages, comp); endInsertRows(); } namespace { -enum class Column { Language, Completeness }; +enum class Column : std::uint8_t { Language, Completeness }; } -QVariant TranslationsModel::data(const QModelIndex& index, int role) const +QVariant TranslationsModel::data(const QModelIndex& index, const int role) const { - if (!index.isValid()) - return QVariant(); + if (!index.isValid()) { + return {}; + } - int row = index.row(); - auto column = static_cast(index.column()); + const int row = index.row(); + const auto column = static_cast(index.column()); - if (row < 0 || row >= d->m_languages.size()) - return QVariant(); + if (row < 0 || row >= d->m_languages.size()) { + return {}; + } auto& lang = d->m_languages[row]; switch (role) { @@ -378,11 +381,11 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const case Qt::UserRole: return lang.key; default: - return QVariant(); + return {}; } } -QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant TranslationsModel::headerData(int section, const Qt::Orientation orientation, const int role) const { auto column = static_cast(section); if (role == Qt::DisplayRole) { @@ -417,49 +420,50 @@ int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) c return 2; } -QList::Iterator TranslationsModel::findLanguage(const QString& key) +QList::Iterator TranslationsModel::findLanguage(const QString& key) const { - return std::find_if(d->m_languages.begin(), d->m_languages.end(), [key](Language& lang) { return lang.key == key; }); + return std::ranges::find_if(d->m_languages, [key](const Language& lang) { return lang.key == key; }); } -std::optional TranslationsModel::findLanguageAsOptional(const QString& key) +std::optional TranslationsModel::findLanguageAsOptional(const QString& key) const { auto found = findLanguage(key); - if (found != d->m_languages.end()) + if (found != d->m_languages.end()) { return *found; + } return {}; } -void TranslationsModel::setUseSystemLocale(bool useSystemLocale) +void TranslationsModel::setUseSystemLocale(const bool useSystemLocale) const { APPLICATION->settings()->set("UseSystemLocale", useSystemLocale); QLocale::setDefault(useSystemLocale ? QLocale::system() : QLocale(selectedLanguage())); } -bool TranslationsModel::selectLanguage(QString key) +bool TranslationsModel::selectLanguage(QString key) const { QString& langCode = key; auto langPtr = findLanguageAsOptional(key); if (langCode.isEmpty()) { - d->no_language_set = true; + d->m_noLanguageSet = true; } if (!langPtr.has_value()) { - qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode; - langCode = defaultLangCode; + qWarning() << "Selected invalid language" << key << ", defaulting to" << g_defaultLangCode; + langCode = g_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_appTranslator) { + QCoreApplication::removeTranslator(d->m_appTranslator.get()); + d->m_appTranslator.reset(); } - if (d->m_qt_translator) { - QCoreApplication::removeTranslator(d->m_qt_translator.get()); - d->m_qt_translator.reset(); + if (d->m_qtTranslator) { + QCoreApplication::removeTranslator(d->m_qtTranslator.get()); + d->m_qtTranslator.reset(); } /* @@ -471,7 +475,7 @@ bool TranslationsModel::selectLanguage(QString key) QLocale::setDefault(useSystemLocale ? QLocale::system() : QLocale(langCode)); // if it's the default UI language, finish - if (langCode == defaultLangCode) { + if (langCode == g_defaultLangCode) { d->m_selectedLanguage = langCode; return true; } @@ -479,89 +483,88 @@ bool TranslationsModel::selectLanguage(QString key) // 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::path(QLibraryInfo::TranslationsPath))) { + d->m_qtTranslator = std::make_unique(); + if (d->m_qtTranslator->load("qt_" + langCode, QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "..."; - if (!QCoreApplication::installTranslator(d->m_qt_translator.get())) { + if (!QCoreApplication::installTranslator(d->m_qtTranslator.get())) { qCritical() << "Loading Qt Language File failed."; - d->m_qt_translator.reset(); + d->m_qtTranslator.reset(); } else { successful = true; } } else { - d->m_qt_translator.reset(); + d->m_qtTranslator.reset(); } - if (langPtr->localFileType == FileType::PO) { + if (langPtr->localFileType == FileType::Po) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; - auto poTranslator = new POTranslator(FS::PathCombine(d->m_dir.path(), langCode + ".po")); - if (!poTranslator->isEmpty()) { - if (!QCoreApplication::installTranslator(poTranslator)) { - delete poTranslator; + d->m_appTranslator = std::make_unique(FS::PathCombine(d->m_dir.path(), langCode + ".po")); + if (!d->m_appTranslator->isEmpty()) { + if (!QCoreApplication::installTranslator(d->m_appTranslator.get())) { qCritical() << "Installing Application Language File failed."; + d->m_appTranslator.reset(); } else { - d->m_app_translator.reset(poTranslator); successful = true; } } else { qCritical() << "Loading Application Language File failed."; - d->m_app_translator.reset(); + d->m_appTranslator.reset(); } - } else if (langPtr->localFileType == FileType::QM) { - d->m_app_translator.reset(new QTranslator()); - if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path())) { + } else if (langPtr->localFileType == FileType::Qm) { + d->m_appTranslator = std::make_unique(); + if (d->m_appTranslator->load("mmc_" + langCode, d->m_dir.path())) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; - if (!QCoreApplication::installTranslator(d->m_app_translator.get())) { + if (!QCoreApplication::installTranslator(d->m_appTranslator.get())) { qCritical() << "Installing Application Language File failed."; - d->m_app_translator.reset(); + d->m_appTranslator.reset(); } else { successful = true; } } else { - d->m_app_translator.reset(); + d->m_appTranslator.reset(); } } else { - d->m_app_translator.reset(); + d->m_appTranslator.reset(); } d->m_selectedLanguage = langCode; return successful; } -QModelIndex TranslationsModel::selectedIndex() +QModelIndex TranslationsModel::selectedIndex() const { auto found = findLanguage(d->m_selectedLanguage); if (found != d->m_languages.end()) { return index(std::distance(d->m_languages.begin(), found), 0, QModelIndex()); } - return QModelIndex(); + return {}; } -QString TranslationsModel::selectedLanguage() +QString TranslationsModel::selectedLanguage() const { return d->m_selectedLanguage; } void TranslationsModel::downloadIndex() { - if (d->m_index_job || d->m_dl_job) { + if (d->m_indexJob || d->m_downloadJob) { return; } qDebug() << "Downloading Translations Index..."; - d->m_index_job.reset(new NetJob("Translations Index", APPLICATION->network())); - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json"); + d->m_indexJob.reset(new NetJob("Translations Index", APPLICATION->network())); + const MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json"); entry->setStale(true); auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + "index_v2.json"), entry); - d->m_index_task = task.get(); - d->m_index_job->addNetAction(task); - d->m_index_job->setAskRetry(false); - connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed); - connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived); - d->m_index_job->start(); + d->m_indexTask = task.get(); + d->m_indexJob->addNetAction(task); + d->m_indexJob->setAskRetry(false); + connect(d->m_indexJob.get(), &NetJob::failed, this, &TranslationsModel::indexFailed); + connect(d->m_indexJob.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived); + d->m_indexJob->start(); } -void TranslationsModel::updateLanguage(QString key) +void TranslationsModel::updateLanguage(const QString& key) { - if (key == defaultLangCode) { + if (key == g_defaultLangCode) { qWarning() << "Cannot update builtin language" << key; return; } @@ -575,9 +578,9 @@ void TranslationsModel::updateLanguage(QString key) } } -void TranslationsModel::downloadTranslation(QString key) +void TranslationsModel::downloadTranslation(const QString& key) { - if (d->m_dl_job) { + if (d->m_downloadJob) { d->m_nextDownload = key; return; } @@ -588,21 +591,21 @@ void TranslationsModel::downloadTranslation(QString key) } d->m_downloadingTranslation = key; - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm"); + const MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm"); entry->setStale(true); - auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + lang->file_name), entry); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1)); - dl->setProgress(dl->getProgress(), lang->file_size); + auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + lang->fileName), entry); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->fileSha1)); + dl->setProgress(dl->getProgress(), lang->fileSize); - d->m_dl_job.reset(new NetJob("Translation for " + key, APPLICATION->network())); - d->m_dl_job->addNetAction(dl); - d->m_dl_job->setAskRetry(false); + d->m_downloadJob.reset(new NetJob("Translation for " + key, APPLICATION->network())); + d->m_downloadJob->addNetAction(dl); + d->m_downloadJob->setAskRetry(false); - connect(d->m_dl_job.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood); - connect(d->m_dl_job.get(), &NetJob::failed, this, &TranslationsModel::dlFailed); + connect(d->m_downloadJob.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood); + connect(d->m_downloadJob.get(), &NetJob::failed, this, &TranslationsModel::dlFailed); - d->m_dl_job->start(); + d->m_downloadJob->start(); } void TranslationsModel::downloadNext() @@ -613,10 +616,10 @@ void TranslationsModel::downloadNext() } } -void TranslationsModel::dlFailed(QString reason) +void TranslationsModel::dlFailed(const QString& reason) { qCritical() << "Translations Download Failed:" << reason; - d->m_dl_job.reset(); + d->m_downloadJob.reset(); downloadNext(); } @@ -627,12 +630,12 @@ void TranslationsModel::dlGood() if (d->m_downloadingTranslation == d->m_selectedLanguage) { selectLanguage(d->m_selectedLanguage); } - d->m_dl_job.reset(); + d->m_downloadJob.reset(); downloadNext(); } -void TranslationsModel::indexFailed(QString reason) +void TranslationsModel::indexFailed(const QString& reason) const { qCritical() << "Translations Index Download Failed:" << reason; - d->m_index_job.reset(); + d->m_indexJob.reset(); } diff --git a/launcher/translations/TranslationsModel.h b/launcher/translations/TranslationsModel.h index 945e689fc..1dd0b601c 100644 --- a/launcher/translations/TranslationsModel.h +++ b/launcher/translations/TranslationsModel.h @@ -24,38 +24,38 @@ struct Language; class TranslationsModel : public QAbstractListModel { Q_OBJECT public: - explicit TranslationsModel(QString path, QObject* parent = 0); - virtual ~TranslationsModel(); - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - int columnCount(const QModelIndex& parent) const override; - - bool selectLanguage(QString key); - void updateLanguage(QString key); - QModelIndex selectedIndex(); - QString selectedLanguage(); - - void downloadIndex(); - void setUseSystemLocale(bool useSystemLocale); - - private: - QList::Iterator findLanguage(const QString& key); - std::optional findLanguageAsOptional(const QString& key); - void reloadLocalFiles(); - void downloadTranslation(QString key); - void downloadNext(); + explicit TranslationsModel(const QString& path, QObject* parent = nullptr); + ~TranslationsModel() override; // hide copy constructor TranslationsModel(const TranslationsModel&) = delete; // hide assign op TranslationsModel& operator=(const TranslationsModel&) = delete; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; // NOLINT(*-default-arguments) + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; // NOLINT(*-default-arguments) + bool selectLanguage(QString key) const; + void updateLanguage(const QString& key); + QModelIndex selectedIndex() const; + QString selectedLanguage() const; + + void downloadIndex(); + void setUseSystemLocale(bool useSystemLocale) const; + + private: + int columnCount(const QModelIndex& parent) const override; + + QList::Iterator findLanguage(const QString& key) const; + std::optional findLanguageAsOptional(const QString& key) const; + void reloadLocalFiles(); + void downloadTranslation(const QString& key); + void downloadNext(); + private slots: void indexReceived(); - void indexFailed(QString reason); - void dlFailed(QString reason); + void indexFailed(const QString& reason) const; + void dlFailed(const QString& reason); void dlGood(); void translationDirChanged(const QString& path);