Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlocale_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include "qlocale_p.h"
8
9#include "qstringlist.h"
10#include "qvariant.h"
11#include "qdatetime.h"
12#include "qdebug.h"
13
14#include "QtCore/private/qgregoriancalendar_p.h" // for yearSharingWeekDays()
15
16#include <q20algorithm.h>
17
18// TODO QTBUG-121193: port away from the use of LCID to always use names.
19#include <qt_windows.h>
20#include <time.h>
21
22#if QT_CONFIG(cpp_winrt)
23# include <QtCore/private/qt_winrtbase_p.h>
24
25# include <winrt/Windows.Foundation.h>
26# include <winrt/Windows.Foundation.Collections.h>
27# include <winrt/Windows.System.UserProfile.h>
28#endif // QT_CONFIG(cpp_winrt)
29
31
32using namespace Qt::StringLiterals;
33
34// Shared interpretation of %LANG%
35static auto scanLangEnv()
36{
37 struct R
38 {
39 QByteArray name; // empty means unknown; lookup from id may work
40 LCID id = 0; // 0 means unknown; lookup from name may work
41 };
42 const QByteArray lang = qgetenv("LANG");
43 if (lang.size() && (lang == "C" || qt_splitLocaleName(QString::fromLocal8Bit(lang)))) {
44 // See if we have a Windows locale code instead of a locale name:
45 const auto [id, used] = qstrntoll(lang.data(), lang.size(), 0);
46 if (used > 0 && id && INT_MIN <= id && id <= INT_MAX)
47 return R {QByteArray(), static_cast<LCID>(id)};
48 return R {lang, 0};
49 }
50 return R{};
51}
52
53static auto getDefaultWinId()
54{
55 const auto [name, id] = scanLangEnv();
56 if (id)
57 return id;
58
59 if (!name.isEmpty()) {
60 LCID id = LocaleNameToLCID(static_cast<LPCWSTR>(
61 QString::fromUtf8(name).toStdWString().data()), 0);
62 if (id)
63 return id;
64 }
65
66 return GetUserDefaultLCID();
67}
68
70
71#ifndef QT_NO_SYSTEMLOCALE
72
73#ifndef MUI_LANGUAGE_NAME
74#define MUI_LANGUAGE_NAME 0x8
75#endif
76#ifndef LOCALE_SSHORTESTDAYNAME1
77# define LOCALE_SSHORTESTDAYNAME1 0x0060
78# define LOCALE_SSHORTESTDAYNAME2 0x0061
79# define LOCALE_SSHORTESTDAYNAME3 0x0062
80# define LOCALE_SSHORTESTDAYNAME4 0x0063
81# define LOCALE_SSHORTESTDAYNAME5 0x0064
82# define LOCALE_SSHORTESTDAYNAME6 0x0065
83# define LOCALE_SSHORTESTDAYNAME7 0x0066
84#endif
85#ifndef LOCALE_SNATIVELANGUAGENAME
86# define LOCALE_SNATIVELANGUAGENAME 0x00000004
87#endif
88#ifndef LOCALE_SNATIVECOUNTRYNAME
89# define LOCALE_SNATIVECOUNTRYNAME 0x00000008
90#endif
91#ifndef LOCALE_SSHORTTIME
92# define LOCALE_SSHORTTIME 0x00000079
93#endif
94
95namespace {
96template <typename T>
97static QVariant nullIfEmpty(T &&value)
98{
99 // For use where we should fall back to CLDR if we got an empty value.
100 if (value.isEmpty())
101 return {};
102 return std::move(value);
103}
104}
105
107{
109
110 QVariant zeroDigit();
111 QVariant decimalPoint();
112 QVariant groupingSizes();
113 QVariant groupSeparator();
114 QVariant negativeSign();
115 QVariant positiveSign();
119 QVariant dayName(int, QLocale::FormatType);
121 QVariant monthName(int, QLocale::FormatType);
122 QVariant toString(QDate, QLocale::FormatType);
123 QVariant toString(QTime, QLocale::FormatType);
124 QVariant toString(const QDateTime &, QLocale::FormatType);
126 QVariant collation();
127 QVariant amText();
128 QVariant pmText();
129 QVariant firstDayOfWeek();
131 QVariant toCurrencyString(const QSystemLocale::CurrencyToStringArgument &);
132 QVariant uiLanguages();
135
136 void update();
137
138private:
139 enum SubstitutionType {
140 SUnknown,
141 SContext,
142 SAlways,
143 SNever
144 };
145
146 // cached values:
147 LCID lcid;
148 SubstitutionType substitutionType = SUnknown;
149 QString zero; // cached value for zeroDigit()
150 QLocaleData::GroupSizes sizes; // cached value for groupingSizes()
151
152 int getLocaleInfo(LCTYPE type, LPWSTR data, int size);
153 QVariant getLocaleInfo(LCTYPE type);
154 int getLocaleInfo_int(LCTYPE type);
155
156 int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size);
157 int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size);
158 int getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size);
159
160 SubstitutionType substitution();
161 QString substituteDigits(QString &&string);
162 QString correctDigits(QString &&string);
163 QString yearFix(int year, int fakeYear, QString &&formatted);
164
165 static QString winToQtFormat(QStringView sys_fmt);
166
167};
168Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
169
170QSystemLocalePrivate::QSystemLocalePrivate()
171 : lcid(getDefaultWinId())
172{
173}
174
175inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size)
176{
177 return GetCurrencyFormat(lcid, flags, value, format, data, size);
178}
179
180inline int QSystemLocalePrivate::getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size)
181{
182 return GetDateFormat(lcid, flags, date, format, data, size);
183}
184
185inline int QSystemLocalePrivate::getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size)
186{
187 return GetTimeFormat(lcid, flags, date, format, data, size);
188}
189
190inline int QSystemLocalePrivate::getLocaleInfo(LCTYPE type, LPWSTR data, int size)
191{
192 return GetLocaleInfo(lcid, type, data, size);
193}
194
195QVariant QSystemLocalePrivate::getLocaleInfo(LCTYPE type)
196{
197 // https://docs.microsoft.com/en-us/windows/win32/intl/locale-spositivesign
198 // says empty for LOCALE_SPOSITIVESIGN means "+", although GetLocaleInfo()
199 // is documented to return 0 only on failure, so it's not clear how it
200 // returns empty to mean this; hence the two checks for it below.
201 const QString plus = QStringLiteral("+");
202 QVarLengthArray<wchar_t, 64> buf(64);
203 // Need to distinguish empty QString packaged as (non-null) QVariant from null QVariant:
204 if (!getLocaleInfo(type, buf.data(), buf.size())) {
205 const auto lastError = GetLastError();
206 if (type == LOCALE_SPOSITIVESIGN && lastError == ERROR_SUCCESS)
207 return plus;
208 if (lastError != ERROR_INSUFFICIENT_BUFFER)
209 return {};
210 int cnt = getLocaleInfo(type, 0, 0);
211 if (cnt == 0)
212 return {};
213 buf.resize(cnt);
214 if (!getLocaleInfo(type, buf.data(), buf.size()))
215 return {};
216 }
217 if (type == LOCALE_SPOSITIVESIGN && !buf[0])
218 return plus;
219 return QString::fromWCharArray(buf.data());
220}
221
222int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type)
223{
224 DWORD value;
225 int r = GetLocaleInfo(lcid, type | LOCALE_RETURN_NUMBER,
226 reinterpret_cast<wchar_t *>(&value),
227 sizeof(value) / sizeof(wchar_t));
228 return r == sizeof(value) / sizeof(wchar_t) ? value : 0;
229}
230
231QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
232{
233 if (substitutionType == SUnknown) {
234 wchar_t buf[8];
235 if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
236 substitutionType = SNever;
237 return substitutionType;
238 }
239 if (buf[0] == '1')
240 substitutionType = SNever;
241 else if (buf[0] == '0')
242 substitutionType = SContext;
243 else if (buf[0] == '2')
244 substitutionType = SAlways;
245 else {
246 wchar_t digits[11]; // See zeroDigit() for why 11.
247 if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
248 substitutionType = SNever;
249 return substitutionType;
250 }
251 if (buf[0] == digits[0] + 2)
252 substitutionType = SAlways;
253 else
254 substitutionType = SNever;
255 }
256 }
257 return substitutionType;
258}
259
260QString QSystemLocalePrivate::substituteDigits(QString &&string)
261{
262 zeroDigit(); // Ensure zero is set.
263 switch (zero.size()) {
264 case 1: {
265 ushort z = zero.at(0).unicode();
266 if (z == '0') // Nothing to do
267 break;
268 Q_ASSERT(z > '9');
269 ushort *const qch = reinterpret_cast<ushort *>(string.data());
270 for (qsizetype i = 0, stop = string.size(); i < stop; ++i) {
271 ushort &ch = qch[i];
272 if (ch >= '0' && ch <= '9')
273 ch = unicodeForDigit(ch - '0', z);
274 }
275 break;
276 }
277 case 2: {
278 // Surrogate pair (high, low):
279 char32_t z = QChar::surrogateToUcs4(zero.at(0), zero.at(1));
280 for (int i = 0; i < 10; i++) {
281 char32_t digit = unicodeForDigit(i, z);
282 const QChar s[2] = { QChar::highSurrogate(digit), QChar::lowSurrogate(digit) };
283 string.replace(QString(QLatin1Char('0' + i)), QString(s, 2));
284 }
285 break;
286 }
287 default:
288 Q_ASSERT(!"Expected zero digit to be a single UCS2 code-point or a surrogate pair");
289 case 0: // Apparently this locale info was not available.
290 break;
291 }
292 return std::move(string);
293}
294
295QString QSystemLocalePrivate::correctDigits(QString &&string)
296{
297 return substitution() == SAlways ? substituteDigits(std::move(string)) : std::move(string);
298}
299
301{
302 if (zero.isEmpty()) {
303 /* Ten digits plus a terminator.
304
305 https://docs.microsoft.com/en-us/windows/win32/intl/locale-snative-constants
306 "Native equivalents of ASCII 0 through 9. The maximum number of
307 characters allowed for this string is eleven, including a terminating
308 null character."
309 */
310 wchar_t digits[11];
311 if (getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
312 // assert all(digits[i] == i + digits[0] for i in range(1, 10)),
313 // assumed above (unless digits[0] is 0x3007; see QTBUG-85409).
314 zero = QString::fromWCharArray(digits, 1);
315 }
316 }
317 return nullIfEmpty(zero); // Do not std::move().
318}
319
321{
322 return nullIfEmpty(getLocaleInfo(LOCALE_SDECIMAL).toString());
323}
324
326{
327 if (sizes.higher == 0) {
328 wchar_t grouping[10];
329 /*
330 * Nine digits/semicolons plus a terminator.
331
332 * https://learn.microsoft.com/en-us/windows/win32/intl/locale-sgrouping
333 * "Sizes for each group of digits to the left of the decimal. The maximum
334 * number of characters allowed for this string is ten, including a
335 * terminating null character."
336 */
337 int dataSize = getLocaleInfo(LOCALE_SGROUPING, grouping, int(std::size(grouping)));
338 if (dataSize) {
339 // MS does not seem to include {first} so it will always be NAN.
340 QString sysGroupingStr = QString::fromWCharArray(grouping, dataSize);
341 auto tokenized = sysGroupingStr.tokenize(u";");
342 int width[2] = {0, 0};
343 int index = 0;
344 for (const auto tok : tokenized) {
345 bool ok = false;
346 int value = tok.toInt(&ok);
347 if (!ok || !value || index >= 2)
348 break;
349 width[index++] = value;
350 }
351 // The MS docs allow patterns Qt doesn't support, so we treat "X;Y" as "X;Y;0"
352 // and "X" as "X;0" and ignore all but the first two widths. The MS API does
353 // not support an equivalent of sizes.first.
354 if (index > 1) {
355 sizes.least = width[0];
356 sizes.higher = width[1];
357 } else if (index) {
358 sizes.least = sizes.higher = width[0];
359 }
360 }
361 }
362 return QVariant::fromValue(sizes);
363}
364
366{
367 return getLocaleInfo(LOCALE_STHOUSAND); // Empty means don't group digits.
368}
369
371{
372 return nullIfEmpty(getLocaleInfo(LOCALE_SNEGATIVESIGN).toString());
373}
374
376{
377 return nullIfEmpty(getLocaleInfo(LOCALE_SPOSITIVESIGN).toString());
378}
379
380QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type)
381{
382 switch (type) {
383 case QLocale::ShortFormat:
384 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE).toString()));
385 case QLocale::LongFormat:
386 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE).toString()));
387 case QLocale::NarrowFormat:
388 break;
389 }
390 return QVariant();
391}
392
393QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type)
394{
395 switch (type) {
396 case QLocale::ShortFormat:
397 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME).toString()));
398 case QLocale::LongFormat:
399 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT).toString()));
400 case QLocale::NarrowFormat:
401 break;
402 }
403 return {};
404}
405
406QVariant QSystemLocalePrivate::dateTimeFormat(QLocale::FormatType type)
407{
408 QVariant d = dateFormat(type), t = timeFormat(type);
409 if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString)
410 return QString(d.toString() + u' ' + t.toString());
411 return {};
412}
413
414QVariant QSystemLocalePrivate::dayName(int day, QLocale::FormatType type)
415{
416 if (day < 1 || day > 7)
417 return {};
418
419 static constexpr LCTYPE short_day_map[]
420 = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
421 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
422 LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 };
423
424 static constexpr LCTYPE long_day_map[]
425 = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2,
426 LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5,
427 LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 };
428
429 static constexpr LCTYPE narrow_day_map[]
434
435 return nullIfEmpty(getLocaleInfo(
436 (type == QLocale::LongFormat ? long_day_map
437 : type == QLocale::NarrowFormat ? narrow_day_map
438 : short_day_map)[day - 1]).toString());
439}
440
441QVariant QSystemLocalePrivate::standaloneMonthName(int month, QLocale::FormatType type)
442{
443 static constexpr LCTYPE short_month_map[]
444 = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
445 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
446 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
447 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 };
448
449 static constexpr LCTYPE long_month_map[]
450 = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
451 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
452 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
453 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12 };
454
455 if (month < 1 || month > 12)
456 return {};
457
458 // Month is Jan = 1, ... Dec = 12; adjust by 1 to match array indexing from 0:
459 return nullIfEmpty(getLocaleInfo(
460 (type == QLocale::LongFormat ? long_month_map : short_month_map)[month - 1]).toString());
461}
462
463QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type)
464{
465 SYSTEMTIME st = {};
466 st.wYear = 2001;
467 st.wMonth = month;
468 st.wDay = 10;
469
470 const DWORD flags{}; // Must be clear when passing a format string.
471 // MS's docs for the LOCALE_SMONTHNAME* say to include the day in a format.
472 // Educated guess: this works for the LOCALE_SABBREVMONTHNAME*, too, in so
473 // far as the abbreviated plain name might differ from abbreviated
474 // standalone one.
475 const wchar_t *const format = type == QLocale::LongFormat ? L"ddMMMM" : L"ddMMM";
476 wchar_t buf[255];
477 if (getDateFormat(flags, &st, format, buf, 255) > 2) {
478 // Elide the two digits of day number
479 return nullIfEmpty(correctDigits(QString::fromWCharArray(buf + 2)));
480 }
481 return {};
482}
483
484static QString fourDigitYear(int year)
485{
486 // Return year formatted as an (at least) four digit number:
487 return QStringLiteral("%1").arg(year, 4, 10, QChar(u'0'));
488}
489
490QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatted)
491{
492 // Replace our ersatz fakeYear (that MS formats faithfully) with the correct
493 // form of year. We know the two-digit short form of fakeYear can not be
494 // mistaken for the month or day-of-month in the formatted date.
495 Q_ASSERT(fakeYear >= 1970 && fakeYear <= 2400);
496 const bool matchTwo = year >= 0 && year % 100 == fakeYear % 100;
497 auto yearUsed = fourDigitYear(fakeYear);
498 QString sign(year < 0 ? 1 : 0, u'-');
499 auto trueYear = fourDigitYear(year < 0 ? -year : year);
500 if (formatted.contains(yearUsed))
501 return std::move(formatted).replace(yearUsed, sign + trueYear);
502
503 auto tail = QStringView{yearUsed}.last(2);
504 Q_ASSERT(!matchTwo || tail == QString(sign + trueYear.last(2)));
505 if (formatted.contains(tail)) {
506 if (matchTwo)
507 return std::move(formatted);
508 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(2));
509 }
510
511 // Localized digits (regardless of SAlways), perhaps ?
512 // First call to substituteDigits() ensures zero is initialized:
513 trueYear = substituteDigits(std::move(trueYear));
514 if (zero != u'0') {
515 yearUsed = substituteDigits(std::move(yearUsed));
516 if (year < 0)
517 sign = negativeSign().toString();
518
519 if (formatted.contains(yearUsed))
520 return std::move(formatted).replace(yearUsed, sign + trueYear);
521
522 const qsizetype twoDigits = 2 * zero.size();
523 tail = QStringView{yearUsed}.last(twoDigits);
524 if (formatted.contains(tail)) {
525 if (matchTwo)
526 return std::move(formatted);
527 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(twoDigits));
528 }
529 }
530 qWarning("Failed to fix up year in formatted date-string using %d for %d", fakeYear, year);
531 return std::move(formatted);
532}
533
534QVariant QSystemLocalePrivate::toString(QDate date, QLocale::FormatType type)
535{
536 SYSTEMTIME st = {};
537 const int year = date.year();
538 // st.wYear is unsigned; and GetDateFormat() is documented to not handle
539 // dates before 1601.
540 const bool fixup = year < 1601;
541 st.wYear = fixup ? QGregorianCalendar::yearSharingWeekDays(date) : year;
542 st.wMonth = date.month();
543 st.wDay = date.day();
544
545 Q_ASSERT(!fixup || st.wYear % 100 != st.wMonth);
546 Q_ASSERT(!fixup || st.wYear % 100 != st.wDay);
547 // i.e. yearFix() can trust a match of its fakeYear's last two digits to not
548 // be the month or day part of the formatted date.
549
550 DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
551 wchar_t buf[255];
552 if (getDateFormat(flags, &st, NULL, buf, 255)) {
553 QString text = QString::fromWCharArray(buf);
554 if (fixup)
555 text = yearFix(year, st.wYear, std::move(text));
556 return nullIfEmpty(correctDigits(std::move(text)));
557 }
558 return {};
559}
560
561QVariant QSystemLocalePrivate::toString(QTime time, QLocale::FormatType type)
562{
563 SYSTEMTIME st = {};
564 st.wHour = time.hour();
565 st.wMinute = time.minute();
566 st.wSecond = time.second();
567 st.wMilliseconds = 0;
568
569 DWORD flags = 0;
570 // keep the same conditional as timeFormat() above
571 const QString format = type == QLocale::ShortFormat
572 ? getLocaleInfo(LOCALE_SSHORTTIME).toString()
573 : QString();
574 auto formatStr = reinterpret_cast<const wchar_t *>(format.isEmpty() ? nullptr : format.utf16());
575
576 wchar_t buf[255];
577 if (getTimeFormat(flags, &st, formatStr, buf, int(std::size(buf))))
578 return nullIfEmpty(correctDigits(QString::fromWCharArray(buf)));
579 return {};
580}
581
582QVariant QSystemLocalePrivate::toString(const QDateTime &dt, QLocale::FormatType type)
583{
584 QVariant d = toString(dt.date(), type), t = toString(dt.time(), type);
585 if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString)
586 return QString(d.toString() + u' ' + t.toString());
587 return {};
588}
589
591{
592 wchar_t output[2];
593
594 if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
595 if (output[0] == L'1' && !output[1])
596 return QLocale::ImperialSystem;
597 }
598
599 return QLocale::MetricSystem;
600}
601
603{
604 return getLocaleInfo(LOCALE_SSORTLOCALE);
605}
606
608{
609 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
610
611 if (getLocaleInfo(LOCALE_S1159, output, 15))
612 return nullIfEmpty(QString::fromWCharArray(output));
613
614 return QVariant();
615}
616
618{
619 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
620
621 if (getLocaleInfo(LOCALE_S2359, output, 15))
622 return nullIfEmpty(QString::fromWCharArray(output));
623
624 return QVariant();
625}
626
628{
629 wchar_t output[4]; // maximum length including terminating zero character for Win2003+
630
631 if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
632 return QString::fromWCharArray(output).toUInt()+1;
633
634 return 1;
635}
636
637QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat format)
638{
639 wchar_t buf[13];
640 switch (format) {
641 case QLocale::CurrencySymbol:
642 // Some locales do have empty currency symbol. All the same, fall back
643 // to CLDR for confirmation if MS claims that applies.
644 if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
645 return nullIfEmpty(QString::fromWCharArray(buf));
646 break;
647 case QLocale::CurrencyIsoCode:
648 if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
649 return nullIfEmpty(QString::fromWCharArray(buf));
650 break;
651 case QLocale::CurrencyDisplayName: {
652 QVarLengthArray<wchar_t, 64> buf(64);
653 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
654 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
655 break;
656 buf.resize(255); // should be large enough, right?
657 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
658 break;
659 }
660 return nullIfEmpty(QString::fromWCharArray(buf.data()));
661 }
662 default:
663 break;
664 }
665 return QVariant();
666}
667
668QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToStringArgument &arg)
669{
670 QString value;
671 switch (arg.value.typeId()) {
672 case QMetaType::Int:
673 value = QLocaleData::c()->longLongToString(
674 arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
675 break;
676 case QMetaType::UInt:
677 value = QLocaleData::c()->unsLongLongToString(
678 arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
679 break;
680 case QMetaType::Double:
681 value = QLocaleData::c()->doubleToString(
682 arg.value.toDouble(), -1, QLocaleData::DFDecimal, -1, QLocale::OmitGroupSeparator);
683 break;
684 case QMetaType::LongLong:
685 value = QLocaleData::c()->longLongToString(
686 arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
687 break;
688 case QMetaType::ULongLong:
689 value = QLocaleData::c()->unsLongLongToString(
690 arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
691 break;
692 default:
693 return QVariant();
694 }
695
696 QVarLengthArray<wchar_t, 64> out(64);
697
698 QString decimalSep;
699 QString thousandSep;
700 CURRENCYFMT format;
701 CURRENCYFMT *pformat = NULL;
702 if (!arg.symbol.isEmpty()) {
703 format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
704 format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
705 decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP).toString();
706 format.lpDecimalSep = reinterpret_cast<wchar_t *>(decimalSep.data());
707 thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP).toString();
708 format.lpThousandSep = reinterpret_cast<wchar_t *>(thousandSep.data());
709 format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
710 format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
711 format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
712
713 // grouping is complicated and ugly:
714 // int(0) == "123456789.00" == string("0")
715 // int(3) == "123,456,789.00" == string("3;0")
716 // int(30) == "123456,789.00" == string("3;0;0")
717 // int(32) == "12,34,56,789.00" == string("3;2;0")
718 // int(320)== "1234,56,789.00" == string("3;2")
719 QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING).toString();
720 format.Grouping = groupingStr.remove(u';').toInt();
721 if (format.Grouping % 10 == 0) // magic
722 format.Grouping /= 10;
723 else
724 format.Grouping *= 10;
725 pformat = &format;
726 }
727
728 int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
729 pformat, out.data(), out.size());
730 if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
731 ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
732 pformat, out.data(), 0);
733 out.resize(ret);
734 getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
735 pformat, out.data(), out.size());
736 }
737
738 return nullIfEmpty(correctDigits(QString::fromWCharArray(out.data())));
739}
740
742{
743 QStringList result;
744#if QT_CONFIG(cpp_winrt)
745 using namespace winrt::Windows::System::UserProfile;
746 QT_TRY {
747 auto languages = GlobalizationPreferences::Languages();
748 for (const auto &lang : languages)
749 result << QString::fromStdString(winrt::to_string(lang));
750 } QT_CATCH(...) {
751 // pass, just fall back to WIN32 API implementation
752 }
753 if (!result.isEmpty())
754 return result; // else just fall back to WIN32 API implementation
755#endif // QT_CONFIG(cpp_winrt)
756 // mingw and clang still have to use Win32 API
757 unsigned long cnt = 0;
758 QVarLengthArray<wchar_t, 64> buf(64);
759# if !defined(QT_BOOTSTRAPPED) // Not present in MinGW 4.9/bootstrap builds.
760 unsigned long size = buf.size();
761 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
762 size = 0;
763 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
764 GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) {
765 buf.resize(size);
766 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size))
767 return {};
768 }
769 }
770# endif // !QT_BOOTSTRAPPED
771 result.reserve(cnt);
772 const wchar_t *str = buf.constData();
773 for (; cnt > 0; --cnt) {
774 QString s = QString::fromWCharArray(str);
775 if (s.isEmpty())
776 break; // something is wrong
777 result.append(s);
778 str += s.size() + 1;
779 }
780 return nullIfEmpty(std::move(result));
781}
782
784{
785 return getLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
786}
787
789{
790 return getLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
791}
792
793
795{
796 lcid = getDefaultWinId();
797 substitutionType = SUnknown;
798 zero.resize(0);
799}
800
801QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt)
802{
803 QString result;
804 qsizetype i = 0;
805
806 while (i < sys_fmt.size()) {
807 if (sys_fmt.at(i).unicode() == u'\'') {
808 QString text = qt_readEscapedFormatString(sys_fmt, &i);
809 if (text == "'"_L1)
810 result += "''"_L1;
811 else
812 result += u'\'' + text + u'\'';
813 continue;
814 }
815
816 QChar c = sys_fmt.at(i);
817 qsizetype repeat = qt_repeatCount(sys_fmt.mid(i));
818
819 switch (c.unicode()) {
820 // Date
821 case 'y':
822 if (repeat > 5)
823 repeat = 5;
824 else if (repeat == 3)
825 repeat = 2;
826 switch (repeat) {
827 case 1:
828 result += "yy"_L1; // "y" unsupported by Qt, use "yy"
829 break;
830 case 5:
831 result += "yyyy"_L1; // "yyyyy" same as "yyyy" on Windows
832 break;
833 default:
834 result += QString(repeat, u'y');
835 break;
836 }
837 break;
838 case 'g':
839 if (repeat > 2)
840 repeat = 2;
841 switch (repeat) {
842 case 2:
843 break; // no equivalent of "gg" in Qt
844 default:
845 result += u'g';
846 break;
847 }
848 break;
849 case 't':
850 if (repeat > 2)
851 repeat = 2;
852 result += "AP"_L1; // "t" unsupported, use "AP"
853 break;
854 default:
855 result += QString(repeat, c);
856 break;
857 }
858
859 i += repeat;
860 }
861
862 return result;
863}
864
865QLocale QSystemLocale::fallbackLocale() const
866{
867 return QLocale(QString::fromLatin1(getWinLocaleName()));
868}
869
870QVariant QSystemLocale::query(QueryType type, QVariant &&in) const
871{
872 QSystemLocalePrivate *d = systemLocalePrivate();
873 if (!d)
874 return QVariant();
875 switch(type) {
876 case DecimalPoint:
877 return d->decimalPoint();
878 case Grouping:
879 return d->groupingSizes();
880 case GroupSeparator:
881 return d->groupSeparator();
882 case NegativeSign:
883 return d->negativeSign();
884 case PositiveSign:
885 return d->positiveSign();
886 case DateFormatLong:
887 return d->dateFormat(QLocale::LongFormat);
888 case DateFormatShort:
889 return d->dateFormat(QLocale::ShortFormat);
890 case TimeFormatLong:
891 return d->timeFormat(QLocale::LongFormat);
892 case TimeFormatShort:
893 return d->timeFormat(QLocale::ShortFormat);
894 case DateTimeFormatLong:
895 return d->dateTimeFormat(QLocale::LongFormat);
896 case DateTimeFormatShort:
897 return d->dateTimeFormat(QLocale::ShortFormat);
898 case DayNameLong:
899 return d->dayName(in.toInt(), QLocale::LongFormat);
900 case DayNameShort:
901 return d->dayName(in.toInt(), QLocale::ShortFormat);
902 case DayNameNarrow:
903 return d->dayName(in.toInt(), QLocale::NarrowFormat);
904 case StandaloneDayNameLong:
905 case StandaloneDayNameShort:
906 case StandaloneDayNameNarrow:
907 // Windows does not provide standalone day names, so fall back to CLDR
908 return QVariant();
909 case MonthNameLong:
910 return d->monthName(in.toInt(), QLocale::LongFormat);
911 case StandaloneMonthNameLong:
912 return d->standaloneMonthName(in.toInt(), QLocale::LongFormat);
913 case MonthNameShort:
914 return d->monthName(in.toInt(), QLocale::ShortFormat);
915 case StandaloneMonthNameShort:
916 return d->standaloneMonthName(in.toInt(), QLocale::ShortFormat);
917 case MonthNameNarrow:
918 case StandaloneMonthNameNarrow:
919 // Windows provides no narrow month names, so we fall back to CLDR
920 return QVariant();
921 case DateToStringShort:
922 return d->toString(in.toDate(), QLocale::ShortFormat);
923 case DateToStringLong:
924 return d->toString(in.toDate(), QLocale::LongFormat);
925 case TimeToStringShort:
926 return d->toString(in.toTime(), QLocale::ShortFormat);
927 case TimeToStringLong:
928 return d->toString(in.toTime(), QLocale::LongFormat);
929 case DateTimeToStringShort:
930 return d->toString(in.toDateTime(), QLocale::ShortFormat);
931 case DateTimeToStringLong:
932 return d->toString(in.toDateTime(), QLocale::LongFormat);
933 case ZeroDigit:
934 return d->zeroDigit();
935 case LanguageId:
936 case ScriptId:
937 case TerritoryId: {
938 QLocaleId lid = QLocaleId::fromName(QString::fromLatin1(getWinLocaleName()));
939 if (type == LanguageId)
940 return lid.language_id;
941 if (type == ScriptId)
942 return lid.script_id ? lid.script_id : ushort(fallbackLocale().script());
943 return lid.territory_id ? lid.territory_id : ushort(fallbackLocale().territory());
944 }
945 case MeasurementSystem:
946 return d->measurementSystem();
947 case Collation:
948 return d->collation();
949 case AMText:
950 return d->amText();
951 case PMText:
952 return d->pmText();
953 case FirstDayOfWeek:
954 return d->firstDayOfWeek();
955 case CurrencySymbol:
956 return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
957 case CurrencyToString:
958 return d->toCurrencyString(in.value<CurrencyToStringArgument>());
959 case UILanguages:
960 return d->uiLanguages();
961 case LocaleChanged:
962 d->update();
963 break;
964 case NativeLanguageName:
965 return d->nativeLanguageName();
966 case NativeTerritoryName:
967 return d->nativeTerritoryName();
968 default:
969 break;
970 }
971 return QVariant();
972}
973#endif // QT_NO_SYSTEMLOCALE
974
977 char iso_name[6];
978};
979
980namespace {
981struct ByWindowsCode {
982 constexpr bool operator()(int lhs, WindowsToISOListElt rhs) const noexcept
983 { return lhs < int(rhs.windows_code); }
984 constexpr bool operator()(WindowsToISOListElt lhs, int rhs) const noexcept
985 { return int(lhs.windows_code) < rhs; }
986 constexpr bool operator()(WindowsToISOListElt lhs, WindowsToISOListElt rhs) const noexcept
987 { return lhs.windows_code < rhs.windows_code; }
988};
989} // unnamed namespace
990
992 { 0x0401, "ar_SA" },
993 { 0x0402, "bg" },
994 { 0x0403, "ca" },
995 { 0x0404, "zh_TW" },
996 { 0x0405, "cs" },
997 { 0x0406, "da" },
998 { 0x0407, "de" },
999 { 0x0408, "el" },
1000 { 0x0409, "en_US" },
1001 { 0x040a, "es" },
1002 { 0x040b, "fi" },
1003 { 0x040c, "fr" },
1004 { 0x040d, "he" },
1005 { 0x040e, "hu" },
1006 { 0x040f, "is" },
1007 { 0x0410, "it" },
1008 { 0x0411, "ja" },
1009 { 0x0412, "ko" },
1010 { 0x0413, "nl" },
1011 { 0x0414, "no" },
1012 { 0x0414, "nb" }, // alternative spelling; lower_bound will find the first one
1013 { 0x0415, "pl" },
1014 { 0x0416, "pt_BR" },
1015 { 0x0418, "ro" },
1016 { 0x0419, "ru" },
1017 { 0x041a, "hr" },
1018 { 0x041c, "sq" },
1019 { 0x041d, "sv" },
1020 { 0x041e, "th" },
1021 { 0x041f, "tr" },
1022 { 0x0420, "ur" },
1023 { 0x0421, "in" },
1024 { 0x0422, "uk" },
1025 { 0x0423, "be" },
1026 { 0x0425, "et" },
1027 { 0x0426, "lv" },
1028 { 0x0427, "lt" },
1029 { 0x0429, "fa" },
1030 { 0x042a, "vi" },
1031 { 0x042d, "eu" },
1032 { 0x042f, "mk" },
1033 { 0x0436, "af" },
1034 { 0x0438, "fo" },
1035 { 0x0439, "hi" },
1036 { 0x043e, "ms" },
1037 { 0x0458, "mt" },
1038 { 0x0801, "ar_IQ" },
1039 { 0x0804, "zh_CN" },
1040 { 0x0807, "de_CH" },
1041 { 0x0809, "en_GB" },
1042 { 0x080a, "es_MX" },
1043 { 0x080c, "fr_BE" },
1044 { 0x0810, "it_CH" },
1045 { 0x0812, "ko" },
1046 { 0x0813, "nl_BE" },
1047 { 0x0814, "no" },
1048 { 0x0814, "nn" }, // alternative spelling; lower_bound will find the first one
1049 { 0x0816, "pt" },
1050 { 0x081a, "sr" },
1051 { 0x081d, "sv_FI" },
1052 { 0x0c01, "ar_EG" },
1053 { 0x0c04, "zh_HK" },
1054 { 0x0c07, "de_AT" },
1055 { 0x0c09, "en_AU" },
1056 { 0x0c0a, "es" },
1057 { 0x0c0c, "fr_CA" },
1058 { 0x0c1a, "sr" },
1059 { 0x1001, "ar_LY" },
1060 { 0x1004, "zh_SG" },
1061 { 0x1007, "de_LU" },
1062 { 0x1009, "en_CA" },
1063 { 0x100a, "es_GT" },
1064 { 0x100c, "fr_CH" },
1065 { 0x1401, "ar_DZ" },
1066 { 0x1407, "de_LI" },
1067 { 0x1409, "en_NZ" },
1068 { 0x140a, "es_CR" },
1069 { 0x140c, "fr_LU" },
1070 { 0x1801, "ar_MA" },
1071 { 0x1809, "en_IE" },
1072 { 0x180a, "es_PA" },
1073 { 0x1c01, "ar_TN" },
1074 { 0x1c09, "en_ZA" },
1075 { 0x1c0a, "es_DO" },
1076 { 0x2001, "ar_OM" },
1077 { 0x2009, "en_JM" },
1078 { 0x200a, "es_VE" },
1079 { 0x2401, "ar_YE" },
1080 { 0x2409, "en" },
1081 { 0x240a, "es_CO" },
1082 { 0x2801, "ar_SY" },
1083 { 0x2809, "en_BZ" },
1084 { 0x280a, "es_PE" },
1085 { 0x2c01, "ar_JO" },
1086 { 0x2c09, "en_TT" },
1087 { 0x2c0a, "es_AR" },
1088 { 0x3001, "ar_LB" },
1089 { 0x300a, "es_EC" },
1090 { 0x3401, "ar_KW" },
1091 { 0x340a, "es_CL" },
1092 { 0x3801, "ar_AE" },
1093 { 0x380a, "es_UY" },
1094 { 0x3c01, "ar_BH" },
1095 { 0x3c0a, "es_PY" },
1096 { 0x4001, "ar_QA" },
1097 { 0x400a, "es_BO" },
1098 { 0x440a, "es_SV" },
1099 { 0x480a, "es_HN" },
1100 { 0x4c0a, "es_NI" },
1101 { 0x500a, "es_PR" }
1102};
1103
1104static_assert(q20::is_sorted(std::begin(windows_to_iso_list), std::end(windows_to_iso_list),
1105 ByWindowsCode{}));
1106
1107static const char *winLangCodeToIsoName(int code)
1108{
1109 int cmp = code - windows_to_iso_list[0].windows_code;
1110 if (cmp < 0)
1111 return nullptr;
1112
1113 if (cmp == 0)
1115
1116 const auto it = std::lower_bound(std::begin(windows_to_iso_list),
1118 code,
1119 ByWindowsCode{});
1120 if (it != std::end(windows_to_iso_list) && !ByWindowsCode{}(code, *it))
1121 return it->iso_name;
1122
1123 return nullptr;
1124
1125}
1126
1127LCID qt_inIsoNametoLCID(const char *name)
1128{
1129 if (!name)
1130 return LOCALE_USER_DEFAULT;
1131 if (std::strlen(name) >= sizeof(WindowsToISOListElt::iso_name))
1132 return LOCALE_USER_DEFAULT; // cannot possibly match (too long)
1133
1134 // normalize separators:
1135 char n[sizeof(WindowsToISOListElt::iso_name)];
1136 // we know it will fit (we checked at the top of the function)
1137 strncpy(n, name, sizeof(n));
1138 char *c = n;
1139 while (*c) {
1140 if (*c == '-')
1141 *c = '_';
1142 ++c;
1143 }
1144
1145 for (const WindowsToISOListElt &i : windows_to_iso_list) {
1146 if (!memcmp(n, i.iso_name, sizeof(WindowsToISOListElt::iso_name)))
1147 return i.windows_code;
1148 }
1149 return LOCALE_USER_DEFAULT;
1150}
1151
1152
1153static QString winIso639LangName(LCID id)
1154{
1155 QString result;
1156
1157 // Windows returns the wrong ISO639 for some languages, we need to detect them here using
1158 // the language code
1159 QString lang_code;
1160 wchar_t out[256];
1161 if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
1162 lang_code = QString::fromWCharArray(out);
1163
1164 if (!lang_code.isEmpty()) {
1165 const QByteArray latin1 = std::move(lang_code).toLatin1();
1166 const auto [i, used] = qstrntoull(latin1.data(), latin1.size(), 16);
1167 if (used >= latin1.size() || (used > 0 && latin1[used] == '\0')) {
1168 switch (i) {
1169 case 0x814:
1170 result = u"nn"_s; // Nynorsk
1171 break;
1172 default:
1173 break;
1174 }
1175 }
1176 }
1177
1178 if (!result.isEmpty())
1179 return result;
1180
1181 // not one of the problematic languages - do the usual lookup
1182 if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
1183 result = QString::fromWCharArray(out);
1184
1185 return result;
1186}
1187
1188static QString winIso3116CtryName(LCID id)
1189{
1190 QString result;
1191
1192 wchar_t out[256];
1193 if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
1194 result = QString::fromWCharArray(out);
1195
1196 return result;
1197}
1198
1200{
1201 if (id == LOCALE_USER_DEFAULT) {
1202 const auto [name, lcid] = scanLangEnv();
1203 if (!name.isEmpty())
1204 return name;
1205 if (lcid)
1206 return winLangCodeToIsoName(lcid);
1207
1208 id = GetUserDefaultLCID();
1209 }
1210
1211 QString resultusage = winIso639LangName(id);
1212 QString country = winIso3116CtryName(id);
1213 if (!country.isEmpty())
1214 resultusage += u'_' + country;
1215
1216 return std::move(resultusage).toLatin1();
1217}
1218
1219// Helper for plugins/platforms/windows/
1220Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
1221{
1222 return QLocale(QString::fromLatin1(getWinLocaleName(id)));
1223}
1224
1225#if !QT_CONFIG(icu)
1226
1227static QString localeConvertString(const QString &localeID, const QString &str, bool *ok,
1228 DWORD flags)
1229{
1230 Q_ASSERT(ok);
1231 LCID lcid = LocaleNameToLCID(reinterpret_cast<const wchar_t *>(localeID.constData()), 0);
1232 // First compute the size of the output string
1233 const int size = LCMapStringW(lcid, flags, reinterpret_cast<const wchar_t *>(str.constData()),
1234 str.size(), 0, 0);
1235 QString buf(size, Qt::Uninitialized);
1236 if (lcid == 0 || size == 0
1237 || LCMapStringW(lcid, flags, reinterpret_cast<const wchar_t *>(str.constData()), str.size(),
1238 reinterpret_cast<wchar_t *>(buf.data()), buf.size()) == 0) {
1239 *ok = false;
1240 return QString();
1241 }
1242
1243 *ok = true;
1244
1245 return buf;
1246}
1247
1248QString QLocalePrivate::toLower(const QString &str, bool *ok) const
1249{
1250 return localeConvertString(QString::fromUtf8(bcp47Name()), str, ok,
1251 LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING);
1252}
1253
1254QString QLocalePrivate::toUpper(const QString &str, bool *ok) const
1255{
1256 return localeConvertString(QString::fromUtf8(bcp47Name()), str, ok,
1257 LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING);
1258}
1259
1260#endif
1261
1262QT_END_NAMESPACE
LCID qt_inIsoNametoLCID(const char *name)
static constexpr WindowsToISOListElt windows_to_iso_list[]
static QByteArray getWinLocaleName(LCID id=LOCALE_USER_DEFAULT)
static QString fourDigitYear(int year)
#define LOCALE_SSHORTESTDAYNAME2
static auto scanLangEnv()
#define LOCALE_SSHORTTIME
#define LOCALE_SSHORTESTDAYNAME4
#define LOCALE_SSHORTESTDAYNAME3
#define LOCALE_SSHORTESTDAYNAME7
#define LOCALE_SSHORTESTDAYNAME5
#define LOCALE_SNATIVELANGUAGENAME
static QString winIso3116CtryName(LCID id)
#define LOCALE_SSHORTESTDAYNAME6
#define LOCALE_SNATIVECOUNTRYNAME
static auto getDefaultWinId()
#define MUI_LANGUAGE_NAME
static const char * winLangCodeToIsoName(int code)
#define LOCALE_SSHORTESTDAYNAME1
static QString winIso639LangName(LCID id)
#define QStringLiteral(str)
Definition qstring.h:1826
QVariant dateFormat(QLocale::FormatType)
QVariant toString(QDate, QLocale::FormatType)
QVariant dayName(int, QLocale::FormatType)
QVariant monthName(int, QLocale::FormatType)
QVariant currencySymbol(QLocale::CurrencySymbolFormat)
QVariant nativeLanguageName()
QVariant toString(QTime, QLocale::FormatType)
QVariant standaloneMonthName(int, QLocale::FormatType)
QVariant nativeTerritoryName()
QVariant dateTimeFormat(QLocale::FormatType)
QVariant toString(const QDateTime &, QLocale::FormatType)
QVariant measurementSystem()
QVariant timeFormat(QLocale::FormatType)
QVariant toCurrencyString(const QSystemLocale::CurrencyToStringArgument &)