Source code
Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "translationutils.h"
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <optional>
using namespace Qt::Literals::StringLiterals;
static std::optional<QJsonArray> findJsonArray(const QJsonValue &jval, const QString &key)
{
if (jval.isObject()) {
const QJsonObject obj = jval.toObject();
auto it = obj.find(key);
if (it != obj.end() && it->isArray())
return it->toArray();
for (it = obj.constBegin(); it != obj.constEnd(); ++it) {
if (it.key().trimmed() == key && it.value().isArray())
return it.value().toArray();
if (const auto r = findJsonArray(it.value(), key); r)
return r;
}
} else if (jval.isArray()) {
const QJsonArray arr = jval.toArray();
for (const QJsonValue &element : arr)
if (const auto r = findJsonArray(element, key); r)
return r;
} else if (jval.isString()) {
QString str = jval.toString();
const int startIdx = str.indexOf('{'_L1);
const int endIdx = str.lastIndexOf('}'_L1);
if (startIdx < 0 || endIdx < 0)
return {};
str.slice(startIdx, endIdx - startIdx + 1);
QJsonParseError err;
auto inner = QJsonDocument::fromJson(str.toUtf8(), &err);
if (err.error != QJsonParseError::NoError || !inner.isObject())
return {};
const auto obj = inner.object();
if (auto it = obj.find(key); it != obj.end()) {
if (it.value().isArray())
return it.value().toArray();
}
}
return {};
}
QT_BEGIN_NAMESPACE
QHash<QString, QString> extractKeyValuePairs(const QJsonValue &jval, const QString &arrayKey)
{
auto array = findJsonArray(jval, arrayKey);
if (!array)
return {};
QHash<QString, QString> out;
out.reserve(array->size());
for (const QJsonValue &v : std::as_const(*array)) {
if (v.isObject()) {
const QJsonObject obj = v.toObject();
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
if (it.value().isString())
out[it.key()] = it.value().toString();
}
}
}
return out;
}
QHash<QString, QStringList> extractPluralTranslations(const QJsonValue &jval,
const QString &arrayKey)
{
auto array = findJsonArray(jval, arrayKey);
if (!array)
return {};
QHash<QString, QStringList> out;
out.reserve(array->size());
for (const QJsonValue &v : std::as_const(*array)) {
if (v.isObject()) {
const QJsonObject obj = v.toObject();
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
if (it.value().isArray()) {
QStringList forms;
const QJsonArray arr = it.value().toArray();
forms.reserve(arr.size());
for (const QJsonValue &form : arr) {
if (form.isString())
forms.append(form.toString());
}
if (!forms.isEmpty())
out[it.key()] = forms;
}
}
}
}
return out;
}
QString translationSystemPrompt()
{
static QString systemPrompt = uR"(
You are a professional software translator specialized in Qt UI strings.
When given a list of items of the given 'Context', each may include:
- source: the original text to translate
- comment: an optional developer note for more context
If "Application Context" is provided, use it to understand the domain and terminology
appropriate for the application (e.g., medical, financial, gaming) to produce more
accurate and contextually appropriate translations.
Translate the items into the **target language** specified by the user,
preserving keyboard accelerators (e.g. "&File"), placeholders (e.g. "%1"),
and ending punctuation.
RESULT FORMAT (MUST FOLLOW):
A single JSON object with one key, "Translations",
whose value is an array of objects.
Each object maps the original source string to translated string:
Two examples:
Input:
Context: MainWindow
Target: German
Items:
- source: "File"
- source: "Exit"
- source: "&Open", comment: "opens a document"
Output:
{"Translations":[{"File":"Datei"},{"Exit":"Beenden"},{"&Open":"&Öffnen"}]}
Input:
Context: MainWindow
Target: French
Items:
– source: "File"
– source: "Exit"
Output:
{"Translations":[{"File":"Fichier"},{"Exit":"Quitter"}]}
Return **only** valid JSON, no code fences, no extra text.
After generating and before returning, verify:
1. Every string is in the target language; if any aren't, correct them before returning.
2. Every JSON key exactly matches one of the input source strings.
3. No key equals its value.
4. Every string is translated
)"_s;
return systemPrompt;
}
QString pluralTranslationSystemPrompt()
{
static QString systemPrompt = uR"PROMPT(
You are a professional software translator specialized in Qt UI strings with plural forms.
When given a list of items, each may include:
- source: the original text to translate (contains a placeholder like %n for the count)
- comment: an optional developer note for more context
The user will specify:
- The target language
- The number of plural forms required for that language
You must provide exactly the specified number of plural forms for each source string.
Each plural form corresponds to different grammatical number categories
(e.g., "one", "few", "many", "other" depending on the language).
Preserve keyboard accelerators (e.g. "&File"), placeholders (e.g. "%1", "%n"),
and ending punctuation in all plural forms.
RESULT FORMAT (MUST FOLLOW):
A single JSON object with one key, "Plurals",
whose value is an array of objects.
Each object maps the original source string to an array of translated plural forms:
Example for German (2 plural forms):
Input:
Target: German
Plural forms: 2
Items:
- source: "%n file(s) selected"
- source: "%n item(s) deleted"
Output:
{"Plurals":[{"%n file(s) selected":["%n Datei ausgewählt","%n Dateien ausgewählt"]},{"%n item(s) deleted":["%n Element gelöscht","%n Elemente gelöscht"]}]}
Example for Polish (3 plural forms):
Input:
Target: Polish
Plural forms: 3
Items:
- source: "%n file(s)"
Output:
{"Plurals":[{"%n file(s)":["%n plik","%n pliki","%n plików"]}]}
Return **only** valid JSON, no code fences, no extra text.
After generating and before returning, verify:
1. Every string is in the target language.
2. Every JSON key exactly matches one of the input source strings.
3. Each source has exactly the specified number of plural forms.
4. The %n placeholder is preserved in all forms.
)PROMPT"_s;
return systemPrompt;
}
QT_END_NAMESPACE