Perform uiLanguages() likely-adjusted processing purely on IDs
The collection of translations available to us need not have anything to do with whether CLDR has matching data, so preserve the system UI language list's entries as they are, rather than forcing them through the QLocale constructor's exercise of likely sub-tag rules. Instead, simply parse the given locale tags to QLocaleId instances and use these in the likely-subtag processing to determine what other entries to add to the list in addition to those supplied by the operating system. Since going via QLocale did usually supply a territory, that was included in the BCP 47 name, it's now possible for the given entry to lack the language_territory name, so be sure to add that if missing. This incidentally reduces heap traffic and saves a fair deal of hidden likely-subtag processing in calls to the constructor and bcp47Name(). Expand testing of QLocale::uiLanguages(), both plain and system. In the process, cross-link the two closely-related tests, move a comment on one's _data() to the other's, where it really belongs, and add reporting of the actual lists on failure. Enable MySystemLocale to remember the requested locale's ID, before likely sub-tag processing, so that we can make query() report results for language, script and territory as requested, to ensure the fake system locale really does match what was requested. The new german-britain test failed without it, because there is no de-GB locale in CLDR. Task-number: QTBUG-99531 Change-Id: Ide041577772c442a4413e3b9a590e11140c48f49 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
ebe9aca900
commit
6a14ad4993
|
|
@ -4427,18 +4427,19 @@ QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats
|
|||
QStringList QLocale::uiLanguages() const
|
||||
{
|
||||
QStringList uiLanguages;
|
||||
QList<QLocale> locales;
|
||||
QList<QLocaleId> localeIds;
|
||||
#ifdef QT_NO_SYSTEMLOCALE
|
||||
constexpr bool isSystem = false;
|
||||
#else
|
||||
const bool isSystem = d->m_data == &systemLocaleData;
|
||||
if (isSystem) {
|
||||
uiLanguages = systemLocale()->query(QSystemLocale::UILanguages).toStringList();
|
||||
// ... but we need to include likely-adjusted forms of each of those, too:
|
||||
// ... but we need to include likely-adjusted forms of each of those, too.
|
||||
// For now, collect up locale Ids representing the entries, for later processing:
|
||||
for (const auto &entry : std::as_const(uiLanguages))
|
||||
locales.append(QLocale(entry));
|
||||
if (locales.isEmpty())
|
||||
locales.append(systemLocale()->fallbackLocale());
|
||||
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
|
||||
|
|
@ -4453,20 +4454,17 @@ QStringList QLocale::uiLanguages() const
|
|||
return QLocaleId::fromName(entry).withLikelySubtagsRemoved() == mine;
|
||||
};
|
||||
if (std::none_of(uiLanguages.constBegin(), uiLanguages.constEnd(), isMine)) {
|
||||
locales.prepend(*this);
|
||||
localeIds.prepend(d->m_data->id());
|
||||
uiLanguages.prepend(name);
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
locales.append(*this);
|
||||
localeIds.append(d->m_data->id());
|
||||
}
|
||||
for (qsizetype i = locales.size(); i-- > 0; ) {
|
||||
const QLocale &locale = locales.at(i);
|
||||
const auto data = locale.d->m_data;
|
||||
QLocaleId id = data->id();
|
||||
|
||||
for (qsizetype i = localeIds.size(); i-- > 0; ) {
|
||||
QLocaleId id = localeIds.at(i);
|
||||
qsizetype j;
|
||||
QByteArray prior;
|
||||
if (isSystem && i < uiLanguages.size()) {
|
||||
|
|
@ -4477,35 +4475,49 @@ QStringList QLocale::uiLanguages() const
|
|||
j = i + 1;
|
||||
} else if (id.language_id == C) {
|
||||
// Attempt no likely sub-tag amendments to C:
|
||||
uiLanguages.append(locale.name());
|
||||
uiLanguages.append(QString::fromLatin1(id.name()));
|
||||
continue;
|
||||
} else {
|
||||
// Plain locale or empty system uiLanguages; just append.
|
||||
const QString name = locale.bcp47Name();
|
||||
uiLanguages.append(name);
|
||||
prior = name.toLatin1();
|
||||
prior = id.name();
|
||||
uiLanguages.append(QString::fromLatin1(prior));
|
||||
j = uiLanguages.size();
|
||||
}
|
||||
|
||||
const QLocaleId max = id.withLikelySubtagsAdded();
|
||||
const QLocaleId min = max.withLikelySubtagsRemoved();
|
||||
id.script_id = 0; // For re-use as script-less variant.
|
||||
|
||||
// Include minimal version (last) unless it's what our locale is derived from:
|
||||
if (min.name() != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(min.name()));
|
||||
if (auto name = min.name(); name != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(name));
|
||||
else if (!isSystem)
|
||||
--j; // bcp47Name() matches min(): put more specific forms *before* it.
|
||||
|
||||
// Include scriptless version if likely-equivalent and distinct:
|
||||
if (data->m_script_id && id != min && id.name() != prior
|
||||
&& id.withLikelySubtagsAdded() == max) {
|
||||
uiLanguages.insert(j, QString::fromLatin1(id.name()));
|
||||
if (id.script_id) {
|
||||
// Include scriptless version if likely-equivalent and distinct:
|
||||
id.script_id = 0;
|
||||
if (id != min && id.withLikelySubtagsAdded() == max) {
|
||||
if (auto name = id.name(); name != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(name));
|
||||
}
|
||||
}
|
||||
|
||||
if (!id.territory_id) {
|
||||
Q_ASSERT(!min.territory_id);
|
||||
Q_ASSERT(!id.script_id); // because we just cleared it.
|
||||
// Include version with territory if it likely-equivalent and distinct:
|
||||
id.territory_id = max.territory_id;
|
||||
if (id != max && id.withLikelySubtagsAdded() == max) {
|
||||
if (auto name = id.name(); name != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(name));
|
||||
}
|
||||
}
|
||||
|
||||
// Include version with all likely sub-tags (first) if distinct from the rest:
|
||||
if (max != min && max != id && max.name() != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(max.name()));
|
||||
if (max != min && max != id) {
|
||||
if (auto name = max.name(); name != prior)
|
||||
uiLanguages.insert(j, QString::fromLatin1(name));
|
||||
}
|
||||
}
|
||||
return uiLanguages;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ namespace QIcu {
|
|||
|
||||
struct QLocaleId
|
||||
{
|
||||
[[nodiscard]] static QLocaleId fromName(QStringView name);
|
||||
[[nodiscard]] Q_AUTOTEST_EXPORT static QLocaleId fromName(QStringView name);
|
||||
[[nodiscard]] inline bool operator==(QLocaleId other) const
|
||||
{ return language_id == other.language_id && script_id == other.script_id && territory_id == other.territory_id; }
|
||||
[[nodiscard]] inline bool operator!=(QLocaleId other) const
|
||||
|
|
|
|||
|
|
@ -122,8 +122,10 @@ private slots:
|
|||
void bcp47Name_data();
|
||||
void bcp47Name();
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
void systemLocale_data();
|
||||
void systemLocale();
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_SYSTEMLOCALE
|
||||
void systemLocaleDayAndMonthNames_data();
|
||||
|
|
@ -2939,18 +2941,22 @@ void tst_QLocale::uiLanguages_data()
|
|||
<< QLocale(QLocale::Chinese, QLocale::SimplifiedHanScript, QLocale::China)
|
||||
<< QStringList{QString("zh-Hans-CN"), QString("zh-CN"), QString("zh")};
|
||||
|
||||
// TODO: test actual system backends correctly handle locales with
|
||||
// script-specificity (script listed first is the default, in CLDR v40):
|
||||
// az_{Latn,Cyrl}_AZ, bs_{Latn,Cyrl}_BA, sr_{Cyrl,Latn}_{BA,RS,XK,UZ},
|
||||
// sr_{Latn,Cyrl}_ME, ff_{Latn,Adlm}_{BF,CM,GH,GM,GN,GW,LR,MR,NE,NG,SL,SN},
|
||||
// shi_{Tfng,Latn}_MA, vai_{Vaii,Latn}_LR, zh_{Hant,Hans}_{MO,HK}
|
||||
// We presently map und (or any other unrecognized language) to C, ignoring
|
||||
// what a sub-tag lookup would surely find us.
|
||||
QTest::newRow("und_US") << QLocale("und_US") << QStringList{QString("C")};
|
||||
QTest::newRow("und_Latn") << QLocale("und_Latn") << QStringList{QString("C")};
|
||||
}
|
||||
|
||||
void tst_QLocale::uiLanguages()
|
||||
{
|
||||
// Compare systemLocale(), which tests the same for a stub system locale.
|
||||
QFETCH(const QLocale, locale);
|
||||
QFETCH(const QStringList, all);
|
||||
auto reporter = qScopeGuard([&locale]() {
|
||||
qDebug("\n\t%s", qPrintable(locale.uiLanguages().join(u"\n\t")));
|
||||
});
|
||||
QCOMPARE(locale.uiLanguages(), all);
|
||||
reporter.dismiss();
|
||||
}
|
||||
|
||||
void tst_QLocale::weekendDays()
|
||||
|
|
@ -3229,19 +3235,31 @@ void tst_QLocale::bcp47Name()
|
|||
QCOMPARE(QLocale(QLatin1String(QTest::currentDataTag())).bcp47Name(), expect);
|
||||
}
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
class MySystemLocale : public QSystemLocale
|
||||
{
|
||||
public:
|
||||
MySystemLocale(const QString &locale) : m_name(locale), m_locale(locale)
|
||||
MySystemLocale(const QString &locale)
|
||||
: m_name(locale), m_id(QLocaleId::fromName(locale)), m_locale(locale)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant query(QueryType type, QVariant /*in*/) const override
|
||||
{
|
||||
if (type == UILanguages) {
|
||||
switch (type) {
|
||||
case UILanguages:
|
||||
if (m_name == u"en-DE") // QTBUG-104930: simulate macOS's list not including m_name.
|
||||
return QVariant(QStringList{QStringLiteral("en-GB"), QStringLiteral("de-DE")});
|
||||
return QVariant(QStringList{m_name});
|
||||
case LanguageId:
|
||||
return m_id.language_id;
|
||||
case TerritoryId:
|
||||
return m_id.territory_id;
|
||||
case ScriptId:
|
||||
return m_id.script_id;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
|
@ -3253,6 +3271,7 @@ public:
|
|||
|
||||
private:
|
||||
const QString m_name;
|
||||
const QLocaleId m_id;
|
||||
const QLocale m_locale;
|
||||
};
|
||||
|
||||
|
|
@ -3266,6 +3285,13 @@ void tst_QLocale::systemLocale_data()
|
|||
QTest::addRow("catalan")
|
||||
<< QString("ca") << QLocale::Catalan
|
||||
<< QStringList{QStringLiteral("ca"), QStringLiteral("ca-Latn-ES"), QStringLiteral("ca-ES")};
|
||||
QTest::addRow("catalan-spain")
|
||||
<< QString("ca-ES") << QLocale::Catalan
|
||||
<< QStringList{QStringLiteral("ca-ES"), QStringLiteral("ca-Latn-ES"), QStringLiteral("ca")};
|
||||
QTest::addRow("catalan-latin")
|
||||
<< QString("ca-Latn") << QLocale::Catalan
|
||||
<< QStringList{QStringLiteral("ca-Latn"), QStringLiteral("ca-Latn-ES"),
|
||||
QStringLiteral("ca-ES"), QStringLiteral("ca")};
|
||||
QTest::addRow("ukrainian")
|
||||
<< QString("uk") << QLocale::Ukrainian
|
||||
<< QStringList{QStringLiteral("uk"), QStringLiteral("uk-Cyrl-UA"), QStringLiteral("uk-UA")};
|
||||
|
|
@ -3278,16 +3304,55 @@ void tst_QLocale::systemLocale_data()
|
|||
QTest::addRow("german")
|
||||
<< QString("de") << QLocale::German
|
||||
<< QStringList{QStringLiteral("de"), QStringLiteral("de-Latn-DE"), QStringLiteral("de-DE")};
|
||||
QTest::addRow("german-britain")
|
||||
<< QString("de-GB") << QLocale::German
|
||||
<< QStringList{QStringLiteral("de-GB"), QStringLiteral("de-Latn-GB")};
|
||||
QTest::addRow("chinese-min")
|
||||
<< QString("zh") << QLocale::Chinese
|
||||
<< QStringList{QStringLiteral("zh"), QStringLiteral("zh-Hans-CN"), QStringLiteral("zh-CN")};
|
||||
QTest::addRow("chinese-full")
|
||||
<< QString("zh-Hans-CN") << QLocale::Chinese
|
||||
<< QStringList{QStringLiteral("zh-Hans-CN"), QStringLiteral("zh-CN"), QStringLiteral("zh")};
|
||||
|
||||
// For C, it should preserve what the system gave us but only add "C", never anything more:
|
||||
QTest::addRow("C") << QString("C") << QLocale::C << QStringList{QStringLiteral("C")};
|
||||
QTest::addRow("C-Latn")
|
||||
<< QString("C-Latn") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-Latn"), QStringLiteral("C")};
|
||||
QTest::addRow("C-US")
|
||||
<< QString("C-US") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-US"), QStringLiteral("C")};
|
||||
QTest::addRow("C-Latn-US")
|
||||
<< QString("C-Latn-US") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-Latn-US"), QStringLiteral("C")};
|
||||
QTest::addRow("C-Hans")
|
||||
<< QString("C-Hans") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-Hans"), QStringLiteral("C")};
|
||||
QTest::addRow("C-CN")
|
||||
<< QString("C-CN") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-CN"), QStringLiteral("C")};
|
||||
QTest::addRow("C-Hans-CN")
|
||||
<< QString("C-Hans-CN") << QLocale::C
|
||||
<< QStringList{QStringLiteral("C-Hans-CN"), QStringLiteral("C")};
|
||||
|
||||
QTest::newRow("und-US")
|
||||
<< QString("und-US") << QLocale::C
|
||||
<< QStringList{QStringLiteral("und-US"), QStringLiteral("C")};
|
||||
|
||||
QTest::newRow("und-Latn")
|
||||
<< QString("und-Latn") << QLocale::C
|
||||
<< QStringList{QStringLiteral("und-Latn"), QStringLiteral("C")};
|
||||
|
||||
// TODO: test actual system backends correctly handle locales with
|
||||
// script-specificity (script listed first is the default, in CLDR v40):
|
||||
// az_{Latn,Cyrl}_AZ, bs_{Latn,Cyrl}_BA, sr_{Cyrl,Latn}_{BA,RS,XK,UZ},
|
||||
// sr_{Latn,Cyrl}_ME, ff_{Latn,Adlm}_{BF,CM,GH,GM,GN,GW,LR,MR,NE,NG,SL,SN},
|
||||
// shi_{Tfng,Latn}_MA, vai_{Vaii,Latn}_LR, zh_{Hant,Hans}_{MO,HK}
|
||||
}
|
||||
|
||||
void tst_QLocale::systemLocale()
|
||||
{
|
||||
// Compare uiLanguages(), which tests this for CLDR-derived locales.
|
||||
QLocale originalLocale;
|
||||
QLocale originalSystemLocale = QLocale::system();
|
||||
|
||||
|
|
@ -3306,9 +3371,11 @@ void tst_QLocale::systemLocale()
|
|||
reporter.dismiss();
|
||||
}
|
||||
|
||||
// Verify MySystemLocale tidy-up restored prior state:
|
||||
QCOMPARE(QLocale(), originalLocale);
|
||||
QCOMPARE(QLocale::system(), originalSystemLocale);
|
||||
}
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
|
||||
#ifndef QT_NO_SYSTEMLOCALE
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue