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>
This commit is contained in:
parent
d7bad11a87
commit
91e70f239e
@ -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)
|
||||
|
@ -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 (;;) {
|
||||
|
@ -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;
|
||||
|
@ -1340,6 +1340,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.
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user