diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index cdea4ac988f..44c8f9b18d1 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -23,6 +23,8 @@ #include "qendian.h" #include "qresource.h" +#include + #if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) # define QT_USE_MMAP # include "private/qcore_unix_p.h" @@ -628,7 +630,7 @@ static QString find_translation(const QLocale & locale, QString realname; realname += path + filename + prefix; // using += in the hope for some reserve capacity - const int realNameBaseSize = realname.size(); + const qsizetype realNameBaseSize = realname.size(); // see http://www.unicode.org/reports/tr35/#LanguageMatching for inspiration @@ -641,7 +643,51 @@ static QString find_translation(const QLocale & locale, // Windows (in other words: this codepath is *not* UNIX-only). QStringList languages = locale.uiLanguages(QLocale::TagSeparator::Underscore); qCDebug(lcTranslator) << "Requested UI languages" << languages; - for (int i = languages.size() - 1; i >= 0; --i) { + + QDuplicateTracker duplicates(languages.size() * 2); + for (const auto &l : std::as_const(languages)) + (void)duplicates.hasSeen(l); + + for (qsizetype i = languages.size() - 1; i >= 0; --i) { + QString language = languages.at(i); + + // Add candidates for each entry where we progressively truncate sections + // from the end, until a matching language tag is found. For compatibility + // reasons (see QTBUG-124898) we add a special case: if we find a + // language_Script_Territory entry (i.e. an entry with two sections), try + // language_Territory as well as language_Script. Use QDuplicateTracker + // so that we don't add any entries as fallbacks that are already in the + // list anyway. + // This is a kludge, and such entries are added at the end of the candidate + // list; from 6.9 on, this is fixed in QLocale::uiLanguages(). + QStringList fallbacks; + const auto addIfNew = [&duplicates, &fallbacks](const QString &fallback) { + if (!duplicates.hasSeen(fallback)) + fallbacks.append(fallback); + }; + + while (true) { + const qsizetype last = language.lastIndexOf(u'_'); + if (last < 0) // no more sections + break; + + const qsizetype first = language.indexOf(u'_'); + // two sections, add fallback without script + if (first != last && language.count(u'_') == 2) { + QString fallback = language.left(first) + language.mid(last); + addIfNew(fallback); + } + QString fallback = language.left(last); + addIfNew(fallback); + + language.truncate(last); + } + for (qsizetype j = fallbacks.size() - 1; j >= 0; --j) + languages.insert(i + 1, fallbacks.at(j)); + } + + qCDebug(lcTranslator) << "Augmented UI languages" << languages; + for (qsizetype i = languages.size() - 1; i >= 0; --i) { const QString &lang = languages.at(i); QString lowerLang = lang.toLower(); if (lang != lowerLang) @@ -649,24 +695,16 @@ static QString find_translation(const QLocale & locale, } 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) - for (;;) { - realname += localeName + suffixOrDotQM; - if (is_readable_file(realname)) - return realname; + // try each locale with and without suffix + 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 + localeName.size()); + if (is_readable_file(realname)) + return realname; - realname.truncate(realNameBaseSize); - - int rightmost = localeName.lastIndexOf(u'_'); - if (rightmost <= 0) - break; // no truncations anymore, break - localeName.truncate(rightmost); - } + realname.truncate(realNameBaseSize); } const int realNameBaseSizeFallbacks = path.size() + filename.size(); diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp index 3a60040b71c..fae3b4118e1 100644 --- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp +++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp @@ -254,16 +254,6 @@ void tst_QTranslator::loadLocale() // more general alternatives, or to languages with lower priority. for (const auto &filePath : files) { QVERIFY(tor.load(wantedLocale, "foo", "-", path, ".qm")); - // we search 'en_Latn_US/AU', 'en_Latn', and 'en', but never 'en_US/AU' - if (filePath.endsWith("en_US") || filePath.endsWith("en_US.qm")) { - QEXPECT_FAIL("US English", - "QTBUG-124898 - we search 'en_Latn_US', 'en_Latn', and 'en', but never 'en_US", - Continue); - } else if (filePath.endsWith("en_AU") || filePath.endsWith("en_AU.qm")) { - QEXPECT_FAIL("Australia", - "QTBUG-124898 - we search 'en_Latn_AU', 'en_Latn', and 'en', but never 'en_AU", - Continue); - } QCOMPARE(tor.filePath(), filePath); QVERIFY2(file.remove(filePath), qPrintable(file.errorString())); }