Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QTranslator>
#include <QQmlContext>
#include <QQuickItem>
#include <private/qqmlengine_p.h>
#include <private/qqmltypedata_p.h>
#include <private/qqmldelegatemodel_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
class tst_qqmltranslation : public QQmlDataTest
{
Q_OBJECT
public:
tst_qqmltranslation() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private slots:
void translation_data();
void translation();
void idTranslation();
void translationChange();
void preferJSContext();
void pragmaContext();
void pragmaContextStringLiteral();
void listModel();
};
void tst_qqmltranslation::translation_data()
{
QTest::addColumn<QString>("translation");
QTest::addColumn<QUrl>("testFile");
QTest::addColumn<bool>("verifyCompiledData");
QTest::newRow("qml") << QStringLiteral("qml_fr") << testFileUrl("translation.qml") << true;
QTest::newRow("qrc") << QStringLiteral(":/qml_fr.qm") << QUrl("qrc:/translation.qml") << true;
QTest::newRow("js") << QStringLiteral("qml_fr") << testFileUrl("jstranslation.qml") << false;
}
void tst_qqmltranslation::translation()
{
QFETCH(QString, translation);
QFETCH(QUrl, testFile);
QFETCH(bool, verifyCompiledData);
QTranslator translator;
QVERIFY(translator.load(translation, dataDirectory()));
QCoreApplication::installTranslator(&translator);
QQmlEngine engine;
QQmlComponent component(&engine, testFile);
std::unique_ptr<QObject> object { component.create() };
QVERIFY(object);
if (verifyCompiledData) {
QQmlContext *context = qmlContext(object.get());
QQmlRefPointer<QQmlTypeData> typeData
= QQmlTypeLoader::get(context->engine())->getType(context->baseUrl());
QVERIFY(!typeData->backupSourceCode().isValid());
QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit();
QVERIFY(compilationUnit);
QSet<QString> compiledTranslations;
compiledTranslations << QStringLiteral("basic")
<< QStringLiteral("basic2")
<< QStringLiteral("disambiguation")
<< QStringLiteral("disambiguation2")
<< QStringLiteral("singular")
<< QStringLiteral("plural")
<< QStringLiteral("emptyContext");
const QV4::CompiledData::Object *rootObject
= compilationUnit->qmlData->objectAt(/*root object*/0);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
const bool expectCompiledTranslation = compiledTranslations.contains(propertyName);
if (expectCompiledTranslation) {
if (binding->type() != QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_Translation);
} else {
if (binding->type() == QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation";
QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
QCOMPARE(object->property("basic").toString(), QLatin1String("bonjour"));
QCOMPARE(object->property("basic2").toString(), QLatin1String("au revoir"));
QCOMPARE(object->property("basic3").toString(), QLatin1String("bonjour"));
QCOMPARE(object->property("disambiguation").toString(), QLatin1String("salut"));
QCOMPARE(object->property("disambiguation2").toString(), QString::fromUtf8("\xc3\xa0 plus tard"));
QCOMPARE(object->property("disambiguation3").toString(), QLatin1String("salut"));
QCOMPARE(object->property("noop").toString(), QLatin1String("bonjour"));
QCOMPARE(object->property("noop2").toString(), QLatin1String("au revoir"));
QCOMPARE(object->property("singular").toString(), QLatin1String("1 canard"));
QCOMPARE(object->property("singular2").toString(), QLatin1String("1 canard"));
QCOMPARE(object->property("plural").toString(), QLatin1String("2 canards"));
QCOMPARE(object->property("plural2").toString(), QLatin1String("2 canards"));
QCOMPARE(object->property("emptyContext").toString(), QLatin1String("hello"));
QCoreApplication::removeTranslator(&translator);
}
void tst_qqmltranslation::idTranslation()
{
QTranslator translator;
QVERIFY(translator.load(QLatin1String("qmlid_fr"), dataDirectory()));
QCoreApplication::installTranslator(&translator);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("idtranslation.qml"));
std::unique_ptr<QObject> object { component.create() };
QVERIFY(object);
{
QQmlContext *context = qmlContext(object.get());
QQmlRefPointer<QQmlTypeData> typeData
= QQmlTypeLoader::get(context->engine())->getType(context->baseUrl());
QVERIFY(!typeData->backupSourceCode().isValid());
QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit();
QVERIFY(compilationUnit);
const QV4::CompiledData::Object *rootObject
= compilationUnit->qmlData->objectAt(/*root object*/0);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
if (propertyName == "idTranslation") {
if (binding->type() != QV4::CompiledData::Binding::Type_TranslationById)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_TranslationById);
} else {
QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
QCOMPARE(object->property("idTranslation").toString(), QLatin1String("bonjour tout le monde"));
QCOMPARE(object->property("idTranslation2").toString(), QLatin1String("bonjour tout le monde"));
QCOMPARE(object->property("idTranslation3").toString(), QLatin1String("bonjour tout le monde"));
QCoreApplication::removeTranslator(&translator);
}
class CppTranslationBase : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
public:
Q_PROPERTY(QString qProperty MEMBER qProperty BINDABLE bindableQProperty)
QBindable<QString> bindableQProperty() {return QBindable<QString>(&qProperty); }
private:
QProperty<QString> qProperty;
};
class DummyTranslator : public QTranslator
{
Q_OBJECT
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override
{
Q_UNUSED(context);
Q_UNUSED(disambiguation);
Q_UNUSED(n);
if (!qstrcmp(sourceText, "translate me"))
return QString::fromUtf8("xxx");
if (!qstrcmp(sourceText, "English in mylibrary") && !qstrcmp(context, "mylibrary"))
return QString::fromUtf8("Deutsch in mylibrary");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation"))
return QString::fromUtf8("Deutsch in Setzung");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragma"))
return QString::fromUtf8("Deutsch in Setzung pragma");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragmaStringLiteral"))
return QString::fromUtf8("Deutsch in Setzung pragma string literal");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "setContext"))
return QString::fromUtf8("Deutsch in Setzung set");
if (!qstrcmp(sourceText, "soup"))
return QString::fromUtf8("Suppe");
if (!qstrcmp(sourceText, "fish"))
return QString::fromUtf8("Fisch");
if (!qstrcmp(sourceText, "meat"))
return QString::fromUtf8("Fleisch");
if (!qstrcmp(sourceText, "bread"))
return QString::fromUtf8("Brot");
if (!qstrcmp(sourceText, "sizzling hot pebbles"))
return QString::fromUtf8("Eiersalat");
return QString();
}
bool isEmpty() const override
{
return false;
}
};
void tst_qqmltranslation::translationChange()
{
QQmlEngine engine;
qmlRegisterTypesAndRevisions<CppTranslationBase>("Test", 1);
QQmlComponent component(&engine, testFileUrl("translationChange.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate"));
QCOMPARE(object->property("text1").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("text2").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("text3").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("qProperty").toString(), QString::fromUtf8("translate me"));
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
QEvent ev(QEvent::LanguageChange);
QCoreApplication::sendEvent(&engine, &ev);
engine.setUiLanguage("xxx");
QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate"));
QCOMPARE(object->property("text1").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("text2").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("text3").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("qProperty").toString(), QString::fromUtf8("xxx"));
QCoreApplication::removeTranslator(&translator);
}
void tst_qqmltranslation::preferJSContext()
{
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("preferjs.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
QCOMPARE(object->property("german1").toString(),
QStringLiteral("Deutsch in Setzung"));
QCOMPARE(object->property("german2").toString(),
QStringLiteral("Deutsch in mylibrary"));
QCoreApplication::removeTranslator(&translator);
}
void tst_qqmltranslation::pragmaContext()
{
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("pragmacontext.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
QCOMPARE(object->property("german1").toString(),
QStringLiteral("Deutsch in Setzung pragma"));
QCOMPARE(object->property("german2").toString(),
QStringLiteral("Deutsch in Setzung set"));
QCoreApplication::removeTranslator(&translator);
}
void tst_qqmltranslation::pragmaContextStringLiteral()
{
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("pragmacontextstringliteral.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
QCOMPARE(object->property("german1").toString(),
QStringLiteral("Deutsch in Setzung pragma string literal"));
QCOMPARE(object->property("german2").toString(),
QStringLiteral("Deutsch in Setzung set"));
QCoreApplication::removeTranslator(&translator);
}
void tst_qqmltranslation::listModel()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("translatedElements.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> o(component.create());
QVERIFY(o);
QQmlDelegateModel *model = qobject_cast<QQmlDelegateModel *>(o.data());
QVERIFY(model);
QCOMPARE(model->count(), 5);
QCOMPARE(model->object(0)->property("dish").toString(), QStringLiteral("soup"));
QCOMPARE(model->object(1)->property("dish").toString(), QStringLiteral("fish"));
QCOMPARE(model->object(2)->property("dish").toString(), QStringLiteral("meat"));
QCOMPARE(model->object(3)->property("dish").toString(), QStringLiteral("bread"));
QCOMPARE(model->object(4)->property("dish").toString(), QStringLiteral("sizzling hot pebbles"));
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
engine.setUiLanguage(QStringLiteral("xxx"));
engine.retranslate();
QCOMPARE(model->object(0)->property("dish").toString(), QStringLiteral("Suppe"));
QCOMPARE(model->object(1)->property("dish").toString(), QStringLiteral("Fisch"));
QCOMPARE(model->object(2)->property("dish").toString(), QStringLiteral("Fleisch"));
QCOMPARE(model->object(3)->property("dish").toString(), QStringLiteral("Brot"));
QCOMPARE(model->object(4)->property("dish").toString(), QStringLiteral("Eiersalat"));
}
QTEST_MAIN(tst_qqmltranslation)
#include "tst_qqmltranslation.moc"