QTranslator: work around uiLanguages not including lang_Terr variants
Amends 7a7f2547f3e9ddf4e50decd85189200033193177, which added the triplet including the script before the language_Territory name without it. The truncate-from-the-end algorithm in QTranslator ended up trying the pure language first, and - if found - ignored any more specific territory- variant that might be present. To fix this, replace the truncate-from-the-end algorithm when we try to find files, and instead create a comprehensive list of fallbacks, avoiding duplicates as we go along. Insert those fallbacks right after their base-entry. We then try to find files for each entry, with and without the prefix. This fix is needed only in 6.8 and 6.5; from 6.9 onwards, the change to QLocale::uiLanguages in 84afaafc9c6968dd76fcadc5392065d340543521 fixes this on the correct level, making kludges in QTranslator unnecessary. As a drive-by, replace int indices in for-loops with qsizetype. Fixes: QTBUG-124898 Pick-to: 6.5 Change-Id: Ia3deceb4ed2c6ef5ca0a815cc7b8878bf29ef088 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
26774ee8f5
commit
8c40e8cf12
@ -23,6 +23,8 @@
|
||||
#include "qendian.h"
|
||||
#include "qresource.h"
|
||||
|
||||
#include <QtCore/private/qduplicatetracker_p.h>
|
||||
|
||||
#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<QString> 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();
|
||||
|
@ -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()));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user