QLocale: only use LANGUAGE if it contradicts LC_ALL/LC_MESSAGES/LANG

If LANGUAGE specified only the language, without any script or
country, and matched the value we'd got from other environment
variables, we were throwing away their knowledge of script and
country, leading to falling back on the default script and country for
that language, which might be at odds with what other environment
variables had told us.

Changed to only use LANGUAGE if it contradicts (or extends) what we
would otherwise have used.  Clarified some comments in the process.

[ChangeLog][QLocale][Unix] When using LANGUAGE would lose information
about script or country, without changing language, use the locale
implied by LC_ALL, LC_MESSAGES or LANG.

Prompted-by: Safa AlFulaij <safa1996alfulaij@gmail.com>
Change-Id: Ie433e57ae6b995abafd05c931136cc9796494895
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2018-01-12 11:36:06 +01:00
parent 7aaa7debc4
commit c416a7f257

View File

@ -107,8 +107,36 @@ Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData)
#ifndef QT_NO_SYSTEMLOCALE #ifndef QT_NO_SYSTEMLOCALE
static bool contradicts(const QByteArray &maybe, const QByteArray &known)
{
if (maybe.isEmpty())
return false;
/*
If \a known (our current best shot at deciding which language to use)
provides more information (e.g. script, country) than \a maybe (a
candidate to replace \a known) and \a maybe agrees with \a known in what
it does provide, we keep \a known; this happens when \a maybe comes from
LANGUAGE (usually a simple language code) and LANG includes script and/or
country. A textual comparison won't do because, for example, bn (Bengali)
isn't a prefix of ben_IN, but the latter is a refinement of the former.
(Meanwhile, bn is a prefix of bnt, Bantu; and a prefix of ben is be,
Belarusian. There are many more such prefixings between two- and
three-letter codes.)
*/
QLocale::Language langm, langk;
QLocale::Script scriptm, scriptk;
QLocale::Country landm, landk;
QLocalePrivate::getLangAndCountry(maybe, langm, scriptm, landm);
QLocalePrivate::getLangAndCountry(known, langk, scriptk, landk);
return (langm != QLocale::AnyLanguage && langm != langk)
|| (scriptm != QLocale::AnyScript && scriptm != scriptk)
|| (landm != QLocale::AnyCountry && landm != landk);
}
QLocale QSystemLocale::fallbackUiLocale() const QLocale QSystemLocale::fallbackUiLocale() const
{ {
// See man 7 locale for precedence - LC_ALL beats LC_MESSAGES beats LANG:
QByteArray lang = qgetenv("LC_ALL"); QByteArray lang = qgetenv("LC_ALL");
if (lang.isEmpty()) if (lang.isEmpty())
lang = qgetenv("LC_MESSAGES"); lang = qgetenv("LC_MESSAGES");
@ -118,12 +146,12 @@ QLocale QSystemLocale::fallbackUiLocale() const
if (lang.isEmpty() || lang == QByteArray("C") || lang == QByteArray("POSIX")) if (lang.isEmpty() || lang == QByteArray("C") || lang == QByteArray("POSIX"))
return QLocale(QString::fromLatin1(lang)); return QLocale(QString::fromLatin1(lang));
// if the locale is not the "C" locale and LANGUAGE is not empty, return // ... otherwise, if the first part of LANGUAGE says more than or
// the first part of LANGUAGE if LANGUAGE is set and has a first part: // contradicts what we have, use that:
QByteArray language = qgetenv("LANGUAGE"); QByteArray language = qgetenv("LANGUAGE");
if (!language.isEmpty()) { if (!language.isEmpty()) {
language = language.split(':').constFirst(); language = language.split(':').constFirst();
if (!language.isEmpty()) if (contradicts(language, lang))
return QLocale(QString::fromLatin1(language)); return QLocale(QString::fromLatin1(language));
} }