From d6e0d5630a49e9614a70bf960a213b3eff03a68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucie=20G=C3=A9rard?= Date: Tue, 10 Oct 2023 15:33:48 +0200 Subject: 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 --- src/qml/common/qv4compileddata_p.h | 16 ++++++++++++++- src/qml/compiler/qqmlirbuilder.cpp | 9 +++++++++ src/qml/compiler/qqmlirbuilder_p.h | 2 ++ src/qml/compiler/qv4compiler.cpp | 4 ++++ src/qml/jsruntime/qv4executablecompilationunit.cpp | 13 +++++++++--- src/qml/qml/qqmlbuiltinfunctions.cpp | 5 +++++ .../qml/qqmltranslation/data/pragmacontext.qml | 8 ++++++++ .../qml/qqmltranslation/tst_qqmltranslation.cpp | 23 ++++++++++++++++++++++ 8 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qqmltranslation/data/pragmacontext.qml 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(reinterpret_cast(this) + offsetToTranslationTable); } + const quint32_le *translationContextIndex() const{ + if ( translationTableSize == 0) + return nullptr; + return reinterpret_cast((reinterpret_cast(this)) + + offsetToTranslationTable + + translationTableSize * sizeof(CompiledData::TranslationData)); } + + quint32_le *translationContextIndex() { + if ( translationTableSize == 0) + return nullptr; + return reinterpret_cast((reinterpret_cast(this)) + + offsetToTranslationTable + + translationTableSize * sizeof(CompiledData::TranslationData)); } + const ImportEntry *importEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToImportEntryTable); } const ExportEntry *localExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToLocalExportEntryTable); } const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast(reinterpret_cast(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::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(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(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 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; -- cgit v1.2.3