From ef5d9702a9726aca186fb89f9860b486f48db301 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 28 Apr 2025 11:01:19 +0200 Subject: [PATCH] Move system locale later in uiLanguages(), when adding it as fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ø --- src/corelib/text/qlocale.cpp | 36 ++++++++++++------- .../auto/corelib/text/qlocale/tst_qlocale.cpp | 20 ++++++----- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 430c18f511d..d2fdcb6dc81 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -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) { diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index 254d4312418..35f14d459b9 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -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