diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2022-07-15 11:15:52 +0200 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2022-08-15 11:20:32 +0200 |
commit | e9dc0ac68134bc6a9cc6522eaca6d893ee10b86d (patch) | |
tree | f584dbb5ff239bbe7f931f35d2b8e46465f3ff60 | |
parent | 70074e1acaca16afd6306a9c967ce4d15966c333 (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>
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 |