From cbc3c63633ad2c19ca4aa8fad302a0d1c30e7d7e Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 17 Jan 2024 18:45:07 +0100 Subject: [PATCH] Make QLocale self-consistent on Windows The constructor and update() method for the system locale were using GetUserDefaultLCID(), where query() and the fallback locale first checked the LANG environment variable, leading to inconsistent results if the user set the environment variable to something different from the system's configured locale. Break out the logic of parsing %LANG% into a static helper, replace the existing parsing with a call to this and add a helper to get the right ID to use, possibly via it, using GetUserDefaultLCID() as fall-back. Drive-by: initialize substititionType in its declaration. Also look up %LANG% each time we want it; it's not that expensive, given how rarely this code is called, and client code could change its value at runtime. Partially inspired by a patch from Wladimir Leuschner Pick-to: 6.6 6.5 Fixes: QTBUG-120961 Change-Id: Ie706c7089bd2b3757a3eab627723ec34a5e2b07f Reviewed-by: Ivan Solovev Reviewed-by: Thiago Macieira (cherry picked from commit fcd2a219c4c222309152f1ea2a3124ddc17d4ea5) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/text/qlocale_win.cpp | 61 +++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp index 0cd6249b7fa..89779bcd2f4 100644 --- a/src/corelib/text/qlocale_win.cpp +++ b/src/corelib/text/qlocale_win.cpp @@ -31,6 +31,41 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +// Shared interpretation of %LANG% +static auto scanLangEnv() +{ + struct R + { + QByteArray name; // empty means unknown; lookup from id may work + LCID id = 0; // 0 means unknown; lookup from name may work + } result; + const QByteArray lang = qgetenv("LANG"); + if (lang.size() && (lang == "C" || qt_splitLocaleName(QString::fromLocal8Bit(lang)))) { + // See if we have a Windows locale code instead of a locale name: + const auto [id, used] = qstrntoll(lang.data(), lang.size(), 0); + if (used > 0 && id && INT_MIN <= id && id <= INT_MAX) + return R {QByteArray(), static_cast(id)}; + return R {lang, 0}; + } + return R{}; +} + +static auto getDefaultWinId() +{ + const auto [name, id] = scanLangEnv(); + if (id) + return id; + + if (!name.isEmpty()) { + LCID id = LocaleNameToLCID(static_cast( + QString::fromUtf8(name).toStdWString().data()), 0); + if (id) + return id; + } + + return GetUserDefaultLCID(); +} + static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT); static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT); static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT); @@ -111,7 +146,7 @@ private: // cached values: LCID lcid; - SubstitutionType substitutionType; + SubstitutionType substitutionType = SUnknown; QString zero; // cached value for zeroDigit() int getLocaleInfo(LCTYPE type, LPWSTR data, int size); @@ -133,9 +168,8 @@ private: Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate) QSystemLocalePrivate::QSystemLocalePrivate() - : substitutionType(SUnknown) + : lcid(getDefaultWinId()) { - lcid = GetUserDefaultLCID(); } inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size) @@ -720,7 +754,7 @@ QVariant QSystemLocalePrivate::nativeTerritoryName() void QSystemLocalePrivate::update() { - lcid = GetUserDefaultLCID(); + lcid = getDefaultWinId(); substitutionType = SUnknown; zero.resize(0); } @@ -1123,20 +1157,15 @@ static QByteArray getWinLocaleName(LCID id) { QByteArray result; if (id == LOCALE_USER_DEFAULT) { - static const QByteArray langEnvVar = qgetenv("LANG"); - result = langEnvVar; - if (result == "C" - || (!result.isEmpty() && qt_splitLocaleName(QString::fromLocal8Bit(result)))) { - // See if we have a Windows locale code instead of a locale name: - auto [id, used] = qstrntoll(result.data(), result.size(), 0); - if (used <= 0 || id == 0 || id < INT_MIN || id > INT_MAX) // Assume real locale name - return result; - return winLangCodeToIsoName(int(id)); - } + const auto [name, lcid] = scanLangEnv(); + if (!name.isEmpty()) + return name; + if (lcid) + return winLangCodeToIsoName(lcid); + + id = GetUserDefaultLCID(); } - if (id == LOCALE_USER_DEFAULT) - id = GetUserDefaultLCID(); QString resultusage = winIso639LangName(id); QString country = winIso3116CtryName(id); if (!country.isEmpty())