diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
| commit | d3261e64152397db2dca4d691a990c6bc2a6f4dd (patch) | |
| tree | fac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/launcher/translations/POTranslator.cpp | |
| parent | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff) | |
| download | Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip | |
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/launcher/translations/POTranslator.cpp')
| -rw-r--r-- | archived/projt-launcher/launcher/translations/POTranslator.cpp | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/translations/POTranslator.cpp b/archived/projt-launcher/launcher/translations/POTranslator.cpp new file mode 100644 index 0000000000..6e893097d2 --- /dev/null +++ b/archived/projt-launcher/launcher/translations/POTranslator.cpp @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "POTranslator.h" + +#include <QDebug> +#include "FileSystem.h" + +struct POEntry +{ + QString text; + bool fuzzy; +}; + +struct POTranslatorPrivate +{ + QString filename; + QHash<QByteArray, POEntry> mapping; + QHash<QByteArray, POEntry> mapping_disambiguatrion; + bool loaded = false; + + void reload(); +}; + +class ParserArray : public QByteArray +{ + public: + ParserArray(const QByteArray& in) : QByteArray(in) + {} + bool chomp(const char* data, int length) + { + if (startsWith(data)) + { + remove(0, length); + return true; + } + return false; + } + bool chompString(QByteArray& appendHere) + { + QByteArray msg; + bool escape = false; + if (size() < 2) + { + qDebug() << "String fragment is too short"; + return false; + } + if (!startsWith('"')) + { + qDebug() << "String fragment does not start with \""; + return false; + } + 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++) + { + char c = operator[](i); + if (escape) + { + switch (c) + { + case 'r': msg += '\r'; break; + case 'n': msg += '\n'; break; + case 't': msg += '\t'; break; + case 'v': msg += '\v'; break; + case 'a': msg += '\a'; break; + case 'b': msg += '\b'; break; + case 'f': msg += '\f'; break; + case '"': msg += '"'; break; + case '\\': msg.append('\\'); break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int octal_start = i; + while ((c = operator[](i)) >= '0' && c <= '7') + { + i++; + if (i == length() - 1) + { + qDebug() << "Something went bad while parsing an octal escape string..."; + return false; + } + } + msg += mid(octal_start, i - octal_start).toUInt(0, 8); + break; + } + case 'x': + { + // chomp the 'x' + i++; + int hex_start = i; + while (isxdigit(operator[](i))) + { + i++; + if (i == length() - 1) + { + qDebug() << "Something went bad while parsing a hex escape string..."; + return false; + } + } + msg += mid(hex_start, i - hex_start).toUInt(0, 16); + break; + } + default: + { + qDebug() << "Invalid escape sequence character:" << c; + return false; + } + } + escape = false; + } + else if (c == '\\') + { + escape = true; + } + else + { + msg += c; + } + } + if (escape) + { + qDebug() << "Unterminated escape sequence..."; + return false; + } + appendHere += msg; + return true; + } +}; + +void POTranslatorPrivate::reload() +{ + QFile file(filename); + if (!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text)) + { + qDebug() << "Failed to open PO file:" << filename; + return; + } + + QByteArray context; + QByteArray disambiguation; + QByteArray id; + QByteArray str; + bool fuzzy = false; + bool nextFuzzy = false; + + enum class Mode + { + First, + MessageContext, + MessageId, + MessageString + } mode = Mode::First; + + int lineNumber = 0; + QHash<QByteArray, POEntry> newMapping; + QHash<QByteArray, POEntry> newMapping_disambiguation; + auto endEntry = [&]() + { + auto strStr = QString::fromUtf8(str); + // NOTE: PO header has empty id. We skip it. + if (!id.isEmpty()) + { + auto normalKey = context + "|" + id; + newMapping.insert(normalKey, { strStr, fuzzy }); + if (!disambiguation.isEmpty()) + { + auto disambiguationKey = context + "|" + id + "@" + disambiguation; + newMapping_disambiguation.insert(disambiguationKey, { strStr, fuzzy }); + } + } + context.clear(); + disambiguation.clear(); + id.clear(); + str.clear(); + fuzzy = nextFuzzy; + nextFuzzy = false; + }; + while (!file.atEnd()) + { + ParserArray line = file.readLine(); + if (line.endsWith('\n')) + { + line.resize(line.size() - 1); + } + if (line.endsWith('\r')) + { + line.resize(line.size() - 1); + } + + if (!line.size()) + { + // NIL + } + else if (line[0] == '#') + { + if (line.contains(", fuzzy")) + { + nextFuzzy = true; + } + } + else if (line.startsWith('"')) + { + QByteArray temp; + QByteArray* out = &temp; + + switch (mode) + { + case Mode::First: + qDebug() << "Unexpected escaped string during initial state... line:" << lineNumber; + return; + case Mode::MessageString: out = &str; break; + case Mode::MessageContext: out = &context; break; + case Mode::MessageId: out = &id; break; + } + if (!line.chompString(*out)) + { + qDebug() << "Badly formatted string on line:" << lineNumber; + return; + } + } + else if (line.chomp("msgctxt ", 8)) + { + switch (mode) + { + case Mode::First: break; + case Mode::MessageString: endEntry(); break; + case Mode::MessageContext: + case Mode::MessageId: qDebug() << "Unexpected msgctxt line:" << lineNumber; return; + } + if (line.chompString(context)) + { + auto parts = context.split('|'); + context = parts[0]; + if (parts.size() > 1 && !parts[1].isEmpty()) + { + disambiguation = parts[1]; + } + mode = Mode::MessageContext; + } + } + else if (line.chomp("msgid ", 6)) + { + switch (mode) + { + case Mode::MessageContext: + case Mode::First: break; + case Mode::MessageString: endEntry(); break; + case Mode::MessageId: qDebug() << "Unexpected msgid line:" << lineNumber; return; + } + if (line.chompString(id)) + { + mode = Mode::MessageId; + } + } + else if (line.chomp("msgstr ", 7)) + { + switch (mode) + { + case Mode::First: + case Mode::MessageString: + case Mode::MessageContext: qDebug() << "Unexpected msgstr line:" << lineNumber; return; + case Mode::MessageId: break; + } + if (line.chompString(str)) + { + mode = Mode::MessageString; + } + } + else + { + qDebug() << "I did not understand line: " << lineNumber << ":" << QString::fromUtf8(line); + } + lineNumber++; + } + endEntry(); + mapping = std::move(newMapping); + mapping_disambiguatrion = std::move(newMapping_disambiguation); + loaded = true; +} + +POTranslator::POTranslator(const QString& filename, QObject* parent) : QTranslator(parent) +{ + d = new POTranslatorPrivate; + d->filename = filename; + d->reload(); +} + +POTranslator::~POTranslator() +{ + delete d; +} + +QString POTranslator::translate(const char* context, + const char* sourceText, + const char* disambiguation, + [[maybe_unused]] int n) const +{ + 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()) + { + qDebug() << "Translation entry has no content:" << disambiguationKey; + } + if (entry.fuzzy) + { + qDebug() << "Translation entry is fuzzy:" << disambiguationKey << "->" << entry.text; + } + return entry.text; + } + } + auto key = QByteArray(context) + "|" + QByteArray(sourceText); + auto iter = d->mapping.find(key); + if (iter != d->mapping.end()) + { + auto& entry = *iter; + if (entry.text.isEmpty()) + { + qDebug() << "Translation entry has no content:" << key; + } + if (entry.fuzzy) + { + qDebug() << "Translation entry is fuzzy:" << key << "->" << entry.text; + } + return entry.text; + } + return QString(); +} + +bool POTranslator::isEmpty() const +{ + return !d->loaded; +} |
