aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucie Gérard <lucie.gerard@qt.io>2023-10-10 15:33:48 +0200
committerLucie Gérard <lucie.gerard@qt.io>2023-10-26 14:12:00 +0200
commitd6e0d5630a49e9614a70bf960a213b3eff03a68e (patch)
tree1ecb2d4a5064328dfd4d55c27cdf87709470cef1
parent9187e07d36db8c262b51ba7d1ce35615b31ee918 (diff)
Add pragma syntax to support translation context
Translator pragma can be used to set the translation context instead of having the file name used [ChangeLog][qml][translation][Important Behavior Changes] The context for the translation can now be controled in a given file using pragma Translator. Task-number: QTBUG-114528 Change-Id: I6d9d7fb81ea969a90d8637d7277bdbe96c102088 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/common/qv4compileddata_p.h16
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp9
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h2
-rw-r--r--src/qml/compiler/qv4compiler.cpp4
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp13
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp5
-rw-r--r--tests/auto/qml/qqmltranslation/data/pragmacontext.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp23
8 files changed, 76 insertions, 4 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index be7dac9f34..1a97ddc5a5 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x3D // Reserve special value for "no translation context"
+#define QV4_DATA_STRUCTURE_VERSION 0x3E // Add Translator pragma
class QIODevice;
class QQmlTypeNameCache;
@@ -1331,6 +1331,20 @@ struct Unit
return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
}
+ const quint32_le *translationContextIndex() const{
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
+ quint32_le *translationContextIndex() {
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<quint32_le*>((reinterpret_cast<char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 2d1a617b09..96529f0061 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -921,6 +921,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node)
} else if (node->name == "ValueTypeBehavior"_L1) {
if (!PragmaParser<Pragma::ValueTypeBehaviorValue>::run(this, node, pragma))
return false;
+ } else if (node->name == "Translator"_L1) {
+ pragma->type = Pragma::Translator;
+ pragma->translationContextIndex = registerString(node->values->value.toString());
+
} else {
recordError(node->pragmaToken, QCoreApplication::translate(
"QQmlParser", "Unknown pragma '%1'").arg(node->name));
@@ -1711,6 +1715,11 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
createdUnit->flags |= Unit::ValueTypesAddressable;
}
break;
+ case Pragma::Translator:
+ if (createdUnit->translationTableSize)
+ if (quint32_le *index = createdUnit->translationContextIndex())
+ *index = p->translationContextIndex;
+ break;
}
}
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 8083fd752c..aad2f2fdbe 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -411,6 +411,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
FunctionSignatureBehavior,
NativeMethodBehavior,
ValueTypeBehavior,
+ Translator,
};
enum ListPropertyAssignBehaviorValue
@@ -453,6 +454,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
FunctionSignatureBehaviorValue functionSignatureBehavior;
NativeMethodBehaviorValue nativeMethodBehavior;
ValueTypeBehaviorValues::Int valueTypeBehavior;
+ uint translationContextIndex;
};
QV4::CompiledData::Location location;
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 34c62cc992..7a7c8f621b 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -682,6 +682,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.translationTableSize = translations.size();
unit.offsetToTranslationTable = nextOffset;
nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
+ if (unit.translationTableSize != 0) {
+ constexpr auto spaceForTranslationContextId = sizeof(quint32_le);
+ nextOffset += spaceForTranslationContextId;
+ }
nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index bd43d3cb97..e71568b2d8 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -976,11 +976,18 @@ QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) con
const bool hasContext
= translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex;
+ QByteArray context;
+ if (hasContext) {
+ context = stringAt(translation.contextIndex).toUtf8();
+ } else {
+ auto pragmaTranslationContext = data->translationContextIndex();
+ context = stringAt(*pragmaTranslationContext).toUtf8();
+ context = context.isEmpty() ? fileContext() : context;
+ }
+
QByteArray comment = stringAt(translation.commentIndex).toUtf8();
QByteArray text = stringAt(translation.stringIndex).toUtf8();
- return QCoreApplication::translate(
- hasContext ? stringAt(translation.contextIndex).toUtf8() : fileContext(),
- text, comment, translation.number);
+ return QCoreApplication::translate(context, text, comment, translation.number);
#endif
}
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 1434cffe31..eccf5804cb 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -2171,6 +2171,11 @@ QString GlobalExtensions::currentTranslationContext(ExecutionEngine *engine)
while (frame && context.isEmpty()) {
if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
+ auto translationContextIndex = unit->data->translationContextIndex();
+ if (translationContextIndex)
+ context = unit->stringAt(*translationContextIndex);
+ if (!context.isEmpty())
+ break;
QString fileName = unit->fileName();
QUrl url(unit->fileName());
if (url.isValid() && url.isRelative()) {
diff --git a/tests/auto/qml/qqmltranslation/data/pragmacontext.qml b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml
new file mode 100644
index 0000000000..e158cfe4bf
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+pragma Translator: contextSetWithPragma
+
+QtObject {
+ property string german1: qsTr("English in translation")
+ property string german2: qsTranslate("setContext","English in translation")
+}
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index edc19edf37..e6e715d006 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -24,6 +24,7 @@ private slots:
void idTranslation();
void translationChange();
void preferJSContext();
+ void pragmaContext();
void listModel();
};
@@ -175,6 +176,10 @@ class DummyTranslator : public QTranslator
return QString::fromUtf8("Deutsch in mylibrary");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation"))
return QString::fromUtf8("Deutsch in Setzung");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragma"))
+ return QString::fromUtf8("Deutsch in Setzung pragma");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "setContext"))
+ return QString::fromUtf8("Deutsch in Setzung set");
if (!qstrcmp(sourceText, "soup"))
return QString::fromUtf8("Suppe");
if (!qstrcmp(sourceText, "fish"))
@@ -246,6 +251,24 @@ void tst_qqmltranslation::preferJSContext()
QCoreApplication::removeTranslator(&translator);
}
+void tst_qqmltranslation::pragmaContext()
+{
+ DummyTranslator translator;
+ QCoreApplication::installTranslator(&translator);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("pragmacontext.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("german1").toString(),
+ QStringLiteral("Deutsch in Setzung pragma"));
+ QCOMPARE(object->property("german2").toString(),
+ QStringLiteral("Deutsch in Setzung set"));
+
+ QCoreApplication::removeTranslator(&translator);
+}
+
void tst_qqmltranslation::listModel()
{
QQmlEngine engine;