aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-07-15 11:15:52 +0200
committerSami Shalayel <sami.shalayel@qt.io>2022-08-15 11:20:32 +0200
commite9dc0ac68134bc6a9cc6522eaca6d893ee10b86d (patch)
treef584dbb5ff239bbe7f931f35d2b8e46465f3ff60
parent70074e1acaca16afd6306a9c967ce4d15966c333 (diff)
Create Translation Bindings without CompiledData::Binding
To add translation bindings to qmltc, the methods used to create translation bindings need to be adapted to work without QV4::CompiledData::Binding as it is not available from qmltc. Instead, information already available from the QQmlJSScope should be used, along with a newly introduced helper class QQmlTranslation. Details: Add a QQmlTranslation class that represents a call to qsTr, qsTrId etc that knows how to translate itself (without needing any ExecutableCompilationUnit or Binding). It encapsulates the information needed to create a translation binding. ExecutableCompilationUnit::bindingValueAsString refactored so its functionality can be used without Binding. Instead, it uses only the translationId, see ExecutableCompilationUnit::translateFromId and ExecutableCompilationUnit::translateFrom. Refactored QQmlTranslationBinding to work with QQmlTranslation instead of CompiledData::Binding. Same for QQmlCppBinding::createTranslationBindingForBindable, QQmlTranslationPropertyBinding::create and QQmlCppBinding::createTranslationBindingForNonBindable. Changed TranslationBindingInformation to work without CompiledData::Binding, and also removed static unused QString ProxyTranslator::originStringFromInformation( const TranslationBindingInformation &translationBindingInformation) as I could not find out what this origin string is. Same for the translation debugging in qmldb_preview. Added QmltcCodeGenerator::generate_createTranslationBindingOnProperty. Added #if to avoid compilation error for standalone DOM compilation due to the new QQmlTranslation class. Task-number: QTBUG-105345 Change-Id: Iccd94d5cba4eaf63901233451fec48051c855c2a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp19
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/proxytranslator.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp19
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/common/qqmltranslation.cpp124
-rw-r--r--src/qml/common/qqmltranslation_p.h70
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces.cpp38
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces_p.h13
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp51
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h8
-rw-r--r--src/qml/qml/qqmlbinding.cpp92
-rw-r--r--src/qml/qml/qqmlbinding_p.h7
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h3
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp41
-rw-r--r--src/qml/qml/qqmlpropertybinding_p.h5
-rw-r--r--src/qml/qmltc/supportlibrary/qqmlcppbinding.cpp52
-rw-r--r--src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h12
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp21
-rw-r--r--src/qmlcompiler/qqmljsmetatypes.cpp28
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h35
20 files changed, 561 insertions, 82 deletions
diff --git a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp
index 557d6a0082..34146597d5 100644
--- a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp
@@ -24,25 +24,16 @@ bool ProxyTranslator::hasTranslation(const TranslationBindingInformation &transl
return translationFound();
}
-QString ProxyTranslator::translationFromInformation(const TranslationBindingInformation &translationBindingInformation)
+QString ProxyTranslator::translationFromInformation(const TranslationBindingInformation &info)
{
- return translationBindingInformation.compilationUnit->bindingValueAsString(
- translationBindingInformation.compiledBinding);
-}
-
-
-QString ProxyTranslator::originStringFromInformation(const TranslationBindingInformation &translationBindingInformation)
-{
- return translationBindingInformation.compilationUnit->stringAt(
- translationBindingInformation.compiledBinding->stringIndex);
+ return info.translation.translate();
}
QQmlSourceLocation ProxyTranslator::sourceLocationFromInformation(const TranslationBindingInformation &translationBindingInformation)
{
- return QQmlSourceLocation(
- translationBindingInformation.compilationUnit->fileName(),
- translationBindingInformation.compiledBinding->valueLocation.line(),
- translationBindingInformation.compiledBinding->valueLocation.column());
+ return QQmlSourceLocation(translationBindingInformation.compilationUnit->fileName(),
+ translationBindingInformation.line,
+ translationBindingInformation.column);
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h
index b80d724f05..37024dd049 100644
--- a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h
+++ b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h
@@ -39,8 +39,8 @@ public:
void removeEngine(QQmlEngine *engine);
bool hasTranslation(const TranslationBindingInformation &translationBindingInformation) const;
- static QString translationFromInformation(const TranslationBindingInformation &translationBindingInformation);
- static QString originStringFromInformation(const TranslationBindingInformation &translationBindingInformation);
+ static QString
+ translationFromInformation(const TranslationBindingInformation &translationBindingInformation);
static QQmlSourceLocation sourceLocationFromInformation(const TranslationBindingInformation &translationBindingInformation);
Q_SIGNALS:
void languageChanged(const QLocale &locale);
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
index 859f89367f..6ff84cc800 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
@@ -36,8 +36,8 @@ QDebug operator<<(QDebug debug, const TranslationBindingInformation &translation
{
QQmlError error;
error.setUrl(translationBindingInformation.compilationUnit->url());
- error.setLine(translationBindingInformation.compiledBinding->valueLocation.line());
- error.setColumn(translationBindingInformation.compiledBinding->valueLocation.column());
+ error.setLine(translationBindingInformation.line);
+ error.setColumn(translationBindingInformation.column);
error.setDescription(
QString(QLatin1String(
"QDebug translation binding"
@@ -147,12 +147,9 @@ public:
QObject *scopeObject = information.scopeObject;
auto compilationUnit = information.compilationUnit;
- auto binding = information.compiledBinding;
auto metaObject = scopeObject->metaObject();
- const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
-
- int textIndex = metaObject->indexOfProperty(propertyName.toLatin1());
+ int textIndex = metaObject->indexOfProperty(information.propertyName.toLatin1());
if (textIndex >= 0) {
QmlElement qmlElement;
@@ -161,9 +158,7 @@ public:
auto textProperty = scopeObject->metaObject()->property(textIndex);
qmlElement.propertyName = textProperty.name();
- qmlElement.translationId = compilationUnit->stringAt(
- compilationUnit->data->translations()[binding->value.translationDataIndex]
- .stringIndex);
+ qmlElement.translationId = information.translation.idForQmlDebug();
qmlElement.translatedText = textProperty.read(scopeObject).toString();
qmlElement.elementId = qmlContext(scopeObject)->nameForObject(scopeObject);
@@ -185,7 +180,7 @@ public:
QString warningMessage = "(QQmlDebugTranslationService can not resolve %1 - %2:"\
" this should never happen)";
const QString id = qmlContext(scopeObject)->nameForObject(scopeObject);
- qWarning().noquote() << warningMessage.arg(id, propertyName);
+ qWarning().noquote() << warningMessage.arg(id, information.propertyName);
}
}
std::sort(qmlElements.begin(), qmlElements.end(), [](const auto &l1, const auto &l2){
@@ -263,8 +258,8 @@ private:
{
CodeMarker c;
c.url = information.compilationUnit->url();
- c.line = information.compiledBinding->valueLocation.line();
- c.column = information.compiledBinding->valueLocation.column();
+ c.line = information.line;
+ c.column = information.column;
return c;
}
QString currentStateName;
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index a78f8ae98c..4ed985762c 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -128,6 +128,7 @@ qt_internal_add_qml_module(Qml
common/qv4compileddata_p.h
common/qv4staticvalue_p.h
common/qv4stringtoarrayindex_p.h
+ common/qqmltranslation.cpp common/qqmltranslation_p.h
compiler/qqmlirbuilder.cpp compiler/qqmlirbuilder_p.h
compiler/qv4bytecodegenerator.cpp compiler/qv4bytecodegenerator_p.h
compiler/qv4bytecodehandler.cpp compiler/qv4bytecodehandler_p.h
diff --git a/src/qml/common/qqmltranslation.cpp b/src/qml/common/qqmltranslation.cpp
new file mode 100644
index 0000000000..851049d910
--- /dev/null
+++ b/src/qml/common/qqmltranslation.cpp
@@ -0,0 +1,124 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "private/qqmltranslation_p.h"
+
+QQmlTranslation::QQmlTranslation(const Data &d) : data(d) { }
+
+QString QQmlTranslation::translate() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::monostate>)
+ return arg.translate();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QString QQmlTranslation::serializeForQmltc() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::monostate>)
+ return arg.serializeForQmltc();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QString QQmlTranslation::idForQmlDebug() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::monostate>)
+ return arg.idForQmlDebug();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QQmlTranslation::QsTrData::QsTrData(const QString &context, const QString &text,
+ const QString &comment, int number)
+ : context(context.toUtf8()), text(text.toUtf8()), comment(comment.toUtf8()), number(number)
+{
+}
+
+QString QQmlTranslation::contextFromQmlFilename(const QString &qmlFilename)
+{
+ int lastSlash = qmlFilename.lastIndexOf(QLatin1Char('/'));
+ QStringView contextView = (lastSlash > -1)
+ ? QStringView{ qmlFilename }.mid(lastSlash + 1, qmlFilename.length() - lastSlash - 5)
+ : QStringView();
+ return contextView.toString();
+}
+
+QString QQmlTranslation::QsTrData::translate() const
+{
+#if !QT_CONFIG(translation)
+ return QString();
+#else
+ return QCoreApplication::translate(context, text, comment, number);
+#endif
+}
+
+QString QQmlTranslation::QsTrData::idForQmlDebug() const
+{
+ return QString::fromUtf8(text);
+}
+
+QString QQmlTranslation::QsTrData::serializeForQmltc() const
+{
+ QString result = QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrData(
+ QStringLiteral("%1"),
+ QStringLiteral("%2"),
+ QStringLiteral("%3"),
+ %4)))")
+ .arg(QString::fromUtf8(context), QString::fromUtf8(text),
+ QString::fromUtf8(comment))
+ .arg(number);
+
+ return result;
+}
+
+QQmlTranslation::QsTrIdData::QsTrIdData(const QString &id, int number)
+ : id(id.toUtf8()), number(number)
+{
+}
+
+QString QQmlTranslation::QsTrIdData::translate() const
+{
+#if !QT_CONFIG(translation)
+ return QString();
+#else
+ return qtTrId(id, number);
+#endif
+}
+
+QString QQmlTranslation::QsTrIdData::serializeForQmltc() const
+{
+ QString result = QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrIdData(
+ QStringLiteral("%1"),
+ %4)))")
+ .arg(QString::fromUtf8(id))
+ .arg(number);
+
+ return result;
+}
+
+QString QQmlTranslation::QsTrIdData::idForQmlDebug() const
+{
+ return QString::fromUtf8(id);
+}
diff --git a/src/qml/common/qqmltranslation_p.h b/src/qml/common/qqmltranslation_p.h
new file mode 100644
index 0000000000..144c21f127
--- /dev/null
+++ b/src/qml/common/qqmltranslation_p.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLTRANSLATION_P_H
+#define QQMLTRANSLATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+
+#include <private/qv4qmlcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_PRIVATE_EXPORT QQmlTranslation
+{
+public:
+ class Q_QML_PRIVATE_EXPORT QsTrData
+ {
+ QByteArray context;
+ QByteArray text;
+ QByteArray comment;
+ int number;
+
+ public:
+ QsTrData(const QString &fileNameForContext, const QString &text, const QString &comment,
+ int number);
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+ };
+
+ class Q_QML_PRIVATE_EXPORT QsTrIdData
+ {
+ QByteArray id;
+ int number;
+
+ public:
+ QsTrIdData(const QString &id, int number);
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+ };
+
+ using Data = std::variant<std::monostate, QsTrData, QsTrIdData>;
+
+private:
+ Data data;
+
+public:
+ QQmlTranslation(const Data &d);
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+
+ static QString contextFromQmlFilename(const QString &qmlFilename);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTRANSLATION_P_H
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces.cpp b/src/qml/debugger/qqmldebugserviceinterfaces.cpp
index 7597a381b7..4067d1f5d1 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces.cpp
+++ b/src/qml/debugger/qqmldebugserviceinterfaces.cpp
@@ -45,6 +45,44 @@ QQmlDebugStatesDelegate *QQmlEngineDebugService::createStatesDelegate()
#if QT_CONFIG(translation)
QQmlDebugTranslationService::~QQmlDebugTranslationService()
= default;
+
+const TranslationBindingInformation TranslationBindingInformation::create(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding, QObject *scopeObject,
+ QQmlRefPointer<QQmlContextData> ctxt)
+{
+ QQmlTranslation translation({});
+ if (binding->type() == QV4::CompiledData::Binding::Type_TranslationById) {
+ const QV4::CompiledData::TranslationData data =
+ compilationUnit->data->translations()[binding->value.translationDataIndex];
+ const QString id = compilationUnit->stringAt(data.stringIndex);
+ const int n = data.number;
+
+ translation = QQmlTranslation(QQmlTranslation::QsTrIdData(id, n));
+ } else {
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Translation);
+
+ const QV4::CompiledData::TranslationData data =
+ compilationUnit->data->translations()[binding->value.translationDataIndex];
+ const QString context =
+ QQmlTranslation::contextFromQmlFilename(compilationUnit->fileName());
+ const QString text = compilationUnit->stringAt(data.stringIndex);
+ const QString comment = compilationUnit->stringAt(data.commentIndex);
+ const int n = data.number;
+
+ translation = QQmlTranslation(QQmlTranslation::QsTrData(context, text, comment, n));
+ }
+
+ return { compilationUnit,
+ scopeObject,
+ ctxt,
+
+ compilationUnit->stringAt(binding->propertyNameIndex),
+ translation,
+
+ binding->location.line(),
+ binding->location.column() };
+}
#endif
QT_END_NAMESPACE
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
index 33db3a65c3..7c0b736463 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h
+++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
@@ -22,6 +22,7 @@
#endif
#include <private/qqmldebugstatesdelegate_p.h>
#include <private/qqmlboundsignal_p.h>
+#include <private/qqmltranslation_p.h>
#include <limits>
@@ -143,10 +144,20 @@ protected:
#if QT_CONFIG(translation)
struct TranslationBindingInformation
{
+ static const TranslationBindingInformation
+ create(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding, QObject *scopeObject,
+ QQmlRefPointer<QQmlContextData> ctxt);
+
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
- const QV4::CompiledData::Binding *compiledBinding;
QObject *scopeObject;
QQmlRefPointer<QQmlContextData> ctxt;
+
+ QString propertyName;
+ QQmlTranslation translation;
+
+ quint32 line;
+ quint32 column;
};
class Q_QML_PRIVATE_EXPORT QQmlDebugTranslationService : public QQmlDebugService
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 3fe031501f..8235603f83 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -849,28 +849,15 @@ bool ResolvedTypeReferenceMap::addToHash(
QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
{
- using namespace CompiledData;
#if QT_CONFIG(translation)
+ using namespace CompiledData;
+ bool byId = false;
switch (binding->type()) {
- case Binding::Type_TranslationById: {
- const TranslationData &translation
- = data->translations()[binding->value.translationDataIndex];
- QByteArray id = stringAt(translation.stringIndex).toUtf8();
- return qtTrId(id.constData(), translation.number);
- }
+ case Binding::Type_TranslationById:
+ byId = true;
+ Q_FALLTHROUGH();
case Binding::Type_Translation: {
- const TranslationData &translation
- = data->translations()[binding->value.translationDataIndex];
- // This code must match that in the qsTr() implementation
- const QString &path = fileName();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- QStringView context = (lastSlash > -1) ? QStringView{path}.mid(lastSlash + 1, path.length() - lastSlash - 5)
- : QStringView();
- QByteArray contextUtf8 = context.toUtf8();
- QByteArray comment = stringAt(translation.commentIndex).toUtf8();
- QByteArray text = stringAt(translation.stringIndex).toUtf8();
- return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
- comment.constData(), translation.number);
+ return translateFrom({ binding->value.translationDataIndex, byId });
}
default:
break;
@@ -879,6 +866,32 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind
return CompilationUnit::bindingValueAsString(binding);
}
+QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const
+{
+#if !QT_CONFIG(translation)
+ return QString();
+#else
+ const CompiledData::TranslationData &translation = data->translations()[index.index];
+
+ if (index.byId) {
+ QByteArray id = stringAt(translation.stringIndex).toUtf8();
+ return qtTrId(id.constData(), translation.number);
+ }
+
+ // This code must match that in the qsTr() implementation
+ const QString &path = fileName();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ QStringView context = (lastSlash > -1)
+ ? QStringView{ path }.mid(lastSlash + 1, path.length() - lastSlash - 5)
+ : QStringView();
+ QByteArray contextUtf8 = context.toUtf8();
+ QByteArray comment = stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = stringAt(translation.stringIndex).toUtf8();
+ return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
+ comment.constData(), translation.number);
+#endif
+}
+
bool ExecutableCompilationUnit::verifyHeader(
const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString)
{
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index a43f443852..93f69a9352 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -252,6 +252,14 @@ public:
QString bindingValueAsString(const CompiledData::Binding *binding) const;
+ struct TranslationDataIndex
+ {
+ uint index;
+ bool byId;
+ };
+
+ QString translateFrom(TranslationDataIndex index) const;
+
static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp,
QString *errorString);
protected:
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 6b138bba61..5689438e03 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -277,20 +277,14 @@ protected:
class QQmlTranslationBinding : public GenericBinding<QMetaType::QString>, public QPropertyObserver {
public:
- QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
: QPropertyObserver(&QQmlTranslationBinding::onLanguageChange)
{
setCompilationUnit(compilationUnit);
- m_binding = binding;
setSource(QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage);
}
- QQmlSourceLocation sourceLocation() const override final
- {
- return QQmlSourceLocation(
- m_compilationUnit->fileName(), m_binding->valueLocation.line(),
- m_binding->valueLocation.column());
- }
+ virtual QString bindingValue() const = 0;
static void onLanguageChange(QPropertyObserver *observer, QUntypedPropertyData *)
{ static_cast<QQmlTranslationBinding *>(observer)->update(); }
@@ -304,7 +298,7 @@ public:
if (!isAddedToObject() || hasError())
return;
- const QString result = m_compilationUnit->bindingValueAsString(m_binding);
+ const QString result = this->bindingValue();
Q_ASSERT(targetObject());
@@ -321,9 +315,56 @@ public:
}
bool hasDependencies() const override final { return true; }
+};
-private:
+class QQmlTranslationBindingFromBinding : public QQmlTranslationBinding
+{
const QV4::CompiledData::Binding *m_binding;
+
+public:
+ QQmlTranslationBindingFromBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding)
+ : QQmlTranslationBinding(compilationUnit), m_binding(binding)
+ {
+ }
+
+ QString bindingValue() const override
+ {
+ return this->m_compilationUnit->bindingValueAsString(m_binding);
+ }
+
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line(),
+ m_binding->valueLocation.column());
+ }
+};
+
+class QQmlTranslationBindingFromTranslationInfo : public QQmlTranslationBinding
+{
+ QQmlTranslation m_translationData;
+
+ quint16 m_line;
+ quint16 m_column;
+
+public:
+ QQmlTranslationBindingFromTranslationInfo(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlTranslation &translationData, quint16 line, quint16 column)
+ : QQmlTranslationBinding(compilationUnit),
+ m_translationData(translationData),
+ m_line(line),
+ m_column(column)
+ {
+ }
+
+ virtual QString bindingValue() const override { return m_translationData.translate(); }
+
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_line, m_column);
+ }
};
QQmlBinding *QQmlBinding::createTranslationBinding(
@@ -331,7 +372,7 @@ QQmlBinding *QQmlBinding::createTranslationBinding(
const QV4::CompiledData::Binding *binding, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt)
{
- QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
+ QQmlTranslationBinding *b = new QQmlTranslationBindingFromBinding(unit, binding);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
@@ -339,7 +380,34 @@ QQmlBinding *QQmlBinding::createTranslationBinding(
#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
if (QQmlDebugTranslationService *service
= QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
- service->foundTranslationBinding({unit, binding, b->scopeObject(), ctxt});
+ service->foundTranslationBinding(
+ TranslationBindingInformation::create(unit, binding, b->scopeObject(), ctxt));
+ }
+#endif
+ return b;
+}
+
+QQmlBinding *QQmlBinding::createTranslationBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlRefPointer<QQmlContextData> &ctxt, const QString &propertyName,
+ const QQmlTranslation &translationData, const QQmlSourceLocation &location, QObject *obj)
+{
+ QQmlTranslationBinding *b = new QQmlTranslationBindingFromTranslationInfo(
+ unit, translationData, location.column, location.line);
+
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
+
+#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
+ QString originString;
+ if (QQmlDebugTranslationService *service =
+ QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
+ service->foundTranslationBinding({ unit, b->scopeObject(), ctxt,
+
+ propertyName, translationData,
+
+ location.line, location.column });
}
#endif
return b;
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 6d4c7f22cf..d544670264 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -28,6 +28,7 @@
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qv4functionobject_p.h>
+#include <private/qqmltranslation_p.h>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,12 @@ public:
const QV4::CompiledData::Binding *binding, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt);
+ static QQmlBinding *
+ createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlRefPointer<QQmlContextData> &ctxt,
+ const QString &propertyName, const QQmlTranslation &translationData,
+ const QQmlSourceLocation &location, QObject *obj);
+
Kind kind() const final { return QQmlAbstractBinding::QmlBinding; }
~QQmlBinding() override;
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index c9cb1c7fea..e8e4bb4990 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -164,7 +164,8 @@ private:
friend class QQmlContextData;
friend class QQmlPropertyCapture;
friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
- friend class QQmlTranslationBinding;
+ friend class QQmlTranslationBindingFromBinding;
+ friend class QQmlTranslationBindingFromTranslationInfo;
friend class QQmlJavaScriptExpressionCapture;
// Not refcounted as the context will clear the expressions when destructed.
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index d55f1021f2..c84a8efe74 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -296,13 +296,16 @@ void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
QQmlEnginePrivate::get(engine)->warning(qmlError);
}
-QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(const QQmlPropertyData *pd, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+template<typename TranslateWithUnit>
+auto qQmlTranslationPropertyBindingCreateBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ TranslateWithUnit &&translateWithUnit)
{
- auto translationBinding = [compilationUnit, binding](QMetaType metaType, void *dataPtr) -> bool {
+ return [compilationUnit, translateWithUnit](QMetaType metaType, void *dataPtr) -> bool {
// Create a dependency to the translationLanguage
QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
- QVariant resultVariant(compilationUnit->bindingValueAsString(binding));
+ QVariant resultVariant(translateWithUnit(compilationUnit));
if (metaType != QMetaType::fromType<QString>())
resultVariant.convert(metaType);
@@ -311,8 +314,38 @@ QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(const QQmlPropert
metaType.construct(dataPtr, resultVariant.constData());
return hasChanged;
};
+}
- return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding, QPropertyBindingSourceLocation());
+QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(
+ const QQmlPropertyData *pd,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding)
+{
+ auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
+ compilationUnit,
+ [binding](const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
+ return compilationUnit->bindingValueAsString(binding);
+ });
+
+ return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
+ QPropertyBindingSourceLocation());
+}
+
+QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(
+ const QMetaType &propertyType,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlTranslation &translationData)
+{
+ auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
+ compilationUnit,
+ [translationData](
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
+ Q_UNUSED(compilationUnit);
+ return translationData.translate();
+ });
+
+ return QUntypedPropertyBinding(propertyType, translationBinding,
+ QPropertyBindingSourceLocation());
}
QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(bool *isUndefined)
diff --git a/src/qml/qml/qqmlpropertybinding_p.h b/src/qml/qml/qqmlpropertybinding_p.h
index 2229a6268a..9973fe42d7 100644
--- a/src/qml/qml/qqmlpropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertybinding_p.h
@@ -18,6 +18,7 @@
#include <private/qqmljavascriptexpression_p.h>
#include <private/qqmlpropertydata_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qqmltranslation_p.h>
#include <QtCore/qproperty.h>
@@ -222,6 +223,10 @@ public:
static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT create(const QQmlPropertyData *pd,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QV4::CompiledData::Binding *binding);
+ static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT
+ create(const QMetaType &pd,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlTranslation &translationData);
};
inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
diff --git a/src/qml/qmltc/supportlibrary/qqmlcppbinding.cpp b/src/qml/qmltc/supportlibrary/qqmlcppbinding.cpp
index a9d4207c1b..fdea993124 100644
--- a/src/qml/qmltc/supportlibrary/qqmlcppbinding.cpp
+++ b/src/qml/qmltc/supportlibrary/qqmlcppbinding.cpp
@@ -108,4 +108,56 @@ void QQmlCppBinding::createBindingForNonBindable(const QV4::ExecutableCompilatio
});
}
+QUntypedPropertyBinding QQmlCppBinding::createTranslationBindingForBindable(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, QObject *bindingTarget,
+ int metaPropertyIndex, const QQmlTranslation &translationData, const QString &propertyName)
+{
+ Q_UNUSED(propertyName);
+
+ if (metaPropertyIndex < 0) {
+ // TODO: align with existing logging of such
+ qCritical() << "invalid meta property index (internal error)";
+ return QUntypedPropertyBinding();
+ }
+
+ const QMetaObject *mo = bindingTarget->metaObject();
+ Q_ASSERT(mo);
+ QMetaProperty property = mo->property(metaPropertyIndex);
+ Q_ASSERT(QString::fromUtf8(property.name()) == propertyName);
+
+ return QQmlTranslationPropertyBinding::create(property.metaType(), unit, translationData);
+}
+
+void QQmlCppBinding::createTranslationBindingForNonBindable(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlSourceLocation &location, const QQmlTranslation &translationData,
+ QObject *thisObject, QObject *bindingTarget, int metaPropertyIndex,
+ const QString &propertyName, int valueTypePropertyIndex)
+{
+ Q_UNUSED(propertyName);
+
+ if (metaPropertyIndex < 0) {
+ // TODO: align with existing logging of such
+ qCritical() << "invalid meta property index (internal error)";
+ return;
+ }
+
+ const QMetaObject *mo = bindingTarget->metaObject();
+ Q_ASSERT(mo);
+ QMetaProperty property = mo->property(metaPropertyIndex);
+ Q_ASSERT(QString::fromUtf8(property.name()) == propertyName);
+
+ createBindingInScope(
+ thisObject,
+ [&](const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *) -> void {
+ QQmlBinding *binding = QQmlBinding::createTranslationBinding(
+ unit, ctxt, propertyName, translationData, location, thisObject);
+ // almost as in qv4objectwrapper.cpp:535
+ Q_ASSERT(!property.isAlias()); // we convert aliases to (almost) real properties
+ binding->setTarget(bindingTarget, property.propertyIndex(), false,
+ valueTypePropertyIndex);
+ QQmlPropertyPrivate::setBinding(binding);
+ });
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h b/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
index e692ab4007..0f2e6a4998 100644
--- a/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
+++ b/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
@@ -45,6 +45,18 @@ struct Q_QML_PRIVATE_EXPORT QQmlCppBinding
QObject *bindingTarget, int metaPropertyIndex,
int valueTypePropertyIndex,
const QString &propertyName);
+
+ static QUntypedPropertyBinding
+ createTranslationBindingForBindable(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ QObject *bindingTarget, int metaPropertyIndex,
+ const QQmlTranslation &translationData,
+ const QString &propertyName);
+
+ static void createTranslationBindingForNonBindable(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlSourceLocation &location, const QQmlTranslation &translationData,
+ QObject *thisObject, QObject *bindingTarget, int metaPropertyIndex,
+ const QString &propertyName, int valueTypePropertyIndex);
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 8e16a30a85..f67b897cce 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1604,28 +1604,33 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
leaveEnvironment();
}
-// ### TODO: add warning about suspicious translation binding when returning false?
void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base,
QQmlJS::AST::ArgumentList *args)
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ // #if required for standalone DOM compilation
QStringView mainString;
+ QStringView commentString;
auto registerMainString = [&](QStringView string) {
mainString = string;
return 0;
};
- auto discardCommentString = [](QStringView) {return -1;};
- auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type, QV4::CompiledData::TranslationData) {
+ auto registerCommentString = [&](QStringView string) {
+ commentString = string;
+ return 0;
+ };
+ auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
+ QV4::CompiledData::TranslationData data) {
if (type == QV4::CompiledData::Binding::Type_Translation) {
- binding.setTranslation(mainString);
+ binding.setTranslation(mainString, commentString, data.number);
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
- binding.setTranslationId(mainString);
+ binding.setTranslationId(mainString, data.number);
} else {
binding.setStringLiteral(mainString);
}
};
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
- // #if required for standalone DOM compilation against Qt 6.2
- QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString, discardCommentString, finalizeBinding);
+ QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString,
+ registerCommentString, finalizeBinding);
#endif
}
diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp
index afc7315adf..a5e25c61b5 100644
--- a/src/qmlcompiler/qqmljsmetatypes.cpp
+++ b/src/qmlcompiler/qqmljsmetatypes.cpp
@@ -4,6 +4,10 @@
#include "qqmljsmetatypes_p.h"
#include "qqmljstyperesolver_p.h"
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+# include "QtQml/private/qqmltranslation_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
/*!
@@ -60,6 +64,30 @@ QString QQmlJSMetaPropertyBinding::regExpValue() const
return {};
}
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+/*!
+ * Extracts the information about translations from a binding.
+ * An additional context string is needed for text based translation (e.g. with qsTr())
+ * and can be obtained from the name of the qml file.
+ *
+ * \sa QQmlTranslation
+ */
+QQmlTranslation QQmlJSMetaPropertyBinding::translationDataValue(QString qmlFileNameForContext) const
+{
+ if (auto translation = std::get_if<Content::TranslationById>(&m_bindingContent)) {
+ QQmlTranslation::QsTrIdData data(translation->id, translation->number);
+ return QQmlTranslation(data);
+ } else if (auto translation = std::get_if<Content::TranslationString>(&m_bindingContent)) {
+ const QString context = QQmlTranslation::contextFromQmlFilename(qmlFileNameForContext);
+ QQmlTranslation::QsTrData data(context, translation->text, translation->comment,
+ translation->number);
+ return QQmlTranslation(data);
+ }
+ // warn
+ return QQmlTranslation({});
+}
+#endif
+
/*!
\internal
Uses \a resolver to return the correct type for the stored literal
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index a49dd5a231..635bb8a93b 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -24,6 +24,10 @@
#include <QtQml/private/qqmljssourcelocation_p.h>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+# include <QtQml/private/qqmltranslation_p.h>
+#endif
+
#include "qqmljsannotation_p.h"
// MetaMethod and MetaProperty have both type names and actual QQmlJSScope types.
@@ -444,14 +448,23 @@ private:
friend bool operator!=(Null a, Null b) { return !(a == b); }
};
struct TranslationString {
- friend bool operator==(TranslationString a, TranslationString b) { return a.value == b.value; }
+ friend bool operator==(TranslationString a, TranslationString b)
+ {
+ return a.text == b.text && a.comment == b.comment && a.number == b.number;
+ }
friend bool operator!=(TranslationString a, TranslationString b) { return !(a == b); }
- QString value;
+ QString text;
+ QString comment;
+ int number;
};
struct TranslationById {
- friend bool operator==(TranslationById a, TranslationById b) { return a.value == b.value; }
+ friend bool operator==(TranslationById a, TranslationById b)
+ {
+ return a.id == b.id && a.number == b.number;
+ }
friend bool operator!=(TranslationById a, TranslationById b) { return !(a == b); }
- QString value;
+ QString id;
+ int number;
};
struct Script {
friend bool operator==(Script a, Script b)
@@ -633,17 +646,17 @@ public:
m_bindingContent = Content::RegexpLiteral { value.toString() };
}
- // ### TODO: we might need comment and translation number at some point
- void setTranslation(QStringView translation)
+ void setTranslation(QStringView text, QStringView comment, int number)
{
ensureSetBindingTypeOnce();
- m_bindingContent = Content::TranslationString { translation.toString() };
+ m_bindingContent =
+ Content::TranslationString{ text.toString(), comment.toString(), number };
}
- void setTranslationId(QStringView id)
+ void setTranslationId(QStringView id, int number)
{
ensureSetBindingTypeOnce();
- m_bindingContent = Content::TranslationById { id.toString() };
+ m_bindingContent = Content::TranslationById{ id.toString(), number };
}
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
@@ -675,6 +688,10 @@ public:
QString regExpValue() const;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ QQmlTranslation translationDataValue(QString qmlFileNameForContext = QStringLiteral("")) const;
+#endif
+
QSharedPointer<const QQmlJSScope> literalType(const QQmlJSTypeResolver *resolver) const;
QQmlJSMetaMethod::RelativeFunctionIndex scriptIndex() const