diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index ec92404a154..5c4ebfedf14 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -638,27 +638,43 @@ static QString find_translation(const QLocale & locale, languages.insert(i + 1, lowerLang); } - for (QString localeName : std::as_const(languages)) { - // try the complete locale name first and progressively truncate from - // the end until a matching language tag is found (with or without suffix) + QStringList candidates; + // assume 3 segments for each entry + candidates.reserve(languages.size() * 3); + for (QStringView language : std::as_const(languages)) { + // for each language, add versions without territory and script for (;;) { - realname += localeName + suffixOrDotQM; - if (is_readable_file(realname)) - return realname; - - realname.truncate(realNameBaseSize + localeName.size()); - if (is_readable_file(realname)) - return realname; - - realname.truncate(realNameBaseSize); - - int rightmost = localeName.lastIndexOf(u'_'); + candidates += language.toString(); + int rightmost = language.lastIndexOf(u'_'); if (rightmost <= 0) - break; // no truncations anymore, break - localeName.truncate(rightmost); + break; + language.truncate(rightmost); } } + // now sort the list of candidates + std::sort(candidates.begin(), candidates.end(), [](const auto &lhs, const auto &rhs){ + const auto rhsSegments = rhs.count(u'_'); + const auto lhsSegments = lhs.count(u'_'); + // candidates with more segments come first + if (rhsSegments != lhsSegments) + return rhsSegments < lhsSegments; + // candidates with same number of segments are sorted alphanumerically + return lhs < rhs; + }); + + for (const QString &localeName : std::as_const(candidates)) { + realname += localeName + suffixOrDotQM; + if (is_readable_file(realname)) + return realname; + + realname.truncate(realNameBaseSize + localeName.size()); + if (is_readable_file(realname)) + return realname; + + realname.truncate(realNameBaseSize); + } + const int realNameBaseSizeFallbacks = path.size() + filename.size(); // realname == path + filename + prefix; diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp index c76500ea119..f214572cfa2 100644 --- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp +++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp @@ -27,6 +27,7 @@ private slots: void load_data(); void load(); + void loadLocale_data(); void loadLocale(); void threadLoad(); void testLanguageChange(); @@ -116,12 +117,23 @@ void tst_QTranslator::load() } } +void tst_QTranslator::loadLocale_data() +{ + QTest::addColumn("localeName"); + QTest::addColumn("fileNames"); + + QTest::addRow("US English") + << "en_US" + << QStringList{"en_US.qm", "en_US", "en.qm", "en"}; + QTest::addRow("Australia") + << "en_AU" + << QStringList{"en_Latn_AU.qm", "en_AU.qm", "en.qm"}; +} + void tst_QTranslator::loadLocale() { - QLocale locale; - auto localeName = locale.uiLanguages(QLocale::TagSeparator::Underscore).value(0); - if (localeName.isEmpty()) - QSKIP("This test requires at least one available UI language."); + QFETCH(const QString, localeName); + QFETCH(const QStringList, fileNames); QByteArray ba; { @@ -134,36 +146,16 @@ void tst_QTranslator::loadLocale() QTemporaryDir dir; QVERIFY(dir.isValid()); - auto path = dir.path(); + const auto path = dir.path(); QFile file(path + "/dummy"); QVERIFY2(file.open(QFile::WriteOnly), qPrintable(file.errorString())); QCOMPARE(file.write(ba), ba.size()); file.close(); - /* - Test the following order: - - /tmp/tmpDir/foo-en_US.qm - /tmp/tmpDir/foo-en_US - /tmp/tmpDir/foo-en.qm - /tmp/tmpDir/foo-en - /tmp/tmpDir/foo.qm - /tmp/tmpDir/foo- - /tmp/tmpDir/foo - */ - QStringList files; - while (true) { - files.append(path + "/foo-" + localeName + ".qm"); + for (const auto &fileName : fileNames) { + files.append(path + "/foo-" + fileName); QVERIFY2(file.copy(files.last()), qPrintable(file.errorString())); - - files.append(path + "/foo-" + localeName); - QVERIFY2(file.copy(files.last()), qPrintable(file.errorString())); - - int rightmost = localeName.lastIndexOf(QLatin1Char('_')); - if (rightmost <= 0) - break; - localeName.truncate(rightmost); } files.append(path + "/foo.qm"); @@ -175,10 +167,14 @@ void tst_QTranslator::loadLocale() files.append(path + "/foo"); QVERIFY2(file.rename(files.last()), qPrintable(file.errorString())); + QLocale locale(localeName); QTranslator tor; for (const auto &filePath : files) { QVERIFY(tor.load(locale, "foo", "-", path, ".qm")); - QCOMPARE(tor.filePath(), filePath); + // As the file system might be case insensitive, we can't guarantee that + // the casing of the file name is preserved. The order of loading + // en_AU vs en_au if both exist is undefined anyway. + QCOMPARE(tor.filePath().toLower(), filePath.toLower()); QVERIFY2(file.remove(filePath), qPrintable(file.errorString())); } }