@ -3,14 +3,12 @@
|
||||
#include <QDebug>
|
||||
#include "FileSystem.h"
|
||||
|
||||
struct POEntry
|
||||
{
|
||||
struct POEntry {
|
||||
QString text;
|
||||
bool fuzzy;
|
||||
};
|
||||
|
||||
struct POTranslatorPrivate
|
||||
{
|
||||
struct POTranslatorPrivate {
|
||||
QString filename;
|
||||
QHash<QByteArray, POEntry> mapping;
|
||||
QHash<QByteArray, POEntry> mapping_disambiguatrion;
|
||||
@ -19,47 +17,37 @@ struct POTranslatorPrivate
|
||||
void reload();
|
||||
};
|
||||
|
||||
class ParserArray : public QByteArray
|
||||
{
|
||||
public:
|
||||
ParserArray(const QByteArray &in) : QByteArray(in)
|
||||
class ParserArray : public QByteArray {
|
||||
public:
|
||||
ParserArray(const QByteArray& in) : QByteArray(in) {}
|
||||
bool chomp(const char* data, int length)
|
||||
{
|
||||
}
|
||||
bool chomp(const char * data, int length)
|
||||
{
|
||||
if(startsWith(data))
|
||||
{
|
||||
if (startsWith(data)) {
|
||||
remove(0, length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool chompString(QByteArray & appendHere)
|
||||
bool chompString(QByteArray& appendHere)
|
||||
{
|
||||
QByteArray msg;
|
||||
bool escape = false;
|
||||
if(size() < 2)
|
||||
{
|
||||
if (size() < 2) {
|
||||
qDebug() << "String fragment is too short";
|
||||
return false;
|
||||
}
|
||||
if(!startsWith('"'))
|
||||
{
|
||||
if (!startsWith('"')) {
|
||||
qDebug() << "String fragment does not start with \"";
|
||||
return false;
|
||||
}
|
||||
if(!endsWith('"'))
|
||||
{
|
||||
if (!endsWith('"')) {
|
||||
qDebug() << "String fragment does not end with \", instead, there is" << at(size() - 1);
|
||||
return false;
|
||||
}
|
||||
for(int i = 1; i < size() - 1; i++)
|
||||
{
|
||||
for (int i = 1; i < size() - 1; i++) {
|
||||
char c = operator[](i);
|
||||
if(escape)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
case 'r':
|
||||
msg += '\r';
|
||||
break;
|
||||
@ -94,14 +82,11 @@ public:
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
{
|
||||
case '7': {
|
||||
int octal_start = i;
|
||||
while ((c = operator[](i)) >= '0' && c <= '7')
|
||||
{
|
||||
while ((c = operator[](i)) >= '0' && c <= '7') {
|
||||
i++;
|
||||
if (i == length() - 1)
|
||||
{
|
||||
if (i == length() - 1) {
|
||||
qDebug() << "Something went bad while parsing an octal escape string...";
|
||||
return false;
|
||||
}
|
||||
@ -109,16 +94,13 @@ public:
|
||||
msg += mid(octal_start, i - octal_start).toUInt(0, 8);
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
{
|
||||
case 'x': {
|
||||
// chomp the 'x'
|
||||
i++;
|
||||
int hex_start = i;
|
||||
while (isxdigit(operator[](i)))
|
||||
{
|
||||
while (isxdigit(operator[](i))) {
|
||||
i++;
|
||||
if (i == length() - 1)
|
||||
{
|
||||
if (i == length() - 1) {
|
||||
qDebug() << "Something went bad while parsing a hex escape string...";
|
||||
return false;
|
||||
}
|
||||
@ -126,25 +108,19 @@ public:
|
||||
msg += mid(hex_start, i - hex_start).toUInt(0, 16);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
default: {
|
||||
qDebug() << "Invalid escape sequence character:" << c;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
escape = false;
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
} else if (c == '\\') {
|
||||
escape = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
msg += c;
|
||||
}
|
||||
}
|
||||
if(escape)
|
||||
{
|
||||
if (escape) {
|
||||
qDebug() << "Unterminated escape sequence...";
|
||||
return false;
|
||||
}
|
||||
@ -156,8 +132,7 @@ public:
|
||||
void POTranslatorPrivate::reload()
|
||||
{
|
||||
QFile file(filename);
|
||||
if(!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text))
|
||||
{
|
||||
if (!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text)) {
|
||||
qDebug() << "Failed to open PO file:" << filename;
|
||||
return;
|
||||
}
|
||||
@ -169,13 +144,7 @@ void POTranslatorPrivate::reload()
|
||||
bool fuzzy = false;
|
||||
bool nextFuzzy = false;
|
||||
|
||||
enum class Mode
|
||||
{
|
||||
First,
|
||||
MessageContext,
|
||||
MessageId,
|
||||
MessageString
|
||||
} mode = Mode::First;
|
||||
enum class Mode { First, MessageContext, MessageId, MessageString } mode = Mode::First;
|
||||
|
||||
int lineNumber = 0;
|
||||
QHash<QByteArray, POEntry> newMapping;
|
||||
@ -183,14 +152,12 @@ void POTranslatorPrivate::reload()
|
||||
auto endEntry = [&]() {
|
||||
auto strStr = QString::fromUtf8(str);
|
||||
// NOTE: PO header has empty id. We skip it.
|
||||
if(!id.isEmpty())
|
||||
{
|
||||
if (!id.isEmpty()) {
|
||||
auto normalKey = context + "|" + id;
|
||||
newMapping.insert(normalKey, {strStr, fuzzy});
|
||||
if(!disambiguation.isEmpty())
|
||||
{
|
||||
newMapping.insert(normalKey, { strStr, fuzzy });
|
||||
if (!disambiguation.isEmpty()) {
|
||||
auto disambiguationKey = context + "|" + id + "@" + disambiguation;
|
||||
newMapping_disambiguation.insert(disambiguationKey, {strStr, fuzzy});
|
||||
newMapping_disambiguation.insert(disambiguationKey, { strStr, fuzzy });
|
||||
}
|
||||
}
|
||||
context.clear();
|
||||
@ -200,36 +167,26 @@ void POTranslatorPrivate::reload()
|
||||
fuzzy = nextFuzzy;
|
||||
nextFuzzy = false;
|
||||
};
|
||||
while (!file.atEnd())
|
||||
{
|
||||
while (!file.atEnd()) {
|
||||
ParserArray line = file.readLine();
|
||||
if(line.endsWith('\n'))
|
||||
{
|
||||
if (line.endsWith('\n')) {
|
||||
line.resize(line.size() - 1);
|
||||
}
|
||||
if(line.endsWith('\r'))
|
||||
{
|
||||
if (line.endsWith('\r')) {
|
||||
line.resize(line.size() - 1);
|
||||
}
|
||||
|
||||
if(!line.size())
|
||||
{
|
||||
if (!line.size()) {
|
||||
// NIL
|
||||
}
|
||||
else if(line[0] == '#')
|
||||
{
|
||||
if(line.contains(", fuzzy"))
|
||||
{
|
||||
} else if (line[0] == '#') {
|
||||
if (line.contains(", fuzzy")) {
|
||||
nextFuzzy = true;
|
||||
}
|
||||
}
|
||||
else if(line.startsWith('"'))
|
||||
{
|
||||
} else if (line.startsWith('"')) {
|
||||
QByteArray temp;
|
||||
QByteArray *out = &temp;
|
||||
QByteArray* out = &temp;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case Mode::First:
|
||||
qDebug() << "Unexpected escaped string during initial state... line:" << lineNumber;
|
||||
return;
|
||||
@ -243,16 +200,12 @@ void POTranslatorPrivate::reload()
|
||||
out = &id;
|
||||
break;
|
||||
}
|
||||
if(!line.chompString(*out))
|
||||
{
|
||||
if (!line.chompString(*out)) {
|
||||
qDebug() << "Badly formatted string on line:" << lineNumber;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(line.chomp("msgctxt ", 8))
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
} else if (line.chomp("msgctxt ", 8)) {
|
||||
switch (mode) {
|
||||
case Mode::First:
|
||||
break;
|
||||
case Mode::MessageString:
|
||||
@ -263,21 +216,16 @@ void POTranslatorPrivate::reload()
|
||||
qDebug() << "Unexpected msgctxt line:" << lineNumber;
|
||||
return;
|
||||
}
|
||||
if(line.chompString(context))
|
||||
{
|
||||
if (line.chompString(context)) {
|
||||
auto parts = context.split('|');
|
||||
context = parts[0];
|
||||
if(parts.size() > 1 && !parts[1].isEmpty())
|
||||
{
|
||||
if (parts.size() > 1 && !parts[1].isEmpty()) {
|
||||
disambiguation = parts[1];
|
||||
}
|
||||
mode = Mode::MessageContext;
|
||||
}
|
||||
}
|
||||
else if (line.chomp("msgid ", 6))
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
} else if (line.chomp("msgid ", 6)) {
|
||||
switch (mode) {
|
||||
case Mode::MessageContext:
|
||||
case Mode::First:
|
||||
break;
|
||||
@ -288,15 +236,11 @@ void POTranslatorPrivate::reload()
|
||||
qDebug() << "Unexpected msgid line:" << lineNumber;
|
||||
return;
|
||||
}
|
||||
if(line.chompString(id))
|
||||
{
|
||||
if (line.chompString(id)) {
|
||||
mode = Mode::MessageId;
|
||||
}
|
||||
}
|
||||
else if (line.chomp("msgstr ", 7))
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
} else if (line.chomp("msgstr ", 7)) {
|
||||
switch (mode) {
|
||||
case Mode::First:
|
||||
case Mode::MessageString:
|
||||
case Mode::MessageContext:
|
||||
@ -305,13 +249,10 @@ void POTranslatorPrivate::reload()
|
||||
case Mode::MessageId:
|
||||
break;
|
||||
}
|
||||
if(line.chompString(str))
|
||||
{
|
||||
if (line.chompString(str)) {
|
||||
mode = Mode::MessageString;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
qDebug() << "I did not understand line: " << lineNumber << ":" << QString::fromUtf8(line);
|
||||
}
|
||||
lineNumber++;
|
||||
@ -336,19 +277,15 @@ POTranslator::~POTranslator()
|
||||
|
||||
QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, [[maybe_unused]] int n) const
|
||||
{
|
||||
if(disambiguation)
|
||||
{
|
||||
if (disambiguation) {
|
||||
auto disambiguationKey = QByteArray(context) + "|" + QByteArray(sourceText) + "@" + QByteArray(disambiguation);
|
||||
auto iter = d->mapping_disambiguatrion.find(disambiguationKey);
|
||||
if(iter != d->mapping_disambiguatrion.end())
|
||||
{
|
||||
auto & entry = *iter;
|
||||
if(entry.text.isEmpty())
|
||||
{
|
||||
if (iter != d->mapping_disambiguatrion.end()) {
|
||||
auto& entry = *iter;
|
||||
if (entry.text.isEmpty()) {
|
||||
qDebug() << "Translation entry has no content:" << disambiguationKey;
|
||||
}
|
||||
if(entry.fuzzy)
|
||||
{
|
||||
if (entry.fuzzy) {
|
||||
qDebug() << "Translation entry is fuzzy:" << disambiguationKey << "->" << entry.text;
|
||||
}
|
||||
return entry.text;
|
||||
@ -356,15 +293,12 @@ QString POTranslator::translate(const char* context, const char* sourceText, con
|
||||
}
|
||||
auto key = QByteArray(context) + "|" + QByteArray(sourceText);
|
||||
auto iter = d->mapping.find(key);
|
||||
if(iter != d->mapping.end())
|
||||
{
|
||||
auto & entry = *iter;
|
||||
if(entry.text.isEmpty())
|
||||
{
|
||||
if (iter != d->mapping.end()) {
|
||||
auto& entry = *iter;
|
||||
if (entry.text.isEmpty()) {
|
||||
qDebug() << "Translation entry has no content:" << key;
|
||||
}
|
||||
if(entry.fuzzy)
|
||||
{
|
||||
if (entry.fuzzy) {
|
||||
qDebug() << "Translation entry is fuzzy:" << key << "->" << entry.text;
|
||||
}
|
||||
return entry.text;
|
||||
|
@ -4,14 +4,14 @@
|
||||
|
||||
struct POTranslatorPrivate;
|
||||
|
||||
class POTranslator : public QTranslator
|
||||
{
|
||||
class POTranslator : public QTranslator {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit POTranslator(const QString& filename, QObject * parent = nullptr);
|
||||
public:
|
||||
explicit POTranslator(const QString& filename, QObject* parent = nullptr);
|
||||
virtual ~POTranslator();
|
||||
QString translate(const char * context, const char * sourceText, const char * disambiguation, int n) const override;
|
||||
QString translate(const char* context, const char* sourceText, const char* disambiguation, int n) const override;
|
||||
bool isEmpty() const override;
|
||||
private:
|
||||
POTranslatorPrivate * d;
|
||||
|
||||
private:
|
||||
POTranslatorPrivate* d;
|
||||
};
|
||||
|
@ -37,18 +37,18 @@
|
||||
#include "TranslationsModel.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTranslator>
|
||||
#include <QLocale>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLibraryInfo>
|
||||
#include <QDebug>
|
||||
#include <QLocale>
|
||||
#include <QTranslator>
|
||||
#include <locale>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "POTranslator.h"
|
||||
|
||||
@ -56,50 +56,35 @@
|
||||
|
||||
const static QLatin1String defaultLangCode("en_US");
|
||||
|
||||
enum class FileType
|
||||
{
|
||||
NONE,
|
||||
QM,
|
||||
PO
|
||||
};
|
||||
enum class FileType { NONE, QM, PO };
|
||||
|
||||
struct Language
|
||||
{
|
||||
Language()
|
||||
{
|
||||
updated = true;
|
||||
}
|
||||
Language(const QString & _key)
|
||||
struct Language {
|
||||
Language() { updated = true; }
|
||||
Language(const QString& _key)
|
||||
{
|
||||
key = _key;
|
||||
locale = QLocale(key);
|
||||
updated = (key == defaultLangCode);
|
||||
}
|
||||
|
||||
QString languageName() const {
|
||||
QString languageName() const
|
||||
{
|
||||
QString result;
|
||||
if(key == "ja_KANJI") {
|
||||
if (key == "ja_KANJI") {
|
||||
result = locale.nativeLanguageName() + u8" (漢字)";
|
||||
}
|
||||
else if(key == "es_UY") {
|
||||
} else if (key == "es_UY") {
|
||||
result = u8"español de Latinoamérica";
|
||||
}
|
||||
else if(key == "en_NZ") {
|
||||
result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English
|
||||
}
|
||||
else if(key == "en@pirate") {
|
||||
} else if (key == "en_NZ") {
|
||||
result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English
|
||||
} else if (key == "en@pirate") {
|
||||
result = u8"Tongue of the High Seas";
|
||||
}
|
||||
else if(key == "en@uwu") {
|
||||
} else if (key == "en@uwu") {
|
||||
result = u8"Cute Engwish";
|
||||
}
|
||||
else if(key == "tok") {
|
||||
} else if (key == "tok") {
|
||||
result = u8"toki pona";
|
||||
}
|
||||
else if(key == "nan") {
|
||||
} else if (key == "nan") {
|
||||
result = u8"閩南語"; // Using traditional Chinese script. Not sure if we should use simplified instead?
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result = locale.nativeLanguageName();
|
||||
}
|
||||
|
||||
@ -111,8 +96,7 @@ struct Language
|
||||
|
||||
float percentTranslated() const
|
||||
{
|
||||
if (total == 0)
|
||||
{
|
||||
if (total == 0) {
|
||||
return 100.0f;
|
||||
}
|
||||
return 100.0f * float(translated) / float(total);
|
||||
@ -126,30 +110,17 @@ struct Language
|
||||
total = translated + untranslated + fuzzy;
|
||||
}
|
||||
|
||||
bool isOfSameNameAs(const Language& other) const
|
||||
{
|
||||
return key == other.key;
|
||||
}
|
||||
bool isOfSameNameAs(const Language& other) const { return key == other.key; }
|
||||
|
||||
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 &&
|
||||
translated == other.translated &&
|
||||
fuzzy == other.fuzzy &&
|
||||
total == other.fuzzy &&
|
||||
localFileType == other.localFileType
|
||||
);
|
||||
return (key == other.key && file_name == other.file_name && file_size == other.file_size && file_sha1 == other.file_sha1 &&
|
||||
translated == other.translated && fuzzy == other.fuzzy && total == other.fuzzy && localFileType == other.localFileType);
|
||||
}
|
||||
|
||||
Language & apply(Language & other)
|
||||
Language& apply(Language& other)
|
||||
{
|
||||
if(!isOfSameNameAs(other))
|
||||
{
|
||||
if (!isOfSameNameAs(other)) {
|
||||
return *this;
|
||||
}
|
||||
file_name = other.file_name;
|
||||
@ -178,14 +149,11 @@ struct Language
|
||||
FileType localFileType = FileType::NONE;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct TranslationsModel::Private
|
||||
{
|
||||
struct TranslationsModel::Private {
|
||||
QDir m_dir;
|
||||
|
||||
// initial state is just english
|
||||
QVector<Language> m_languages = {Language (defaultLangCode)};
|
||||
QVector<Language> m_languages = { Language(defaultLangCode) };
|
||||
|
||||
QString m_selectedLanguage = defaultLangCode;
|
||||
std::unique_ptr<QTranslator> m_qt_translator;
|
||||
@ -198,7 +166,7 @@ struct TranslationsModel::Private
|
||||
QString m_nextDownload;
|
||||
|
||||
std::unique_ptr<POTranslator> m_po_translator;
|
||||
QFileSystemWatcher *watcher;
|
||||
QFileSystemWatcher* watcher;
|
||||
|
||||
const QString m_system_locale = QLocale::system().name();
|
||||
const QString m_system_language = m_system_locale.split('_').front();
|
||||
@ -206,7 +174,7 @@ struct TranslationsModel::Private
|
||||
bool no_language_set = false;
|
||||
};
|
||||
|
||||
TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent)
|
||||
TranslationsModel::TranslationsModel(QString path, QObject* parent) : QAbstractListModel(parent)
|
||||
{
|
||||
d.reset(new Private);
|
||||
d->m_dir.setPath(path);
|
||||
@ -218,15 +186,12 @@ TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractLi
|
||||
d->watcher->addPath(d->m_dir.canonicalPath());
|
||||
}
|
||||
|
||||
TranslationsModel::~TranslationsModel()
|
||||
{
|
||||
}
|
||||
TranslationsModel::~TranslationsModel() {}
|
||||
|
||||
void TranslationsModel::translationDirChanged(const QString& path)
|
||||
{
|
||||
qDebug() << "Dir changed:" << path;
|
||||
if (!d->no_language_set)
|
||||
{
|
||||
if (!d->no_language_set) {
|
||||
reloadLocalFiles();
|
||||
}
|
||||
selectLanguage(selectedLanguage());
|
||||
@ -237,69 +202,57 @@ void TranslationsModel::indexReceived()
|
||||
qDebug() << "Got translations index!";
|
||||
d->m_index_job.reset();
|
||||
|
||||
if (d->no_language_set)
|
||||
{
|
||||
if (d->no_language_set) {
|
||||
reloadLocalFiles();
|
||||
|
||||
auto language = d->m_system_locale;
|
||||
if (!findLanguage(language))
|
||||
{
|
||||
if (!findLanguage(language)) {
|
||||
language = d->m_system_language;
|
||||
}
|
||||
selectLanguage(language);
|
||||
if (selectedLanguage() != defaultLangCode)
|
||||
{
|
||||
if (selectedLanguage() != defaultLangCode) {
|
||||
updateLanguage(selectedLanguage());
|
||||
}
|
||||
APPLICATION->settings()->set("Language", selectedLanguage());
|
||||
d->no_language_set = false;
|
||||
}
|
||||
|
||||
else if(d->m_selectedLanguage != defaultLangCode)
|
||||
{
|
||||
else if (d->m_selectedLanguage != defaultLangCode) {
|
||||
downloadTranslation(d->m_selectedLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void readIndex(const QString & path, QMap<QString, Language>& languages)
|
||||
void readIndex(const QString& path, QMap<QString, Language>& languages)
|
||||
{
|
||||
QByteArray data;
|
||||
try
|
||||
{
|
||||
try {
|
||||
data = FS::read(path);
|
||||
} catch ([[maybe_unused]] const Exception& e) {
|
||||
qCritical() << "Translations Download Failed: index file not readable";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
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")
|
||||
{
|
||||
if (file_type != "MMC-TRANSLATION-INDEX") {
|
||||
qCritical() << "Translations Download Failed: index file is of unknown file type" << file_type;
|
||||
return;
|
||||
}
|
||||
auto version = Json::requireInteger(doc, "version");
|
||||
if(version > 2)
|
||||
{
|
||||
if (version > 2) {
|
||||
qCritical() << "Translations Download Failed: index file is of unknown format version" << file_type;
|
||||
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(
|
||||
Json::ensureInteger(langObj, "translated", 0),
|
||||
Json::ensureInteger(langObj, "untranslated", 0),
|
||||
Json::ensureInteger(langObj, "fuzzy", 0)
|
||||
);
|
||||
auto langObj = Json::requireObject(iter.value());
|
||||
lang.setTranslationStats(Json::ensureInteger(langObj, "translated", 0), Json::ensureInteger(langObj, "untranslated", 0),
|
||||
Json::ensureInteger(langObj, "fuzzy", 0));
|
||||
lang.file_name = Json::requireString(langObj, "file");
|
||||
lang.file_sha1 = Json::requireString(langObj, "sha1");
|
||||
lang.file_size = Json::requireInteger(langObj, "size");
|
||||
@ -310,47 +263,36 @@ void readIndex(const QString & path, QMap<QString, Language>& languages)
|
||||
qCritical() << "Translations Download Failed: index file could not be parsed as json";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void TranslationsModel::reloadLocalFiles()
|
||||
{
|
||||
QMap<QString, Language> languages = {{defaultLangCode, Language(defaultLangCode)}};
|
||||
QMap<QString, Language> languages = { { defaultLangCode, Language(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 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;
|
||||
if(completeSuffix == "qm")
|
||||
{
|
||||
langCode = entry.baseName().remove(0,4);
|
||||
if (completeSuffix == "qm") {
|
||||
langCode = entry.baseName().remove(0, 4);
|
||||
fileType = FileType::QM;
|
||||
}
|
||||
else if(completeSuffix == "po")
|
||||
{
|
||||
} else if (completeSuffix == "po") {
|
||||
langCode = entry.baseName();
|
||||
fileType = FileType::PO;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto langIter = languages.find(langCode);
|
||||
if(langIter != languages.end())
|
||||
{
|
||||
auto & language = *langIter;
|
||||
if(int(fileType) > int(language.localFileType))
|
||||
{
|
||||
if (langIter != languages.end()) {
|
||||
auto& language = *langIter;
|
||||
if (int(fileType) > int(language.localFileType)) {
|
||||
language.localFileType = fileType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fileType == FileType::PO)
|
||||
{
|
||||
} else {
|
||||
if (fileType == FileType::PO) {
|
||||
Language localFound(langCode);
|
||||
localFound.localFileType = FileType::PO;
|
||||
languages.insert(langCode, localFound);
|
||||
@ -359,52 +301,40 @@ void TranslationsModel::reloadLocalFiles()
|
||||
}
|
||||
|
||||
// changed and removed languages
|
||||
for(auto iter = d->m_languages.begin(); iter != d->m_languages.end();)
|
||||
{
|
||||
auto &language = *iter;
|
||||
for (auto iter = d->m_languages.begin(); iter != d->m_languages.end();) {
|
||||
auto& language = *iter;
|
||||
auto row = iter - d->m_languages.begin();
|
||||
|
||||
auto updatedLanguageIter = languages.find(language.key);
|
||||
if(updatedLanguageIter != languages.end())
|
||||
{
|
||||
if(language.isIdenticalTo(*updatedLanguageIter))
|
||||
{
|
||||
if (updatedLanguageIter != languages.end()) {
|
||||
if (language.isIdenticalTo(*updatedLanguageIter)) {
|
||||
languages.remove(language.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
language.apply(*updatedLanguageIter);
|
||||
emit dataChanged(index(row), index(row));
|
||||
languages.remove(language.key);
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
iter = d->m_languages.erase(iter);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
// added languages
|
||||
if(languages.isEmpty())
|
||||
{
|
||||
if (languages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
beginInsertRows(QModelIndex(), 0, d->m_languages.size() + languages.size() - 1);
|
||||
for(auto & language: languages)
|
||||
{
|
||||
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) {
|
||||
if (a.key != b.key)
|
||||
{
|
||||
if (a.key == d->m_system_locale || a.key == d->m_system_language)
|
||||
{
|
||||
if (a.key != b.key) {
|
||||
if (a.key == d->m_system_locale || a.key == d->m_system_language) {
|
||||
return true;
|
||||
}
|
||||
if (b.key == d->m_system_locale || b.key == d->m_system_language)
|
||||
{
|
||||
if (b.key == d->m_system_locale || b.key == d->m_system_language) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -414,14 +344,9 @@ void TranslationsModel::reloadLocalFiles()
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum class Column
|
||||
{
|
||||
Language,
|
||||
Completeness
|
||||
};
|
||||
enum class Column { Language, Completeness };
|
||||
}
|
||||
|
||||
|
||||
QVariant TranslationsModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
@ -433,62 +358,48 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const
|
||||
if (row < 0 || row >= d->m_languages.size())
|
||||
return QVariant();
|
||||
|
||||
auto & lang = d->m_languages[row];
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Column::Language:
|
||||
{
|
||||
return lang.languageName();
|
||||
}
|
||||
case Column::Completeness:
|
||||
{
|
||||
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
||||
auto& lang = d->m_languages[row];
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
switch (column) {
|
||||
case Column::Language: {
|
||||
return lang.languageName();
|
||||
}
|
||||
case Column::Completeness: {
|
||||
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
||||
}
|
||||
}
|
||||
qWarning("TranslationModel::data not implemented when role is DisplayRole");
|
||||
}
|
||||
qWarning("TranslationModel::data not implemented when role is DisplayRole");
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total").arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total));
|
||||
}
|
||||
case Qt::UserRole:
|
||||
return lang.key;
|
||||
default:
|
||||
return QVariant();
|
||||
case Qt::ToolTipRole: {
|
||||
return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total")
|
||||
.arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total));
|
||||
}
|
||||
case Qt::UserRole:
|
||||
return lang.key;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
auto column = static_cast<Column>(section);
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Column::Language:
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (column) {
|
||||
case Column::Language: {
|
||||
return tr("Language");
|
||||
}
|
||||
case Column::Completeness:
|
||||
{
|
||||
case Column::Completeness: {
|
||||
return tr("Completeness");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(role == Qt::ToolTipRole)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Column::Language:
|
||||
{
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
switch (column) {
|
||||
case Column::Language: {
|
||||
return tr("The native language name.");
|
||||
}
|
||||
case Column::Completeness:
|
||||
{
|
||||
case Column::Completeness: {
|
||||
return tr("Completeness is the percentage of fully translated strings, not counting automatically guessed ones.");
|
||||
}
|
||||
}
|
||||
@ -506,18 +417,12 @@ int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) c
|
||||
return 2;
|
||||
}
|
||||
|
||||
Language * TranslationsModel::findLanguage(const QString& key)
|
||||
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())
|
||||
{
|
||||
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
|
||||
{
|
||||
} else {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@ -562,10 +467,8 @@ bool TranslationsModel::selectLanguage(QString key)
|
||||
QLocale::setDefault(
|
||||
QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode));
|
||||
|
||||
|
||||
// if it's the default UI language, finish
|
||||
if(langCode == defaultLangCode)
|
||||
{
|
||||
if (langCode == defaultLangCode) {
|
||||
d->m_selectedLanguage = langCode;
|
||||
return true;
|
||||
}
|
||||
@ -574,70 +477,47 @@ bool TranslationsModel::selectLanguage(QString key)
|
||||
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)))
|
||||
{
|
||||
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()))
|
||||
{
|
||||
if (!QCoreApplication::installTranslator(d->m_qt_translator.get())) {
|
||||
qCritical() << "Loading Qt Language File failed.";
|
||||
d->m_qt_translator.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
d->m_qt_translator.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))
|
||||
{
|
||||
if (!poTranslator->isEmpty()) {
|
||||
if (!QCoreApplication::installTranslator(poTranslator)) {
|
||||
delete poTranslator;
|
||||
qCritical() << "Installing Application Language File failed.";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
d->m_app_translator.reset(poTranslator);
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
qCritical() << "Loading Application Language File failed.";
|
||||
d->m_app_translator.reset();
|
||||
}
|
||||
}
|
||||
else if(langPtr->localFileType == FileType::QM)
|
||||
{
|
||||
} 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()))
|
||||
{
|
||||
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()))
|
||||
{
|
||||
if (!QCoreApplication::installTranslator(d->m_app_translator.get())) {
|
||||
qCritical() << "Installing Application Language File failed.";
|
||||
d->m_app_translator.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
d->m_app_translator.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
d->m_app_translator.reset();
|
||||
}
|
||||
d->m_selectedLanguage = langCode;
|
||||
@ -647,8 +527,7 @@ bool TranslationsModel::selectLanguage(QString key)
|
||||
QModelIndex TranslationsModel::selectedIndex()
|
||||
{
|
||||
auto found = findLanguage(d->m_selectedLanguage);
|
||||
if(found)
|
||||
{
|
||||
if (found) {
|
||||
// QVector iterator freely converts to pointer to contained type
|
||||
return index(found - d->m_languages.begin(), 0, QModelIndex());
|
||||
}
|
||||
@ -662,8 +541,7 @@ QString TranslationsModel::selectedLanguage()
|
||||
|
||||
void TranslationsModel::downloadIndex()
|
||||
{
|
||||
if(d->m_index_job || d->m_dl_job)
|
||||
{
|
||||
if (d->m_index_job || d->m_dl_job) {
|
||||
return;
|
||||
}
|
||||
qDebug() << "Downloading Translations Index...";
|
||||
@ -680,33 +558,28 @@ void TranslationsModel::downloadIndex()
|
||||
|
||||
void TranslationsModel::updateLanguage(QString key)
|
||||
{
|
||||
if(key == defaultLangCode)
|
||||
{
|
||||
if (key == defaultLangCode) {
|
||||
qWarning() << "Cannot update builtin language" << key;
|
||||
return;
|
||||
}
|
||||
auto found = findLanguage(key);
|
||||
if(!found)
|
||||
{
|
||||
if (!found) {
|
||||
qWarning() << "Cannot update invalid language" << key;
|
||||
return;
|
||||
}
|
||||
if(!found->updated)
|
||||
{
|
||||
if (!found->updated) {
|
||||
downloadTranslation(key);
|
||||
}
|
||||
}
|
||||
|
||||
void TranslationsModel::downloadTranslation(QString key)
|
||||
{
|
||||
if(d->m_dl_job)
|
||||
{
|
||||
if (d->m_dl_job) {
|
||||
d->m_nextDownload = key;
|
||||
return;
|
||||
}
|
||||
auto lang = findLanguage(key);
|
||||
if(!lang)
|
||||
{
|
||||
if (!lang) {
|
||||
qWarning() << "Will not download an unknown translation" << key;
|
||||
return;
|
||||
}
|
||||
@ -731,8 +604,7 @@ void TranslationsModel::downloadTranslation(QString key)
|
||||
|
||||
void TranslationsModel::downloadNext()
|
||||
{
|
||||
if(!d->m_nextDownload.isEmpty())
|
||||
{
|
||||
if (!d->m_nextDownload.isEmpty()) {
|
||||
downloadTranslation(d->m_nextDownload);
|
||||
d->m_nextDownload.clear();
|
||||
}
|
||||
@ -749,8 +621,7 @@ void TranslationsModel::dlGood()
|
||||
{
|
||||
qDebug() << "Got translation:" << d->m_downloadingTranslation;
|
||||
|
||||
if(d->m_downloadingTranslation == d->m_selectedLanguage)
|
||||
{
|
||||
if (d->m_downloadingTranslation == d->m_selectedLanguage) {
|
||||
selectLanguage(d->m_selectedLanguage);
|
||||
}
|
||||
d->m_dl_job.reset();
|
||||
|
Reference in New Issue
Block a user