diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2020-01-22 13:37:48 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2020-01-23 15:56:40 +0100 |
commit | 020a6e67766595351bcf911e965b26952a7c81b8 (patch) | |
tree | 6cbb9340cb28f18efa98c9395b782cd13b81f101 | |
parent | aca7b33368655949d238a0cdfc0073b6b8532872 (diff) |
Add Qt.uiLanguage and QJSEngine::uiLanguage properties
[ChangeLog][QtQml] Added Qt.uiLanguage and QJSEngine::uiLanguage properties
These properties mirror the same value in QML and C++ and can be used
freely. They also provide API symmetry to Qt for MCUs.
QQmlApplicationEngine binds to this property and applies translations
accordingly by constructing a QLocale with the value and using
QTranslator::load(locale).
Change-Id: Id87d6ee64679b07ff3cb47844594e8eeebd8c8b6
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Christian Kamm <mail@ckamm.de>
-rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 38 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine.h | 7 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.cpp | 37 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine_p.h | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 15 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 43 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 29 | ||||
-rw-r--r-- | tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm | bin | 0 -> 101 bytes | |||
-rw-r--r-- | tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts | 11 | ||||
-rw-r--r-- | tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp | 23 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/data/uiLanguage.qml | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 33 |
16 files changed, 236 insertions, 20 deletions
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 065fbc1c0a..6946a64d11 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -71,11 +71,6 @@ #include <private/qqmlglobal_p.h> #include <qqmlengine.h> -#undef Q_D -#undef Q_Q -#define Q_D(blah) -#define Q_Q(blah) - Q_DECLARE_METATYPE(QList<int>) /*! @@ -257,7 +252,7 @@ Q_DECLARE_METATYPE(QList<int>) \l installExtensions(). \value TranslationExtension Indicates that translation functions (\c qsTr(), - for example) should be installed. + for example) should be installed. This also installs the Qt.uiLanguage property. \value ConsoleExtension Indicates that console functions (\c console.log(), for example) should be installed. @@ -692,7 +687,6 @@ QJSValue QJSEngine::newArray(uint length) */ QJSValue QJSEngine::newQObject(QObject *object) { - Q_D(QJSEngine); QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); if (object) { @@ -719,7 +713,6 @@ QJSValue QJSEngine::newQObject(QObject *object) */ QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { - Q_D(QJSEngine); QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); @@ -1004,6 +997,35 @@ void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message m_v4Engine->throwError(e); } +/*! + \property QJSEngine::uiLanguage + \brief the language to be used for translating user interface strings + \since 5.15 + + This property holds the name of the language to be used for user interface + string translations. It is exposed for reading and writing as \c{Qt.uiLanguage} when + the QJSEngine::TranslationExtension is installed on the engine. It is always exposed + in instances of QQmlEngine. + + You can set the value freely and use it in bindings. It is recommended to set it + after installing translators in your application. By convention, an empty string + means no translation from the language used in the source code is intended to occur. +*/ +void QJSEngine::setUiLanguage(const QString &language) +{ + Q_D(QJSEngine); + if (language == d->uiLanguage) + return; + d->uiLanguage = language; + emit uiLanguageChanged(); +} + +QString QJSEngine::uiLanguage() const +{ + Q_D(const QJSEngine); + return d->uiLanguage; +} + QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) { return e->jsEngine()->d_func(); diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 31a4d68baa..31229e1f20 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -60,6 +60,7 @@ class Q_QML_EXPORT QJSEngine : public QObject { Q_OBJECT + Q_PROPERTY(QString uiLanguage READ uiLanguage WRITE setUiLanguage NOTIFY uiLanguageChanged) public: QJSEngine(); explicit QJSEngine(QObject *parent); @@ -121,6 +122,12 @@ public: void throwError(const QString &message); void throwError(QJSValue::ErrorType errorType, const QString &message = QString()); + QString uiLanguage() const; + void setUiLanguage(const QString &language); + +Q_SIGNALS: + void uiLanguageChanged(); + private: QJSValue create(int type, const void *ptr); diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h index 164a70d000..7866a5bdda 100644 --- a/src/qml/jsapi/qjsengine_p.h +++ b/src/qml/jsapi/qjsengine_p.h @@ -107,6 +107,7 @@ public: // Shared by QQmlEngine mutable QRecursiveMutex mutex; + QString uiLanguage; // These methods may be called from the QML loader thread inline QQmlPropertyCache *cache(QObject *obj, int minorVersion = -1); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index dcdebfb398..86e178d568 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1949,11 +1949,12 @@ void ExecutionEngine::initQmlGlobalObject() void ExecutionEngine::initializeGlobal() { QV4::Scope scope(this); - QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine())); globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); + #if QT_CONFIG(qml_locale) QQmlLocale::registerStringLocaleCompare(this); QQmlDateExtension::registerExtension(this); diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 7f80fe5e1c..7d961cd0c7 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e) : QQmlEnginePrivate(e) { + uiLanguage = QLocale().bcp47Name(); } QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate() @@ -72,6 +73,7 @@ void QQmlApplicationEnginePrivate::init() &QCoreApplication::quit, Qt::QueuedConnection); q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit, Qt::QueuedConnection); + q->connect(q, SIGNAL(uiLanguageChanged()), q_func(), SLOT(_q_loadTranslations())); #if QT_CONFIG(translation) QTranslator* qtTranslator = new QTranslator(q); if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm"))) @@ -83,20 +85,27 @@ void QQmlApplicationEnginePrivate::init() QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true)); } -void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) +void QQmlApplicationEnginePrivate::_q_loadTranslations() { #if QT_CONFIG(translation) - if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc")) + if (translationsDirectory.isEmpty()) return; - QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile)); - Q_Q(QQmlApplicationEngine); - QTranslator *translator = new QTranslator(q); - if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) - QCoreApplication::installTranslator(translator); - else - delete translator; + + QScopedPointer<QTranslator> translator(new QTranslator); + if (!uiLanguage.isEmpty()) { + QLocale locale(uiLanguage); + if (translator->load(locale, QLatin1String("qml"), QLatin1String("_"), translationsDirectory, QLatin1String(".qm"))) { + if (activeTranslator) + QCoreApplication::removeTranslator(activeTranslator.data()); + QCoreApplication::installTranslator(translator.data()); + activeTranslator.swap(translator); + } + } else { + activeTranslator.reset(); + } + q->retranslate(); #else Q_UNUSED(rootFile) #endif @@ -106,7 +115,14 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & { Q_Q(QQmlApplicationEngine); - loadTranslations(url); //Translations must be loaded before the QML file is + if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) { + QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url)); + translationsDirectory = fi.path() + QLatin1String("/i18n"); + } else { + translationsDirectory.clear(); + } + + _q_loadTranslations(); //Translations must be loaded before the QML file is QQmlComponent *c = new QQmlComponent(q, q); if (dataFlag) @@ -181,6 +197,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) \list \li Translation files must have "qml_" prefix e.g. qml_ja_JP.qm. \endlist + \li Translations are reloaded when the \c QJSEngine::uiLanguage / \c Qt.uiLanguage property is changed. \li Automatically sets an incubation controller if the scene contains a QQuickWindow. \li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all QML files and assets. diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index 2b4de91154..37f75d5068 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -74,6 +74,7 @@ Q_SIGNALS: private: Q_DISABLE_COPY(QQmlApplicationEngine) + Q_PRIVATE_SLOT(d_func(), void _q_loadTranslations()) Q_DECLARE_PRIVATE(QQmlApplicationEngine) }; diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 1279e400e8..c514e3daf9 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -70,10 +70,12 @@ public: void cleanUp(); void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false); - void loadTranslations(const QUrl &rootFile); + void _q_loadTranslations(); void finishLoad(QQmlComponent *component); QList<QObject *> objects; QVariantMap initialProperties; + QString translationsDirectory; + QScopedPointer<QTranslator> activeTranslator; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 73d3ba1863..3345fcb19e 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1336,6 +1336,21 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) } /*! + \qmlproperty string Qt::uiLanguage + \since 5.15 + + The uiLanguage holds the name of the language to be used for user interface + string translations. It is exposed in C++ as QQmlEngine::uiLanguage property. + + You can set the value freely and use it in bindings. It is recommended to set it + after installing translators in your application. By convention, an empty string + means no translation from the language used in the source code is intended to occur. + + If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate() + will be called. +*/ + +/*! \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) Returns the instance of a singleton type that was registered under \a qmlTypeId. diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index ac4b9b85b9..b1046b254b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1338,6 +1338,41 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va return QV4::QObjectWrapper::wrap(scope.engine, c); } +ReturnedValue QtObject::method_get_uiLanguage(const FunctionObject *b, const Value * /*thisObject*/, const Value * /*argv*/, int /*argc*/) +{ + QV4::Scope scope(b); + QJSEngine *jsEngine = scope.engine->jsEngine(); + if (!jsEngine) + return Encode::null(); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(scope.engine); + if (ep && ep->propertyCapture) { + static int propertyIndex = -1; + static int notifySignalIndex = -1; + if (propertyIndex < 0) { + QMetaProperty metaProperty = + QQmlEngine::staticMetaObject.property(QQmlEngine::staticMetaObject.indexOfProperty("uiLanguage")); + propertyIndex = metaProperty.propertyIndex(); + notifySignalIndex = metaProperty.notifySignalIndex(); + } + ep->propertyCapture->captureProperty(QQmlEnginePrivate::get(ep), propertyIndex, notifySignalIndex); + } + + return Encode(scope.engine->newString(QJSEnginePrivate::get(jsEngine)->uiLanguage)); +} + +ReturnedValue QtObject::method_set_uiLanguage(const FunctionObject *b, const Value * /*thisObject*/, const Value *argv, int argc) +{ + Scope scope(b); + if (!argc) + THROW_TYPE_ERROR(); + QJSEngine *jsEngine = scope.engine->jsEngine(); + if (!jsEngine) + THROW_TYPE_ERROR(); + jsEngine->setUiLanguage(argv[0].toQString()); + return Encode::undefined(); +} + #if QT_CONFIG(qml_locale) /*! \qmlmethod Qt::locale(name) @@ -1835,6 +1870,14 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext globalObject->defineDefaultProperty(QStringLiteral("qsTrId"), QV4::GlobalExtensions::method_qsTrId); globalObject->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), QV4::GlobalExtensions::method_qsTrIdNoOp); + ScopedString qtName(scope, v4->newString(QStringLiteral("Qt"))); + ScopedObject qt(scope, globalObject->get(qtName)); + if (!qt) { + qt = v4->newObject(); + globalObject->defineDefaultProperty(qtName, qt); + } + qt->defineAccessorProperty(QStringLiteral("uiLanguage"), QV4::QtObject::method_get_uiLanguage, QV4::QtObject::method_set_uiLanguage); + // string prototype extension scope.engine->stringPrototype()->defineDefaultProperty(QStringLiteral("arg"), QV4::GlobalExtensions::method_string_arg); #endif diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index d87b83ba10..5cbb52471d 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -126,6 +126,8 @@ struct QtObject : Object static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_uiLanguage(const FunctionObject *b, const Value *, const Value *, int); + static ReturnedValue method_set_uiLanguage(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); #if QT_CONFIG(qml_locale) static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); #endif diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 28efb519ee..7b59087a72 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -264,6 +264,8 @@ private slots: void printCircularArray(); void typedArraySet(); + void uiLanguage(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -5143,6 +5145,33 @@ void tst_QJSEngine::typedArraySet() } } +void tst_QJSEngine::uiLanguage() +{ + { + QJSEngine engine; + + QVERIFY(!engine.globalObject().hasProperty("Qt")); + + engine.installExtensions(QJSEngine::TranslationExtension); + QVERIFY(engine.globalObject().hasProperty("Qt")); + QVERIFY(engine.globalObject().property("Qt").hasProperty("uiLanguage")); + + engine.setUiLanguage("Blah"); + QCOMPARE(engine.globalObject().property("Qt").property("uiLanguage").toString(), "Blah"); + + engine.evaluate("Qt.uiLanguage = \"another\""); + QCOMPARE(engine.globalObject().property("Qt").property("uiLanguage").toString(), "another"); + } + + { + QQmlEngine qmlEngine; + QVERIFY(qmlEngine.globalObject().hasProperty("Qt")); + QVERIFY(qmlEngine.globalObject().property("Qt").hasProperty("uiLanguage")); + qmlEngine.setUiLanguage("Blah"); + QCOMPARE(qmlEngine.globalObject().property("Qt").property("uiLanguage").toString(), "Blah"); + } +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm Binary files differnew file mode 100644 index 0000000000..926d74f905 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts new file mode 100644 index 0000000000..2105cfb2cf --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_CH" sourcelanguage="en"> +<context> + <name>loadTranslation</name> + <message> + <source>translate it</source> + <translation>Grüezi</translation> + </message> +</context> +</TS> diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 5e855efe1a..b019ff4535 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -53,6 +53,7 @@ private slots: void removeObjectsWhenDestroyed(); void loadTranslation_data(); void loadTranslation(); + void translationChange(); void setInitialProperties(); void failureToLoadTriggersWarningSignal(); @@ -278,6 +279,28 @@ void tst_qqmlapplicationengine::loadTranslation() QCOMPARE(rootObject->property("translation").toString(), translation); } +void tst_qqmlapplicationengine::translationChange() +{ + if (QLocale().language() == QLocale::SwissGerman) { + QSKIP("Skipping this when running under the Swiss locale as we would always load translation."); + } + + QQmlApplicationEngine engine(testFileUrl("loadTranslation.qml")); + + QCOMPARE(engine.uiLanguage(), QLocale().bcp47Name()); + + QObject *rootObject = engine.rootObjects().first(); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("translation").toString(), "translated"); + + engine.setUiLanguage("de_CH"); + QCOMPARE(rootObject->property("translation").toString(), QString::fromUtf8("Gr\u00FCezi")); + + engine.setUiLanguage(QString()); + QCOMPARE(rootObject->property("translation").toString(), "translate it"); +} + void tst_qqmlapplicationengine::setInitialProperties() { QQmlApplicationEngine test {}; diff --git a/tests/auto/qml/qqmlengine/data/uiLanguage.qml b/tests/auto/qml/qqmlengine/data/uiLanguage.qml new file mode 100644 index 0000000000..bc20351245 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/uiLanguage.qml @@ -0,0 +1,9 @@ +import QtQml 2.15 +QtObject { + property string chosenLanguage: Qt.uiLanguage + property string textToTranslate: { + numberOfTranslationBindingEvaluations++; + return qsTr("Translate me maybe"); + } + property int numberOfTranslationBindingEvaluations: 0 +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index d782df3e7f..ab4c083b65 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -84,6 +84,7 @@ private slots: void aggressiveGc(); void cachedGetterLookup_qtbug_75335(); void createComponentOnSingletonDestruction(); + void uiLanguage(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1175,6 +1176,38 @@ void tst_qqmlengine::createComponentOnSingletonDestruction() QVERIFY(obj); } +void tst_qqmlengine::uiLanguage() +{ + QQmlEngine engine; + + QObject::connect(&engine, &QJSEngine::uiLanguageChanged, [&engine]() { + engine.retranslate(); + }); + + QSignalSpy uiLanguageChangeSpy(&engine, SIGNAL(uiLanguageChanged())); + + QQmlComponent component(&engine, testFileUrl("uiLanguage.qml")); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QVERIFY(engine.uiLanguage().isEmpty()); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 1); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + engine.setUiLanguage("TestLanguage"); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 2); + QCOMPARE(object->property("chosenLanguage").toString(), "TestLanguage"); + + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + engine.evaluate("Qt.uiLanguage = \"anotherLanguage\""); + QCOMPARE(engine.uiLanguage(), QString("anotherLanguage")); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 3); + QCOMPARE(object->property("chosenLanguage").toString(), "anotherLanguage"); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" |