diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2023-07-27 15:02:56 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2023-08-16 16:25:48 +0200 |
commit | 91e70f239e166956c0db2d99cfb229c6b7d94598 (patch) | |
tree | 3613bca45096f6bd48b1ceaceb6906215c8c4dd4 | |
parent | d7bad11a87a5edcc55e6d0a45201b48f52f54e23 (diff) |
Give QLocale::uiLanguages() a separator parameter
It has always returned dash-joined forms of the locale names, and
callers who need an underscore-joined form have been obliged to
replace('-', '_') before using them. Given that everything it adds to
the list comes from QLocaleId methods that accept a separator, it's
trivial to let it offer the same choice to its callers and save them
this hassle.
Amended code in QTranslater and QMimeType to save them that hassle.
[ChangeLog][CoreLib][QLocale] QLocale::uiLanguages() now lets the
caller choose what separator to use between the tags that make up each
locale-identifier in the list returned.
Change-Id: I91fcd0b988d9a64e0e9ad9e851f6cb8c1be8ae50
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/compat/removed_api.cpp | 7 | ||||
-rw-r--r-- | src/corelib/kernel/qtranslator.cpp | 4 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimetype.cpp | 14 | ||||
-rw-r--r-- | src/corelib/text/qlocale.cpp | 67 | ||||
-rw-r--r-- | src/corelib/text/qlocale.h | 5 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp | 2 | ||||
-rw-r--r-- | tests/auto/corelib/text/qlocale/tst_qlocale.cpp | 50 |
7 files changed, 118 insertions, 31 deletions
diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp index fe45127718..28207d26d5 100644 --- a/src/corelib/compat/removed_api.cpp +++ b/src/corelib/compat/removed_api.cpp @@ -618,6 +618,13 @@ QStringView QXmlStreamAttributes::value(QLatin1StringView qualifiedName) const #if QT_CORE_REMOVED_SINCE(6, 7) +#include "qlocale.h" + +QStringList QLocale::uiLanguages() const +{ + return uiLanguages(TagSeparator::Dash); +} + #include "qurl.h" QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode) diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 9f9575f517..b4fe045052 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -627,7 +627,7 @@ static QString find_translation(const QLocale & locale, // "prefix_en_us.qm" won't be found under the "en_US" locale. Note // that the Qt resource system is always case-sensitive, even on // Windows (in other words: this codepath is *not* UNIX-only). - QStringList languages = locale.uiLanguages(); + QStringList languages = locale.uiLanguages(QLocale::TagSeparator::Underscore); for (int i = languages.size()-1; i >= 0; --i) { QString lang = languages.at(i); QString lowerLang = lang.toLower(); @@ -636,8 +636,6 @@ static QString find_translation(const QLocale & locale, } for (QString localeName : std::as_const(languages)) { - localeName.replace(u'-', u'_'); - // try the complete locale name first and progressively truncate from // the end until a matching language tag is found (with or without suffix) for (;;) { diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index b824a42049..39cdfaec5e 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -221,16 +221,16 @@ QString QMimeType::comment() const { QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d)); - QStringList languageList = QLocale().uiLanguages(); - qsizetype defaultIndex = languageList.indexOf(u"en-US"_s); + QStringList languageList = QLocale().uiLanguages(QLocale::TagSeparator::Underscore); + qsizetype defaultIndex = languageList.indexOf(u"en_US"_s); // Include the default locale as fall-back. if (defaultIndex >= 0) { // en_US is generally the default, and may be omitted from the // overtly-named locales in the MIME type's data (QTBUG-105007). - ++defaultIndex; // Skip over en-US. - // That's typically followed by en-Latn-US and en (in that order): - if (defaultIndex < languageList.size() && languageList.at(defaultIndex) == u"en-Latn-US") + ++defaultIndex; // Skip over en_US. + // That's typically followed by en_Latn_US and en (in that order): + if (defaultIndex < languageList.size() && languageList.at(defaultIndex) == u"en_Latn_US") ++defaultIndex; if (defaultIndex < languageList.size() && languageList.at(defaultIndex) == u"en") ++defaultIndex; @@ -241,9 +241,7 @@ QString QMimeType::comment() const languageList.insert(defaultIndex, u"default"_s); for (const QString &language : std::as_const(languageList)) { - // uiLanguages() uses '-' as separator, MIME database uses '_' - const QString lang - = language == "C"_L1 ? u"en_US"_s : QString(language).replace(u'-', u'_'); + const QString lang = language == "C"_L1 ? u"en_US"_s : language; QString comm = d->localeComments.value(lang); if (!comm.isEmpty()) return comm; diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index e0a083e151..bb38d82702 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -1341,6 +1341,33 @@ QLocale::Country QLocale::country() const #endif /*! + \since 6.7 + + \enum QLocale::TagSeparator + + Indicate how to combine the parts that make up a locale identifier. + + A locale identifier may be made up of several tags, indicating language, + script and territory (plus, potentially, other details), joined together to + form the identifier. Various standards and conventional forms use either a + dash (the Unicode HYPHEN-MINUS, U+002D) or an underscore (LOW LINE, U+005F). + Different clients of QLocale may thus need one or the other. + + \value Dash Use \c{'-'}, the dash or hyphen character. + \value Underscore Use \c{'_'}, the underscore character. + + \note Although dash and underscore are the only separators used in public + standards (as at 2023), it is possible to cast any \l + {https://en.cppreference.com/w/cpp/language/ascii} {ASCII} character to this + type if a non-standard ASCII separator is needed. Casting a non-ASCII + character (with decimal value above 127) is not supported: such values are + reserved for future use as enum members if some public standard ever uses a + non-ASCII separator. It is, of course, possible to use QString::replace() to + replace the separator used by a function taking a parameter of this type + with an arbitrary Unicode character or string. +*/ + +/*! \brief The short name of this locale. Returns the language and territory of this locale as a string of the form @@ -4602,21 +4629,33 @@ QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats \since 4.8 \brief List of locale names for use in selecting translations - Each entry in the returned list is the dash-joined name of a locale, - suitable to the user's preferences for what to translate the UI into. For - example, if the user has configured their system to use English as used in - the USA, the list would be "en-Latn-US", "en-US", "en". The order of entries - is the order in which to check for translations; earlier items in the list - are to be preferred over later ones. + Each entry in the returned list is the name of a locale suitable to the + user's preferences for what to translate the UI into. Where a name in the + list is composed of several tags, they are joined as indicated by \a + separator. + + For example, using the default separator QLocale::TagSeparator::Dash, if the + user has configured their system to use English as used in the USA, the list + would be "en-Latn-US", "en-US", "en". The order of entries is the order in + which to check for translations; earlier items in the list are to be + preferred over later ones. If your translation files use underscores, rather + than dashes, to separate locale tags, pass QLocale::TagSeparator::Underscore + as \a separator. Most likely you do not need to use this function directly, but just pass the QLocale object to the QTranslator::load() function. \sa QTranslator, bcp47Name() */ -QStringList QLocale::uiLanguages() const +QStringList QLocale::uiLanguages(TagSeparator separator) const { + const char sep = char(separator); QStringList uiLanguages; + if (uchar(sep) > 0x7f) { + qWarning("QLocale::uiLanguages(): Using non-ASCII separator '%c' (%02x) is unsupported", + sep, uint(uchar(sep))); + return uiLanguages; + } QList<QLocaleId> localeIds; #ifdef QT_NO_SYSTEMLOCALE constexpr bool isSystem = false; @@ -4635,7 +4674,7 @@ QStringList QLocale::uiLanguages() const // 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. - const QString name = bcp47Name(); + const QString name = QString::fromLatin1(d->bcp47Name(sep)); 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. @@ -4665,11 +4704,11 @@ QStringList QLocale::uiLanguages() const j = i + 1; } else if (id.language_id == C) { // Attempt no likely sub-tag amendments to C: - uiLanguages.append(QString::fromLatin1(id.name())); + uiLanguages.append(QString::fromLatin1(id.name(sep))); continue; } else { // Plain locale or empty system uiLanguages; just append. - prior = id.name(); + prior = id.name(sep); uiLanguages.append(QString::fromLatin1(prior)); j = uiLanguages.size(); } @@ -4678,7 +4717,7 @@ QStringList QLocale::uiLanguages() const const QLocaleId min = max.withLikelySubtagsRemoved(); // Include minimal version (last) unless it's what our locale is derived from: - if (auto name = min.name(); name != prior) + if (auto name = min.name(sep); name != prior) uiLanguages.insert(j, QString::fromLatin1(name)); else if (!isSystem) --j; // bcp47Name() matches min(): put more specific forms *before* it. @@ -4687,7 +4726,7 @@ QStringList QLocale::uiLanguages() const // 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) + if (auto name = id.name(sep); name != prior) uiLanguages.insert(j, QString::fromLatin1(name)); } } @@ -4698,14 +4737,14 @@ QStringList QLocale::uiLanguages() const // 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) + if (auto name = id.name(sep); 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) { - if (auto name = max.name(); name != prior) + if (auto name = max.name(sep); name != prior) uiLanguages.insert(j, QString::fromLatin1(name)); } } diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h index 3d39c52ec7..ca437ed92d 100644 --- a/src/corelib/text/qlocale.h +++ b/src/corelib/text/qlocale.h @@ -885,6 +885,8 @@ public: FloatingPointShortest = -128 }; + enum class TagSeparator : char { Dash = '-', Underscore = '_' }; + enum CurrencySymbolFormat { CurrencyIsoCode, CurrencySymbol, @@ -1059,7 +1061,10 @@ public: QString formattedDataSize(qint64 bytes, int precision = 2, DataSizeFormats format = DataSizeIecFormat) const; +#if QT_CORE_REMOVED_SINCE(6, 7) QStringList uiLanguages() const; +#endif + QStringList uiLanguages(TagSeparator separator = TagSeparator::Dash) const; enum LanguageCodeType { ISO639Part1 = 1 << 0, diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp index 372a7a9db1..f534389c2b 100644 --- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp +++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp @@ -119,7 +119,7 @@ void tst_QTranslator::load() void tst_QTranslator::loadLocale() { QLocale locale; - auto localeName = locale.uiLanguages().value(0).replace('-', '_'); + auto localeName = locale.uiLanguages(QLocale::TagSeparator::Underscore).value(0); if (localeName.isEmpty()) QSKIP("This test requires at least one available UI language."); diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index c7017b8511..78f3ce6359 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -3602,11 +3602,51 @@ void tst_QLocale::uiLanguages() // Compare mySystemLocale(), which tests the same for a custom 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(); + const auto expected = [all](QChar sep) { + QStringList adjusted; + for (QString name : all) + adjusted << name.replace(u'-', sep); + return adjusted; + }; + + { + // By default tags are joined with a dash: + const QStringList actual = locale.uiLanguages(); + auto reporter = qScopeGuard([&actual]() { + qDebug("\n\t%ls", qUtf16Printable(actual.join("\n\t"_L1))); + }); + QCOMPARE(actual, all); + reporter.dismiss(); + } + { + // We also support joining with an underscore: + const QStringList actual = locale.uiLanguages(QLocale::TagSeparator::Underscore); + auto reporter = qScopeGuard([&actual]() { + qDebug("\n\t%ls", qUtf16Printable(actual.join("\n\t"_L1))); + }); + QCOMPARE(actual, expected(u'_')); + reporter.dismiss(); + } + { + // Or, in fact, any ASCII character: + const QStringList actual = locale.uiLanguages(QLocale::TagSeparator{'|'}); + auto reporter = qScopeGuard([&actual]() { + qDebug("\n\t%ls", qUtf16Printable(actual.join("\n\t"_L1))); + }); + QCOMPARE(actual, expected(u'|')); + reporter.dismiss(); + } + { + // Non-ASCII separator (here, y-umlaut) is unsupported. + QTest::ignoreMessage(QtWarningMsg, "QLocale::uiLanguages(): " + "Using non-ASCII separator '\u00ff' (ff) is unsupported"); + const QStringList actual = locale.uiLanguages(QLocale::TagSeparator{'\xff'}); + auto reporter = qScopeGuard([&actual]() { + qDebug("\n\t%ls", qUtf16Printable(actual.join("\n\t"_L1))); + }); + QCOMPARE(actual, QStringList{}); + reporter.dismiss(); + } } void tst_QLocale::weekendDays() |