Move system locale later in uiLanguages(), when adding it as fallback

Place it after the last mutually intelligible entry (same language and
script), or at the end if none, rather than at the start. The fact
that we add it at all was prompted by macOS leaving it out, which can
happen if the user has specified custom preferred languages that don't
match what they've specified for locale.

Adapted various test-cases to match the new behavior.

[ChangeLog][QtCore][QLocale] When the system locale itself is missing
from what the system reports as suitable languages into which to
translate the UI, it is now inserted later in the list, where
previously it was added at the front.

Task-number: QTBUG-104930
Change-Id: I378d266d6f8894cb17df9d09e841a266da73ecee
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Edward Welbourne 2025-04-28 11:01:19 +02:00
parent 55f69018b3
commit ef5d9702a9
2 changed files with 34 additions and 22 deletions

View File

@ -5215,23 +5215,33 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
localeIds.append(QLocaleId::fromName(entry));
if (localeIds.isEmpty())
localeIds.append(systemLocale()->fallbackLocale().d->m_data->id());
// If the system locale (isn't C and) didn't include itself in the list,
// or as fallback, presume to know better than it and put its name
// first. (Known issue, QTBUG-104930, on some macOS versions when in
// locale en_DE.) Our translation system might have a translation for a
// locale the platform doesn't believe in.
/* Note: Darwin allows entirely independent choice of locale and of
preferred languages, so it's possible the locale implied by
LanguageId, ScriptId and TerritoryId is absent from the UILanguages
list and that this faithfully reflects the user's wishes. None the
less, we include it (if it isn't C) in the list below, after the last
with the same language and script or (if none has) at the end, in
case there is no better option available. (See, QTBUG-104930.)
*/
const QString name = QString::fromLatin1(d->m_data->id().name(sep)); // Raw name
if (!name.isEmpty() && language() != C && !uiLanguages.contains(name)) {
// That uses contains(name) as a cheap pre-test, but there may be an
// entry that matches this on purging likely subtags.
const QLocaleId id = d->m_data->id();
const QLocaleId mine = id.withLikelySubtagsRemoved();
const auto isMine = [mine](const QString &entry) {
return QLocaleId::fromName(entry).withLikelySubtagsRemoved() == mine;
};
if (std::none_of(uiLanguages.constBegin(), uiLanguages.constEnd(), isMine)) {
localeIds.prepend(id);
uiLanguages.prepend(QString::fromLatin1(id.name(sep)));
const QLocaleId max = id.withLikelySubtagsAdded();
const QLocaleId mine = max.withLikelySubtagsRemoved();
// Default to putting at the end:
qsizetype lastAlike = uiLanguages.size() - 1;
bool seen = false;
for (qsizetype i = 0; !seen && i < uiLanguages.size(); ++i) {
const auto its = QLocaleId::fromName(uiLanguages.at(i)).withLikelySubtagsAdded();
seen = its.withLikelySubtagsRemoved() == mine;
if (!seen && its.language_id == max.language_id && its.script_id == max.script_id)
lastAlike = i;
}
if (!seen) {
localeIds.insert(lastAlike + 1, id);
uiLanguages.insert(lastAlike + 1, QString::fromLatin1(id.name(sep)));
}
}
} else
@ -5410,7 +5420,7 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
}
}
if (found) // Don't duplicate.
break; // any further truncations of prefix would also be found.
continue; // Some shorter truncations may still be missing.
// Now we're committed to adding it, get it into known:
(void) known.hasSeen(prefix);
if (justAfter) {

View File

@ -4540,8 +4540,8 @@ void tst_QLocale::mySystemLocale_data()
u"nb"_s};
QTest::addRow("en-Latn") // Android crash
<< u"en-Latn"_s << QLocale::English
<< QStringList{u"en-Latn-US"_s, u"en-US"_s, u"en-Latn"_s, u"en"_s,
u"en-Latn-NO"_s, u"en-NO"_s};
<< QStringList{u"en-Latn-NO"_s, u"en-NO"_s,
u"en-Latn-US"_s, u"en-US"_s, u"en-Latn"_s, u"en"_s};
QTest::addRow("anglo-dutch") // QTBUG-131894
<< u"en-NL"_s << QLocale::English
@ -4571,8 +4571,8 @@ void tst_QLocale::mySystemLocale_data()
QTest::addRow("english-germany")
<< u"en-DE"_s << QLocale::English
// First two were missed out before fix to QTBUG-104930:
<< QStringList{u"en-Latn-DE"_s, u"en-DE"_s,
u"en-Latn-GB"_s, u"en-GB"_s,
<< QStringList{u"en-Latn-GB"_s, u"en-GB"_s,
u"en-Latn-DE"_s, u"en-DE"_s,
u"de-Latn-DE"_s, u"de-DE"_s, u"de-Latn"_s, u"de"_s,
// Fallbacks implied by those:
u"en-Latn"_s, u"en"_s};
@ -4609,7 +4609,8 @@ void tst_QLocale::mySystemLocale_data()
QTest::addRow("pa-Arab-GB")
<< u"pa-Arab-GB"_s << QLocale::Punjabi
<< QStringList{u"pa-Arab-GB"_s, u"pa-Arab-PK"_s, u"pa-PK"_s, u"pa-Arab"_s,
<< QStringList{u"pa-Arab-PK"_s, u"pa-PK"_s, u"pa-Arab"_s,
u"pa-Arab-GB"_s,
u"en-Latn-GB"_s, u"en-GB"_s,
// Truncations:
u"en-Latn"_s, u"en"_s,
@ -4624,8 +4625,9 @@ void tst_QLocale::mySystemLocale_data()
u"en-Latn-GB"_s, u"en-GB"_s, u"en-Latn"_s, u"en"_s};
QTest::newRow("en-mixed")
<< u"en-FO"_s << QLocale::English
<< QStringList{u"en-Latn-FO"_s, u"en-FO"_s, u"en-Latn-DK"_s, u"en-DK"_s,
<< QStringList{u"en-Latn-DK"_s, u"en-DK"_s,
u"en-Latn-GB"_s, u"en-GB"_s,
u"en-Latn-FO"_s, u"en-FO"_s,
u"fo-Latn-FO"_s, u"fo-FO"_s, u"fo-Latn"_s, u"fo"_s,
u"da-Latn-FO"_s, u"da-FO"_s,
u"da-Latn-DK"_s, u"da-DK"_s, u"da-Latn"_s, u"da"_s,
@ -4633,12 +4635,12 @@ void tst_QLocale::mySystemLocale_data()
u"en-Latn"_s, u"en"_s};
QTest::newRow("polylingual-CA")
<< u"de-CA"_s << QLocale::German
<< QStringList{u"de-Latn-CA"_s, u"de-CA"_s, u"en-Latn-CA"_s, u"en-CA"_s,
u"fr-Latn-CA"_s, u"fr-CA"_s, u"de-Latn-AT"_s, u"de-AT"_s,
<< QStringList{u"en-Latn-CA"_s, u"en-CA"_s, u"fr-Latn-CA"_s, u"fr-CA"_s,
u"de-Latn-AT"_s, u"de-AT"_s, u"de-Latn-CA"_s, u"de-CA"_s,
u"en-Latn-GB"_s, u"en-GB"_s,
u"fr-Latn-FR"_s, u"fr-FR"_s, u"fr-Latn"_s, u"fr"_s,
// Fallbacks:
u"de-Latn"_s, u"de"_s, u"en-Latn"_s, u"en"_s};
u"en-Latn"_s, u"en"_s, u"de-Latn"_s, u"de"_s};
QTest::newRow("und-US")
<< u"und-US"_s << QLocale::C