summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2023-07-27 15:02:56 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2023-08-16 16:25:48 +0200
commit91e70f239e166956c0db2d99cfb229c6b7d94598 (patch)
tree3613bca45096f6bd48b1ceaceb6906215c8c4dd4
parentd7bad11a87a5edcc55e6d0a45201b48f52f54e23 (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.cpp7
-rw-r--r--src/corelib/kernel/qtranslator.cpp4
-rw-r--r--src/corelib/mimetypes/qmimetype.cpp14
-rw-r--r--src/corelib/text/qlocale.cpp67
-rw-r--r--src/corelib/text/qlocale.h5
-rw-r--r--tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp2
-rw-r--r--tests/auto/corelib/text/qlocale/tst_qlocale.cpp50
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()