diff options
Diffstat (limited to 'src/corelib/text/qlocale_win.cpp')
-rw-r--r-- | src/corelib/text/qlocale_win.cpp | 382 |
1 files changed, 193 insertions, 189 deletions
diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp index dafe441ee0..9fdb46a4c9 100644 --- a/src/corelib/text/qlocale_win.cpp +++ b/src/corelib/text/qlocale_win.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qlocale_p.h" #include "qlocale_tools_p.h" @@ -48,30 +12,60 @@ #include "QtCore/private/qgregoriancalendar_p.h" // for yearSharingWeekDays() -#ifdef Q_OS_WIN -# include <qt_windows.h> -# include <time.h> -#endif +#include <q20algorithm.h> + +// TODO QTBUG-121193: port away from the use of LCID to always use names. +#include <qt_windows.h> +#include <time.h> + +#if QT_CONFIG(cpp_winrt) +# include <QtCore/private/qt_winrtbase_p.h> -#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) -# include <winrt/base.h> -// Workaround for Windows SDK bug. -// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47 -namespace winrt::impl -{ - template <typename Async> - auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout); -} # include <winrt/Windows.Foundation.h> # include <winrt/Windows.Foundation.Collections.h> # include <winrt/Windows.System.UserProfile.h> -#endif // defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) +#endif // QT_CONFIG(cpp_winrt) 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<LCID>(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<LPCWSTR>( + 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); #ifndef QT_NO_SYSTEMLOCALE @@ -97,6 +91,17 @@ static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT); # define LOCALE_SSHORTTIME 0x00000079 #endif +namespace { +template <typename T> +static QVariant nullIfEmpty(T &&value) +{ + // For use where we should fall back to CLDR if we got an empty value. + if (value.isEmpty()) + return {}; + return std::move(value); +} +} + struct QSystemLocalePrivate { QSystemLocalePrivate(); @@ -138,7 +143,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); @@ -151,6 +156,7 @@ private: SubstitutionType substitution(); QString substituteDigits(QString &&string); + QString correctDigits(QString &&string); QString yearFix(int year, int fakeYear, QString &&formatted); static QString winToQtFormat(QStringView sys_fmt); @@ -159,9 +165,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) @@ -213,10 +218,11 @@ QVariant QSystemLocalePrivate::getLocaleInfo(LCTYPE type) int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type) { - const QString str = getLocaleInfo(type).toString(); - bool ok = false; - const int v = str.toInt(&ok); - return ok ? v : 0; + DWORD value; + int r = GetLocaleInfo(lcid, type | LOCALE_RETURN_NUMBER, + reinterpret_cast<wchar_t *>(&value), + sizeof(value) / sizeof(wchar_t)); + return r == sizeof(value) / sizeof(wchar_t) ? value : 0; } QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution() @@ -224,25 +230,25 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution() if (substitutionType == SUnknown) { wchar_t buf[8]; if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) { - substitutionType = QSystemLocalePrivate::SNever; + substitutionType = SNever; return substitutionType; } if (buf[0] == '1') - substitutionType = QSystemLocalePrivate::SNever; + substitutionType = SNever; else if (buf[0] == '0') - substitutionType = QSystemLocalePrivate::SContext; + substitutionType = SContext; else if (buf[0] == '2') - substitutionType = QSystemLocalePrivate::SAlways; + substitutionType = SAlways; else { wchar_t digits[11]; // See zeroDigit() for why 11. if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) { - substitutionType = QSystemLocalePrivate::SNever; + substitutionType = SNever; return substitutionType; } if (buf[0] == digits[0] + 2) - substitutionType = QSystemLocalePrivate::SAlways; + substitutionType = SAlways; else - substitutionType = QSystemLocalePrivate::SNever; + substitutionType = SNever; } } return substitutionType; @@ -258,7 +264,7 @@ QString QSystemLocalePrivate::substituteDigits(QString &&string) break; Q_ASSERT(z > '9'); ushort *const qch = reinterpret_cast<ushort *>(string.data()); - for (int i = 0, stop = string.size(); i < stop; ++i) { + for (qsizetype i = 0, stop = string.size(); i < stop; ++i) { ushort &ch = qch[i]; if (ch >= '0' && ch <= '9') ch = unicodeForDigit(ch - '0', z); @@ -283,6 +289,11 @@ QString QSystemLocalePrivate::substituteDigits(QString &&string) return std::move(string); } +QString QSystemLocalePrivate::correctDigits(QString &&string) +{ + return substitution() == SAlways ? substituteDigits(std::move(string)) : std::move(string); +} + QVariant QSystemLocalePrivate::zeroDigit() { if (zero.isEmpty()) { @@ -300,36 +311,36 @@ QVariant QSystemLocalePrivate::zeroDigit() zero = QString::fromWCharArray(digits, 1); } } - return zero; + return nullIfEmpty(zero); // Do not std::move(). } QVariant QSystemLocalePrivate::decimalPoint() { - return getLocaleInfo(LOCALE_SDECIMAL); + return nullIfEmpty(getLocaleInfo(LOCALE_SDECIMAL).toString()); } QVariant QSystemLocalePrivate::groupSeparator() { - return getLocaleInfo(LOCALE_STHOUSAND); + return getLocaleInfo(LOCALE_STHOUSAND); // Empty means don't group digits. } QVariant QSystemLocalePrivate::negativeSign() { - return getLocaleInfo(LOCALE_SNEGATIVESIGN); + return nullIfEmpty(getLocaleInfo(LOCALE_SNEGATIVESIGN).toString()); } QVariant QSystemLocalePrivate::positiveSign() { - return getLocaleInfo(LOCALE_SPOSITIVESIGN); + return nullIfEmpty(getLocaleInfo(LOCALE_SPOSITIVESIGN).toString()); } QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type) { switch (type) { case QLocale::ShortFormat: - return winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE).toString()); + return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE).toString())); case QLocale::LongFormat: - return winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE).toString()); + return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE).toString())); case QLocale::NarrowFormat: break; } @@ -340,9 +351,9 @@ QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type) { switch (type) { case QLocale::ShortFormat: - return winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME).toString()); + return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME).toString())); case QLocale::LongFormat: - return winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT).toString()); + return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT).toString())); case QLocale::NarrowFormat: break; } @@ -351,48 +362,48 @@ QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type) QVariant QSystemLocalePrivate::dateTimeFormat(QLocale::FormatType type) { - return QString(dateFormat(type).toString() + QLatin1Char(' ') + timeFormat(type).toString()); + QVariant d = dateFormat(type), t = timeFormat(type); + if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString) + return QString(d.toString() + u' ' + t.toString()); + return {}; } QVariant QSystemLocalePrivate::dayName(int day, QLocale::FormatType type) { if (day < 1 || day > 7) - return QString(); + return {}; - static const LCTYPE short_day_map[] + static constexpr LCTYPE short_day_map[] = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 }; - static const LCTYPE long_day_map[] + static constexpr LCTYPE long_day_map[] = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 }; - static const LCTYPE narrow_day_map[] + static constexpr LCTYPE narrow_day_map[] = { LOCALE_SSHORTESTDAYNAME1, LOCALE_SSHORTESTDAYNAME2, LOCALE_SSHORTESTDAYNAME3, LOCALE_SSHORTESTDAYNAME4, LOCALE_SSHORTESTDAYNAME5, LOCALE_SSHORTESTDAYNAME6, LOCALE_SSHORTESTDAYNAME7 }; - day -= 1; - - if (type == QLocale::LongFormat) - return getLocaleInfo(long_day_map[day]); - if (type == QLocale::NarrowFormat) - return getLocaleInfo(narrow_day_map[day]); - return getLocaleInfo(short_day_map[day]); + return nullIfEmpty(getLocaleInfo( + (type == QLocale::LongFormat ? long_day_map + : type == QLocale::NarrowFormat ? narrow_day_map + : short_day_map)[day - 1]).toString()); } QVariant QSystemLocalePrivate::standaloneMonthName(int month, QLocale::FormatType type) { - static const LCTYPE short_month_map[] + static constexpr LCTYPE short_month_map[] = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3, LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6, LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9, LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 }; - static const LCTYPE long_month_map[] + static constexpr LCTYPE long_month_map[] = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9, @@ -402,8 +413,8 @@ QVariant QSystemLocalePrivate::standaloneMonthName(int month, QLocale::FormatTyp return {}; // Month is Jan = 1, ... Dec = 12; adjust by 1 to match array indexing from 0: - return getLocaleInfo( - (type == QLocale::LongFormat ? long_month_map : short_month_map)[month - 1]); + return nullIfEmpty(getLocaleInfo( + (type == QLocale::LongFormat ? long_month_map : short_month_map)[month - 1]).toString()); } QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type) @@ -422,10 +433,7 @@ QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type) wchar_t buf[255]; if (getDateFormat(flags, &st, format, buf, 255) > 2) { // Elide the two digits of day number - QString text = QString::fromWCharArray(buf + 2); - if (substitution() == SAlways) - text = substituteDigits(std::move(text)); - return text; + return nullIfEmpty(correctDigits(QString::fromWCharArray(buf + 2))); } return {}; } @@ -444,7 +452,7 @@ QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatte Q_ASSERT(fakeYear >= 1970 && fakeYear <= 2400); const bool matchTwo = year >= 0 && year % 100 == fakeYear % 100; auto yearUsed = fourDigitYear(fakeYear); - QString sign(year < 0 ? 1 : 0, QLatin1Char('-')); + QString sign(year < 0 ? 1 : 0, u'-'); auto trueYear = fourDigitYear(year < 0 ? -year : year); if (formatted.contains(yearUsed)) return std::move(formatted).replace(yearUsed, sign + trueYear); @@ -457,7 +465,7 @@ QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatte return std::move(formatted).replace(tail.toString(), sign + trueYear.last(2)); } - // Localized digits, perhaps ? + // Localized digits (regardless of SAlways), perhaps ? // First call to substituteDigits() ensures zero is initialized: trueYear = substituteDigits(std::move(trueYear)); if (zero != u'0') { @@ -468,7 +476,7 @@ QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatte if (formatted.contains(yearUsed)) return std::move(formatted).replace(yearUsed, sign + trueYear); - const int twoDigits = 2 * zero.size(); + const qsizetype twoDigits = 2 * zero.size(); tail = QStringView{yearUsed}.last(twoDigits); if (formatted.contains(tail)) { if (matchTwo) @@ -502,9 +510,7 @@ QVariant QSystemLocalePrivate::toString(QDate date, QLocale::FormatType type) QString text = QString::fromWCharArray(buf); if (fixup) text = yearFix(year, st.wYear, std::move(text)); - if (substitution() == SAlways) - text = substituteDigits(std::move(text)); - return text; + return nullIfEmpty(correctDigits(std::move(text))); } return {}; } @@ -519,22 +525,23 @@ QVariant QSystemLocalePrivate::toString(QTime time, QLocale::FormatType type) DWORD flags = 0; // keep the same conditional as timeFormat() above - if (type == QLocale::ShortFormat) - flags = TIME_NOSECONDS; + const QString format = type == QLocale::ShortFormat + ? getLocaleInfo(LOCALE_SSHORTTIME).toString() + : QString(); + auto formatStr = reinterpret_cast<const wchar_t *>(format.isEmpty() ? nullptr : format.utf16()); wchar_t buf[255]; - if (getTimeFormat(flags, &st, NULL, buf, 255)) { - QString text = QString::fromWCharArray(buf); - if (substitution() == SAlways) - text = substituteDigits(std::move(text)); - return text; - } + if (getTimeFormat(flags, &st, formatStr, buf, int(std::size(buf)))) + return nullIfEmpty(correctDigits(QString::fromWCharArray(buf))); return {}; } QVariant QSystemLocalePrivate::toString(const QDateTime &dt, QLocale::FormatType type) { - return QString(toString(dt.date(), type).toString() + QLatin1Char(' ') + toString(dt.time(), type).toString()); + QVariant d = toString(dt.date(), type), t = toString(dt.time(), type); + if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString) + return QString(d.toString() + u' ' + t.toString()); + return {}; } QVariant QSystemLocalePrivate::measurementSystem() @@ -542,10 +549,8 @@ QVariant QSystemLocalePrivate::measurementSystem() wchar_t output[2]; if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) { - QString iMeasure = QString::fromWCharArray(output); - if (iMeasure == QLatin1String("1")) { + if (output[0] == L'1' && !output[1]) return QLocale::ImperialSystem; - } } return QLocale::MetricSystem; @@ -561,7 +566,7 @@ QVariant QSystemLocalePrivate::amText() wchar_t output[15]; // maximum length including terminating zero character for Win2003+ if (getLocaleInfo(LOCALE_S1159, output, 15)) - return QString::fromWCharArray(output); + return nullIfEmpty(QString::fromWCharArray(output)); return QVariant(); } @@ -571,7 +576,7 @@ QVariant QSystemLocalePrivate::pmText() wchar_t output[15]; // maximum length including terminating zero character for Win2003+ if (getLocaleInfo(LOCALE_S2359, output, 15)) - return QString::fromWCharArray(output); + return nullIfEmpty(QString::fromWCharArray(output)); return QVariant(); } @@ -591,12 +596,14 @@ QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat form wchar_t buf[13]; switch (format) { case QLocale::CurrencySymbol: + // Some locales do have empty currency symbol. All the same, fall back + // to CLDR for confirmation if MS claims that applies. if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13)) - return QString::fromWCharArray(buf); + return nullIfEmpty(QString::fromWCharArray(buf)); break; case QLocale::CurrencyIsoCode: if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9)) - return QString::fromWCharArray(buf); + return nullIfEmpty(QString::fromWCharArray(buf)); break; case QLocale::CurrencyDisplayName: { QVarLengthArray<wchar_t, 64> buf(64); @@ -607,7 +614,7 @@ QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat form if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) break; } - return QString::fromWCharArray(buf.data()); + return nullIfEmpty(QString::fromWCharArray(buf.data())); } default: break; @@ -667,7 +674,7 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS // int(32) == "12,34,56,789.00" == string("3;2;0") // int(320)== "1234,56,789.00" == string("3;2") QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING).toString(); - format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt(); + format.Grouping = groupingStr.remove(u';').toInt(); if (format.Grouping % 10 == 0) // magic format.Grouping /= 10; else @@ -685,25 +692,25 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS pformat, out.data(), out.size()); } - value = QString::fromWCharArray(out.data()); - if (substitution() == SAlways) - value = substituteDigits(std::move(value)); - return value; + return nullIfEmpty(correctDigits(QString::fromWCharArray(out.data()))); } QVariant QSystemLocalePrivate::uiLanguages() { QStringList result; -#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) // msvc supports WinRT calls +#if QT_CONFIG(cpp_winrt) using namespace winrt; - using namespace Windows::Foundation; using namespace Windows::System::UserProfile; - auto languages = GlobalizationPreferences::Languages(); - for (const auto &lang : languages) - result << QString::fromStdString(winrt::to_string(lang)); + QT_TRY { + auto languages = GlobalizationPreferences::Languages(); + for (const auto &lang : languages) + result << QString::fromStdString(winrt::to_string(lang)); + } QT_CATCH(...) { + // pass, just fall back to WIN32 API implementation + } if (!result.isEmpty()) return result; // else just fall back to WIN32 API implementation -#endif // defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) +#endif // QT_CONFIG(cpp_winrt) // mingw and clang still have to use Win32 API unsigned long cnt = 0; QVarLengthArray<wchar_t, 64> buf(64); @@ -715,7 +722,7 @@ QVariant QSystemLocalePrivate::uiLanguages() GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) { buf.resize(size); if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) - return QStringList(); + return {}; } } # endif // !QT_BOOTSTRAPPED @@ -728,7 +735,7 @@ QVariant QSystemLocalePrivate::uiLanguages() result.append(s); str += s.size() + 1; } - return result; + return nullIfEmpty(std::move(result)); } QVariant QSystemLocalePrivate::nativeLanguageName() @@ -744,7 +751,7 @@ QVariant QSystemLocalePrivate::nativeTerritoryName() void QSystemLocalePrivate::update() { - lcid = GetUserDefaultLCID(); + lcid = getDefaultWinId(); substitutionType = SUnknown; zero.resize(0); } @@ -752,20 +759,20 @@ void QSystemLocalePrivate::update() QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt) { QString result; - int i = 0; + qsizetype i = 0; while (i < sys_fmt.size()) { - if (sys_fmt.at(i).unicode() == QLatin1Char('\'')) { + if (sys_fmt.at(i).unicode() == u'\'') { QString text = qt_readEscapedFormatString(sys_fmt, &i); - if (text == QLatin1String("'")) - result += QLatin1String("''"); + if (text == "'"_L1) + result += "''"_L1; else - result += QLatin1Char('\'') + text + QLatin1Char('\''); + result += u'\'' + text + u'\''; continue; } QChar c = sys_fmt.at(i); - int repeat = qt_repeatCount(sys_fmt.mid(i)); + qsizetype repeat = qt_repeatCount(sys_fmt.mid(i)); switch (c.unicode()) { // Date @@ -776,13 +783,13 @@ QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt) repeat = 2; switch (repeat) { case 1: - result += QLatin1String("yy"); // "y" unsupported by Qt, use "yy" + result += "yy"_L1; // "y" unsupported by Qt, use "yy" break; case 5: - result += QLatin1String("yyyy"); // "yyyyy" same as "yyyy" on Windows + result += "yyyy"_L1; // "yyyyy" same as "yyyy" on Windows break; default: - result += QString(repeat, QLatin1Char('y')); + result += QString(repeat, u'y'); break; } break; @@ -793,14 +800,14 @@ QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt) case 2: break; // no equivalent of "gg" in Qt default: - result += QLatin1Char('g'); + result += u'g'; break; } break; case 't': if (repeat > 2) repeat = 2; - result += QLatin1String("AP"); // "t" unsupported, use "AP" + result += "AP"_L1; // "t" unsupported, use "AP" break; default: result += QString(repeat, c); @@ -818,7 +825,7 @@ QLocale QSystemLocale::fallbackLocale() const return QLocale(QString::fromLatin1(getWinLocaleName())); } -QVariant QSystemLocale::query(QueryType type, QVariant in) const +QVariant QSystemLocale::query(QueryType type, QVariant &&in) const { QSystemLocalePrivate *d = systemLocalePrivate(); switch(type) { @@ -902,7 +909,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in) const case CurrencySymbol: return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt())); case CurrencyToString: - return d->toCurrencyString(in.value<QSystemLocale::CurrencyToStringArgument>()); + return d->toCurrencyString(in.value<CurrencyToStringArgument>()); case UILanguages: return d->uiLanguages(); case LocaleChanged: @@ -924,8 +931,18 @@ struct WindowsToISOListElt { char iso_name[6]; }; -/* NOTE: This array should be sorted by the first column! */ -static const WindowsToISOListElt windows_to_iso_list[] = { +namespace { +struct ByWindowsCode { + constexpr bool operator()(int lhs, WindowsToISOListElt rhs) const noexcept + { return lhs < int(rhs.windows_code); } + constexpr bool operator()(WindowsToISOListElt lhs, int rhs) const noexcept + { return int(lhs.windows_code) < rhs; } + constexpr bool operator()(WindowsToISOListElt lhs, WindowsToISOListElt rhs) const noexcept + { return lhs.windows_code < rhs.windows_code; } +}; +} // unnamed namespace + +static constexpr WindowsToISOListElt windows_to_iso_list[] = { { 0x0401, "ar_SA" }, { 0x0402, "bg\0 " }, { 0x0403, "ca\0 " }, @@ -1036,40 +1053,33 @@ static const WindowsToISOListElt windows_to_iso_list[] = { { 0x500a, "es_PR" } }; -static const int windows_to_iso_count - = sizeof(windows_to_iso_list)/sizeof(WindowsToISOListElt); +static_assert(q20::is_sorted(std::begin(windows_to_iso_list), std::end(windows_to_iso_list), + ByWindowsCode{})); static const char *winLangCodeToIsoName(int code) { int cmp = code - windows_to_iso_list[0].windows_code; if (cmp < 0) - return 0; + return nullptr; if (cmp == 0) return windows_to_iso_list[0].iso_name; - int begin = 0; - int end = windows_to_iso_count; - - while (end - begin > 1) { - uint mid = (begin + end)/2; - - const WindowsToISOListElt *elt = windows_to_iso_list + mid; - int cmp = code - elt->windows_code; - if (cmp < 0) - end = mid; - else if (cmp > 0) - begin = mid; - else - return elt->iso_name; - } + const auto it = std::lower_bound(std::begin(windows_to_iso_list), + std::end(windows_to_iso_list), + code, + ByWindowsCode{}); + if (it != std::end(windows_to_iso_list) && !ByWindowsCode{}(code, *it)) + return it->iso_name; - return 0; + return nullptr; } LCID qt_inIsoNametoLCID(const char *name) { + if (!name) + return LOCALE_USER_DEFAULT; // handle norwegian manually, the list above will fail if (!strncmp(name, "nb", 2)) return 0x0414; @@ -1106,14 +1116,12 @@ static QString winIso639LangName(LCID id) lang_code = QString::fromWCharArray(out); if (!lang_code.isEmpty()) { - const char *endptr; - bool ok; - QByteArray latin1_lang_code = std::move(lang_code).toLatin1(); - int i = qstrntoull(latin1_lang_code.data(), latin1_lang_code.size(), &endptr, 16, &ok); - if (ok && *endptr == '\0') { + const QByteArray latin1 = std::move(lang_code).toLatin1(); + const auto [i, used] = qstrntoull(latin1.data(), latin1.size(), 16); + if (used >= latin1.size() || (used > 0 && latin1[used] == '\0')) { switch (i) { case 0x814: - result = QLatin1String("nn"); // Nynorsk + result = u"nn"_s; // Nynorsk break; default: break; @@ -1146,28 +1154,24 @@ 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)))) { - bool ok = false; // See if we have a Windows locale code instead of a locale name: - long id = qstrntoll(result.data(), result.size(), 0, 0, &ok); - if (!ok || 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); - if (id == LOCALE_USER_DEFAULT) id = GetUserDefaultLCID(); + } + QString resultusage = winIso639LangName(id); QString country = winIso3116CtryName(id); if (!country.isEmpty()) - resultusage += QLatin1Char('_') + country; + resultusage += u'_' + country; return std::move(resultusage).toLatin1(); } +// Helper for plugins/platforms/windows/ Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id) { return QLocale(QString::fromLatin1(getWinLocaleName(id))); |