diff --git a/lib/prifiles/buildmaster.pri b/lib/prifiles/buildmaster.pri
index 4fbb73e..30b64c0 100644
--- a/lib/prifiles/buildmaster.pri
+++ b/lib/prifiles/buildmaster.pri
@@ -6,3 +6,6 @@ include(installtranslations.pri)
# Check if this is a Blueprint build
include(checkblueprint.pri)
+
+# Generate .desktop translations
+include(processdesktoptranslations.pri)
diff --git a/lib/prifiles/gentranslations.pri b/lib/prifiles/gentranslations.pri
index afc5ded..58c4d12 100644
--- a/lib/prifiles/gentranslations.pri
+++ b/lib/prifiles/gentranslations.pri
@@ -29,4 +29,5 @@ isEmpty(SKIP_GENTRANSLATION) {
}
DISTFILES += \
- $$PWD/checkblueprint.pri
+ $$PWD/checkblueprint.pri \
+ $$PWD/processdesktoptranslations.pri
diff --git a/lib/prifiles/processdesktoptranslations.pri b/lib/prifiles/processdesktoptranslations.pri
new file mode 100644
index 0000000..a31e849
--- /dev/null
+++ b/lib/prifiles/processdesktoptranslations.pri
@@ -0,0 +1,33 @@
+unix:!macx {
+ desktopgen.output = ${QMAKE_FILE_BASE}.desktop
+ desktopgen.commands = tltrdesktop --desktop-generate --desktop-template ${QMAKE_FILE_NAME} --json-directory $$_PRO_FILE_PWD_/translations/desktop/${QMAKE_FILE_NAME}/ --desktop-output ${QMAKE_FILE_OUT}
+ desktopgen.input = DESKTOP_FILE
+ desktopgen.CONFIG = no_link target_predeps
+ desktopgen.variable_out = DESKTOP_FILE_GENERATED
+
+ desktopgenbp.output = ${QMAKE_FILE_BASE}.desktop
+ desktopgenbp.commands = tltrdesktop --desktop-generate --desktop-template ${QMAKE_FILE_NAME} --json-directory $$_PRO_FILE_PWD_/translations/desktop/${QMAKE_FILE_NAME}/ --desktop-output ${QMAKE_FILE_OUT}
+ desktopgenbp.input = DESKTOP_FILE_BLUEPRINT
+ desktopgenbp.CONFIG = no_link target_predeps
+ desktopgenbp.variable_out = DESKTOP_FILE_BLUEPRINT_GENERATED
+
+ QMAKE_EXTRA_COMPILERS = desktopgen desktopgenbp
+
+ !isEmpty(DESKTOP_FILE):!blueprint {
+ desktop.files = DESKTOP_FILE_GENERATED
+ desktop.path = /usr/share/applications
+ desktop.extra = $$QMAKE_COPY $${OUT_PWD}/$$DESKTOP_FILE $(INSTALL_ROOT)/usr/share/applications
+ INSTALLS += desktop
+
+ message(Will install $$DESKTOP_FILE as the desktop file)
+ }
+
+ !isEmpty(DESKTOP_FILE_BLUEPRINT):blueprint {
+ desktop.files = DESKTOP_FILE_GENERATED_BLUEPRINT
+ desktop.path = /usr/share/applications
+ desktop.extra = $$QMAKE_COPY $${OUT_PWD}/$$DESKTOP_FILE_BLUEPRINT $(INSTALL_ROOT)/usr/share/applications
+ INSTALLS += desktop
+
+ message(Will install $$DESKTOP_FILE_BLUEPRINT as the desktop file)
+ }
+}
diff --git a/the-libs.pro b/the-libs.pro
index 8fa8323..b190c41 100644
--- a/the-libs.pro
+++ b/the-libs.pro
@@ -7,6 +7,10 @@ unittest.depends = library
SUBDIRS += library
+unix:!macx {
+ SUBDIRS += tltrdesktop
+}
+
#!android {
# unittest
#}
diff --git a/tltrdesktop/desktopfile.cpp b/tltrdesktop/desktopfile.cpp
new file mode 100644
index 0000000..335da03
--- /dev/null
+++ b/tltrdesktop/desktopfile.cpp
@@ -0,0 +1,102 @@
+/****************************************
+ *
+ * INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
+ * Copyright (C) 2021 Victor Tran
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see .
+ *
+ * *************************************/
+#include "desktopfile.h"
+
+#include
+#include
+
+struct DesktopFilePrivate {
+ QStringList lines;
+ QStringList outputLines;
+ int currentLine = -1;
+
+ bool lineWritten = true;
+
+ QString currentLineText();
+};
+
+DesktopFile::DesktopFile(QString file) {
+ d = new DesktopFilePrivate();
+
+ QFile f(file);
+ f.open(QFile::ReadOnly);
+ d->lines = QString(f.readAll()).split('\n');
+}
+
+DesktopFile::~DesktopFile() {
+ delete d;
+}
+
+bool DesktopFile::nextLine() {
+ if (!d->lineWritten) {
+ //Copy this line verbatim to the output if it's not a translator's comment
+ if (!d->currentLineText().startsWith("#T:")) d->outputLines.append(d->currentLineText());
+ }
+
+ d->currentLine++;
+ d->lineWritten = false;
+ return d->lines.count() > d->currentLine;
+}
+
+bool DesktopFile::isKeyValue() {
+ return d->currentLineText().contains("=");
+}
+
+QString DesktopFile::key() {
+ QString key = d->currentLineText().left(d->currentLineText().indexOf("="));
+ if (key.endsWith("[]")) key.chop(2);
+ return key;
+}
+
+QString DesktopFile::value() {
+ return d->currentLineText().mid(d->currentLineText().indexOf("=") + 1);
+}
+
+QString DesktopFile::comment() {
+ if (d->currentLine == 0) return "";
+ QString previousLine = d->lines.value(d->currentLine - 1);
+ if (!previousLine.startsWith("#T:")) return "";
+ return previousLine.mid(3);
+}
+
+bool DesktopFile::isLineTranslatable() {
+ if (!isKeyValue()) return false;
+ return d->currentLineText().left(d->currentLineText().indexOf("=")).endsWith("[]");
+}
+
+void DesktopFile::insert(QString key, QString value, QString locale) {
+ QString line = QStringLiteral("%1%2=%3").arg(key);
+ if (locale.isEmpty()) {
+ line = line.arg("");
+ } else {
+ line = line.arg(QStringLiteral("[%1]").arg(locale));
+ }
+ line = line.arg(value);
+ d->outputLines.append(line);
+ d->lineWritten = true;
+}
+
+QString DesktopFile::outputText() {
+ return d->outputLines.join("\n");
+}
+
+QString DesktopFilePrivate::currentLineText() {
+ return lines.at(currentLine);
+}
diff --git a/tltrdesktop/desktopfile.h b/tltrdesktop/desktopfile.h
new file mode 100644
index 0000000..0419a9c
--- /dev/null
+++ b/tltrdesktop/desktopfile.h
@@ -0,0 +1,46 @@
+/****************************************
+ *
+ * INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
+ * Copyright (C) 2021 Victor Tran
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see .
+ *
+ * *************************************/
+#ifndef DESKTOPFILE_H
+#define DESKTOPFILE_H
+
+#include
+
+struct DesktopFilePrivate;
+class DesktopFile {
+ public:
+ DesktopFile(QString file);
+ ~DesktopFile();
+
+ bool nextLine();
+ bool isKeyValue();
+ QString key();
+ QString value();
+ QString comment();
+ bool isLineTranslatable();
+
+ void insert(QString key, QString value, QString locale = "");
+
+ QString outputText();
+
+ private:
+ DesktopFilePrivate* d;
+};
+
+#endif // DESKTOPFILE_H
diff --git a/tltrdesktop/jsonfile.cpp b/tltrdesktop/jsonfile.cpp
new file mode 100644
index 0000000..3b56b11
--- /dev/null
+++ b/tltrdesktop/jsonfile.cpp
@@ -0,0 +1,80 @@
+/****************************************
+ *
+ * INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
+ * Copyright (C) 2021 Victor Tran
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see .
+ *
+ * *************************************/
+#include "jsonfile.h"
+
+#include
+#include
+#include
+
+struct JsonFilePrivate {
+ uint refcount = 0;
+ QJsonObject rootObject;
+};
+
+JsonFile::JsonFile() {
+ d = new JsonFilePrivate();
+ d->refcount++;
+}
+
+JsonFile::JsonFile(QString file) {
+ d = new JsonFilePrivate();
+ d->refcount++;
+
+ QFile f(file);
+ f.open(QFile::ReadOnly);
+ d->rootObject = QJsonDocument::fromJson(f.readAll()).object();
+}
+
+JsonFile::JsonFile(const JsonFile& other) {
+ this->d = other.d;
+ d->refcount++;
+}
+
+JsonFile::~JsonFile() {
+ d->refcount--;
+ if (d->refcount == 0) delete d;
+}
+
+void JsonFile::insertString(QString key, QString message, QString description) {
+ QJsonObject stringObject;
+ stringObject.insert("message", message);
+ if (!description.isEmpty()) stringObject.insert("description", description);
+ d->rootObject.insert(key, stringObject);
+}
+
+QString JsonFile::message(QString key) const {
+ QJsonValue stringValue = d->rootObject.value(key);
+ if (!stringValue.isObject()) return "";
+
+ return stringValue.toObject().value("message").toString();
+}
+
+QByteArray JsonFile::output() {
+ return QJsonDocument(d->rootObject).toJson();
+}
+
+JsonFile& JsonFile::operator =(const JsonFile& other) {
+ d->refcount--;
+ if (d->refcount == 0) delete d;
+
+ d = other.d;
+ d->refcount++;
+ return *this;
+}
diff --git a/tltrdesktop/jsonfile.h b/tltrdesktop/jsonfile.h
new file mode 100644
index 0000000..2396ab2
--- /dev/null
+++ b/tltrdesktop/jsonfile.h
@@ -0,0 +1,45 @@
+/****************************************
+ *
+ * INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
+ * Copyright (C) 2021 Victor Tran
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see .
+ *
+ * *************************************/
+#ifndef JSONFILE_H
+#define JSONFILE_H
+
+#include
+
+struct JsonFilePrivate;
+
+class JsonFile {
+ public:
+ JsonFile();
+ JsonFile(QString file);
+ JsonFile(const JsonFile& other);
+ ~JsonFile();
+
+ void insertString(QString key, QString message, QString description = "");
+ QString message(QString key) const;
+
+ QByteArray output();
+
+ JsonFile& operator=(const JsonFile& other);
+
+ private:
+ JsonFilePrivate* d;
+};
+
+#endif // JSONFILE_H
diff --git a/tltrdesktop/main.cpp b/tltrdesktop/main.cpp
new file mode 100644
index 0000000..75a0d57
--- /dev/null
+++ b/tltrdesktop/main.cpp
@@ -0,0 +1,136 @@
+/****************************************
+ *
+ * INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
+ * Copyright (C) 2021 Victor Tran
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see .
+ *
+ * *************************************/
+#include
+#include
+#include
+#include
+
+#include "desktopfile.h"
+#include "jsonfile.h"
+
+void genJsonFile(QString desktopFile, QDir outputDirectory) {
+ QTextStream output(stdout);
+
+ JsonFile file;
+ DesktopFile desktop(desktopFile);
+ while (desktop.nextLine()) {
+ if (desktop.isLineTranslatable()) {
+ //Export this line to the translation file
+ file.insertString(desktop.key(), desktop.value(), desktop.comment());
+ }
+ }
+
+ //Write the output to en.json
+ QString outputFile = outputDirectory.absoluteFilePath("en.json");
+ output << "Generating file " << outputFile << "...\n";
+
+ QFile f(outputFile);
+ f.open(QFile::WriteOnly);
+ f.write(file.output());
+ f.close();
+}
+
+void genDesktopFile(QString templateFile, QDir jsonDirectory, QString outputFile) {
+ QTextStream output(stdout);
+ QMap jsonFiles;
+ for (const QFileInfo& file : jsonDirectory.entryInfoList({"*.json"}, QDir::Files)) {
+ JsonFile jsonFile(file.absoluteFilePath());
+ jsonFiles.insert(file.baseName(), jsonFile);
+ }
+
+ DesktopFile desktop(templateFile);
+ while (desktop.nextLine()) {
+ if (desktop.isLineTranslatable()) {
+ //Insert the translations into the desktop file
+ //First, insert the translation without a locale
+ desktop.insert(desktop.key(), desktop.value());
+
+ for (const QString& locale : jsonFiles.keys()) {
+ QString value = jsonFiles.value(locale).message(desktop.key());
+ desktop.insert(desktop.key(), value, locale);
+ }
+ }
+ }
+
+ //Output to the output file
+ output << "Generating file " << outputFile << "...\n";
+
+ QFile f(outputFile);
+ f.open(QFile::WriteOnly);
+ f.write(desktop.outputText().toUtf8());
+ f.close();
+}
+
+int main(int argc, char* argv[]) {
+ QCoreApplication a(argc, argv);
+
+ QCommandLineParser parser;
+ parser.addOption({{"j", "json-generate"}, "Generate JSON Translation files from .desktop files"});
+ parser.addOption({{"k", "desktop-generate"}, "Generate .desktop files from JSON Translation files"});
+ parser.addOption({{"d", "json-directory"}, "Directory of JSON Translation files", "json-directory"});
+ parser.addOption({{"t", "desktop-template"}, ".desktop template file", "desktop template"});
+ parser.addOption({{"o", "desktop-output"}, ".desktop output file", "desktop output"});
+ QCommandLineOption helpOption = parser.addHelpOption();
+ parser.parse(a.arguments());
+
+ QTextStream output(stdout);
+ QTextStream eoutput(stderr);
+ if (parser.isSet(helpOption)) {
+ output << parser.helpText() << "\n";
+ return 0;
+ }
+
+ bool genJson = parser.isSet("json-generate");
+ bool genDesktop = parser.isSet("desktop-generate");
+ if (!genJson && !genDesktop) {
+ eoutput << "error: At least one of --json-generate or --desktop-generate must be specified.\n";
+ return 1;
+ }
+
+ if (!parser.isSet("desktop-template")) {
+ eoutput << "error: A desktop template file must be specified with --desktop-template.\n";
+ return 1;
+ }
+
+ if (!parser.isSet("json-directory")) {
+ eoutput << "error: A JSON output directory must be specified with --json-directory.\n";
+ return 1;
+ }
+
+ if (!parser.isSet("desktop-output") && genDesktop) {
+ eoutput << "error: A .desktop output file must be specified with --desktop-output.\n";
+ return 1;
+ }
+
+ QString desktopTemplate = parser.value("desktop-template");
+ QDir outputDirectory(parser.value("json-directory"));
+ if (!outputDirectory.exists()) outputDirectory.mkpath(".");
+
+ if (genJson) {
+ //Generate the en JSON file from a .desktop file
+ genJsonFile(desktopTemplate, outputDirectory);
+ }
+ if (genDesktop) {
+ //Generate the final .desktop file from JSON files
+ genDesktopFile(desktopTemplate, outputDirectory, parser.value("desktop-output"));
+ }
+
+ return 0;
+}
diff --git a/tltrdesktop/tltrdesktop.pro b/tltrdesktop/tltrdesktop.pro
new file mode 100644
index 0000000..ca2f549
--- /dev/null
+++ b/tltrdesktop/tltrdesktop.pro
@@ -0,0 +1,22 @@
+QT -= gui
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ desktopfile.cpp \
+ jsonfile.cpp \
+ main.cpp
+
+unix:!macx {
+ target.path = /usr/bin
+ INSTALLS += target
+}
+
+HEADERS += \
+ desktopfile.h \
+ jsonfile.h