Amend QLocale::uiLanguages() order
Include each of an entry's truncations right after the entry if its script (or, where absent, the one implied via likely subtag rules) matches and there is no later entry that it should appear after. This is a compromise between prefering strictly equivalent matches to potentially incompatible ones and prefering candidates derived from earlier entries in the original list. Defer to QLocaleSelector (see QTBUG-112765) the addition of options to give the caller control over how to make that compromise. On picking to 6.9 (by hand, since initial pick failed for want of a precursor I've now picked) I've decided to pick to 6.8, which shall surely get conflicts, since we want to fix the same issue there. Pick-to: 6.8 Fixes: QTBUG-131894 Change-Id: I8750f4f40530c94638853388f47fcae1008db8d5 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit cbf49f735e3cca85922a85d6548666816686db78)
This commit is contained in:
parent
45c9a35d35
commit
631fa1915e
@ -5150,7 +5150,7 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
|
|||||||
// Third pass: add truncations, when not already present.
|
// Third pass: add truncations, when not already present.
|
||||||
// Cubic in list length, but hopefully that's at most a dozen or so.
|
// Cubic in list length, but hopefully that's at most a dozen or so.
|
||||||
const QLatin1Char cut(sep);
|
const QLatin1Char cut(sep);
|
||||||
const auto hasPrefix = [cut](QStringView name, QStringView stem) {
|
const auto hasPrefix = [cut](auto name, QStringView stem) {
|
||||||
// A prefix only counts if it's either full or followed by a separator.
|
// A prefix only counts if it's either full or followed by a separator.
|
||||||
return name.startsWith(stem)
|
return name.startsWith(stem)
|
||||||
&& (name.size() == stem.size() || name.at(stem.size()) == cut);
|
&& (name.size() == stem.size() || name.at(stem.size()) == cut);
|
||||||
@ -5159,9 +5159,19 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
|
|||||||
const QString entry = uiLanguages.at(i);
|
const QString entry = uiLanguages.at(i);
|
||||||
if (hasPrefix(entry, u"C") || hasPrefix(entry, u"und"))
|
if (hasPrefix(entry, u"C") || hasPrefix(entry, u"und"))
|
||||||
continue;
|
continue;
|
||||||
const qsizetype stopAt = uiLanguages.size();
|
const ushort script = QLocaleId::fromName(entry).withLikelySubtagsAdded().script_id;
|
||||||
|
qsizetype stopAt = uiLanguages.size();
|
||||||
QString prefix = entry;
|
QString prefix = entry;
|
||||||
qsizetype at = 0;
|
qsizetype at = 0;
|
||||||
|
/* By default we append but if no later entry has this as a prefix and
|
||||||
|
the locale it implies would use the same script as entry, put it
|
||||||
|
after entry instead. Thus [en-NL, nl-NL, en-GB] will append en but
|
||||||
|
[en-NL, en-GB, nl-NL] will put it before nl-NL, for example. We
|
||||||
|
require a script match so we don't pick translations that the user
|
||||||
|
cannot read, despite knowing the language. (Ideally that would be
|
||||||
|
a constraint the caller can opt into / out of. See QTBUG-112765.)
|
||||||
|
*/
|
||||||
|
bool justAfter = QLocaleId::fromName(prefix).withLikelySubtagsAdded().script_id == script;
|
||||||
while ((at = prefix.lastIndexOf(cut)) > 0) {
|
while ((at = prefix.lastIndexOf(cut)) > 0) {
|
||||||
prefix = prefix.first(at);
|
prefix = prefix.first(at);
|
||||||
// Don't test with hasSeen() as we might defer adding to later, when
|
// Don't test with hasSeen() as we might defer adding to later, when
|
||||||
@ -5170,11 +5180,19 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
|
|||||||
bool found = known.contains(prefix);
|
bool found = known.contains(prefix);
|
||||||
for (qsizetype j = i + 1; !found && j < stopAt; ++j) {
|
for (qsizetype j = i + 1; !found && j < stopAt; ++j) {
|
||||||
QString later = uiLanguages.at(j);
|
QString later = uiLanguages.at(j);
|
||||||
if (!later.startsWith(prefix))
|
if (!later.startsWith(prefix)) {
|
||||||
|
const QByteArray laterFull =
|
||||||
|
QLocaleId::fromName(later.replace(cut, u'-')
|
||||||
|
).withLikelySubtagsAdded().name(sep);
|
||||||
|
// When prefix matches a later entry's max, it belongs later.
|
||||||
|
if (hasPrefix(QLatin1StringView(laterFull), prefix))
|
||||||
|
justAfter = false;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// The duplicate tracker would already have spotted if equal:
|
// The duplicate tracker would already have spotted if equal:
|
||||||
Q_ASSERT(later.size() > prefix.size());
|
Q_ASSERT(later.size() > prefix.size());
|
||||||
if (later.at(prefix.size()) == cut) {
|
if (later.at(prefix.size()) == cut) {
|
||||||
|
justAfter = false;
|
||||||
// Prefix match. Shall produce the same prefix, but possibly
|
// Prefix match. Shall produce the same prefix, but possibly
|
||||||
// after prefixes of other entries in the list. If later has
|
// after prefixes of other entries in the list. If later has
|
||||||
// a longer prefix not yet in the list, we want that before
|
// a longer prefix not yet in the list, we want that before
|
||||||
@ -5196,7 +5214,12 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
|
|||||||
break; // any further truncations of prefix would also be found.
|
break; // any further truncations of prefix would also be found.
|
||||||
// Now we're committed to adding it, get it into known:
|
// Now we're committed to adding it, get it into known:
|
||||||
(void) known.hasSeen(prefix);
|
(void) known.hasSeen(prefix);
|
||||||
uiLanguages.append(entry.first(prefix.size()));
|
if (justAfter) {
|
||||||
|
uiLanguages.insert(++i, prefix);
|
||||||
|
++stopAt; // All later entries have moved one step later.
|
||||||
|
} else {
|
||||||
|
uiLanguages.append(prefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4094,6 +4094,8 @@ public:
|
|||||||
return QVariant(QStringList{u"en-DK"_s, u"en-GB"_s, u"fo-FO"_s,
|
return QVariant(QStringList{u"en-DK"_s, u"en-GB"_s, u"fo-FO"_s,
|
||||||
u"da-FO"_s, u"da-DK"_s});
|
u"da-FO"_s, u"da-DK"_s});
|
||||||
}
|
}
|
||||||
|
if (m_name == u"en-NL") // Anglophone in Netherlands:
|
||||||
|
return QVariant(QStringList{u"en-NL"_s, u"nl-NL"_s});
|
||||||
if (m_name == u"en-NL-GB") // Netherlander at work for a GB-ish employer:
|
if (m_name == u"en-NL-GB") // Netherlander at work for a GB-ish employer:
|
||||||
return QVariant(QStringList{u"en-NL"_s, u"nl-NL"_s, u"en-GB"_s});
|
return QVariant(QStringList{u"en-NL"_s, u"nl-NL"_s, u"en-GB"_s});
|
||||||
if (m_name == u"de-CA") { // Imagine a 2nd generation Canadian of de-AT ancestry ...
|
if (m_name == u"de-CA") { // Imagine a 2nd generation Canadian of de-AT ancestry ...
|
||||||
@ -4161,6 +4163,12 @@ void tst_QLocale::mySystemLocale_data()
|
|||||||
<< QStringList{u"en-Latn"_s, u"en-Latn-US"_s, u"en-US"_s, u"en"_s,
|
<< QStringList{u"en-Latn"_s, u"en-Latn-US"_s, u"en-US"_s, u"en"_s,
|
||||||
u"en-NO"_s, u"en-Latn-NO"_s};
|
u"en-NO"_s, u"en-Latn-NO"_s};
|
||||||
|
|
||||||
|
QTest::addRow("anglo-dutch") // QTBUG-131894
|
||||||
|
<< u"en-NL"_s << QLocale::English
|
||||||
|
<< QStringList{u"en-NL"_s, u"en-Latn-NL"_s,
|
||||||
|
// No later en-Latn-* or en-* in the list, so include truncations now:
|
||||||
|
u"en-Latn"_s, u"en"_s,
|
||||||
|
u"nl-NL"_s, u"nl-Latn-NL"_s, u"nl-Latn"_s, u"nl"_s};
|
||||||
QTest::addRow("anglo-dutch-GB")
|
QTest::addRow("anglo-dutch-GB")
|
||||||
<< u"en-NL-GB"_s << QLocale::English
|
<< u"en-NL-GB"_s << QLocale::English
|
||||||
<< QStringList{u"en-NL"_s, u"en-Latn-NL"_s,
|
<< QStringList{u"en-NL"_s, u"en-Latn-NL"_s,
|
||||||
@ -4218,9 +4226,8 @@ void tst_QLocale::mySystemLocale_data()
|
|||||||
|
|
||||||
QTest::newRow("en-Dsrt-GB")
|
QTest::newRow("en-Dsrt-GB")
|
||||||
<< u"en-Dsrt-GB"_s << QLocale::English
|
<< u"en-Dsrt-GB"_s << QLocale::English
|
||||||
<< QStringList{u"en-Dsrt-GB"_s, u"en-GB"_s, u"en-Latn-GB"_s,
|
<< QStringList{u"en-Dsrt-GB"_s, u"en-Dsrt"_s,
|
||||||
// Fallbacks - plain "en" last, not between the others:
|
u"en-GB"_s, u"en-Latn-GB"_s, u"en-Latn"_s, u"en"_s};
|
||||||
u"en-Dsrt"_s, u"en-Latn"_s, u"en"_s};
|
|
||||||
QTest::newRow("en-mixed")
|
QTest::newRow("en-mixed")
|
||||||
<< u"en-FO"_s << QLocale::English
|
<< u"en-FO"_s << QLocale::English
|
||||||
<< QStringList{u"en-FO"_s, u"en-Latn-FO"_s, u"en-DK"_s, u"en-Latn-DK"_s,
|
<< QStringList{u"en-FO"_s, u"en-Latn-FO"_s, u"en-DK"_s, u"en-Latn-DK"_s,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user