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::forward<T>(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 auto tokenized = QStringView{grouping, dataSize}.tokenize(u';');
341 int width[2] = {0, 0};
342 int index = 0;
343 for (const auto tok : tokenized) {
344 bool ok = false;
345 int value = tok.toInt(&ok);
346 if (!ok || !value || index >= 2)
347 break;
348 width[index++] = value;
349 }
350 // The MS docs allow patterns Qt doesn't support, so we treat "X;Y" as "X;Y;0"
351 // and "X" as "X;0" and ignore all but the first two widths. The MS API does
352 // not support an equivalent of sizes.first.
353 if (index > 1) {
354 sizes.least = width[0];
355 sizes.higher = width[1];
356 } else if (index) {
357 sizes.least = sizes.higher = width[0];
358 }
359 }
360 }
361 return QVariant::fromValue(sizes);
362}
363
365{
366 return getLocaleInfo(LOCALE_STHOUSAND); // Empty means don't group digits.
367}
368
370{
371 return nullIfEmpty(getLocaleInfo(LOCALE_SNEGATIVESIGN).toString());
372}
373
375{
376 return nullIfEmpty(getLocaleInfo(LOCALE_SPOSITIVESIGN).toString());
377}
378
379QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type)
380{
381 switch (type) {
382 case QLocale::ShortFormat:
383 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE).toString()));
384 case QLocale::LongFormat:
385 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE).toString()));
386 case QLocale::NarrowFormat:
387 break;
388 }
389 return QVariant();
390}
391
392QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type)
393{
394 switch (type) {
395 case QLocale::ShortFormat:
396 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME).toString()));
397 case QLocale::LongFormat:
398 return nullIfEmpty(winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT).toString()));
399 case QLocale::NarrowFormat:
400 break;
401 }
402 return {};
403}
404
405QVariant QSystemLocalePrivate::dateTimeFormat(QLocale::FormatType type)
406{
407 QVariant d = dateFormat(type), t = timeFormat(type);
408 if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString)
409 return QString(d.toString() + u' ' + t.toString());
410 return {};
411}
412
413QVariant QSystemLocalePrivate::dayName(int day, QLocale::FormatType type)
414{
415 if (day < 1 || day > 7)
416 return {};
417
418 static constexpr LCTYPE short_day_map[]
419 = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
420 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
421 LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 };
422
423 static constexpr LCTYPE long_day_map[]
424 = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2,
425 LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5,
426 LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 };
427
428 static constexpr LCTYPE narrow_day_map[]
433
434 return nullIfEmpty(getLocaleInfo(
435 (type == QLocale::LongFormat ? long_day_map
436 : type == QLocale::NarrowFormat ? narrow_day_map
437 : short_day_map)[day - 1]).toString());
438}
439
440QVariant QSystemLocalePrivate::standaloneMonthName(int month, QLocale::FormatType type)
441{
442 static constexpr LCTYPE short_month_map[]
443 = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
444 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
445 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
446 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 };
447
448 static constexpr LCTYPE long_month_map[]
449 = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
450 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
451 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
452 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12 };
453
454 if (month < 1 || month > 12)
455 return {};
456
457 // Month is Jan = 1, ... Dec = 12; adjust by 1 to match array indexing from 0:
458 return nullIfEmpty(getLocaleInfo(
459 (type == QLocale::LongFormat ? long_month_map : short_month_map)[month - 1]).toString());
460}
461
462QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type)
463{
464 SYSTEMTIME st = {};
465 st.wYear = 2001;
466 st.wMonth = month;
467 st.wDay = 10;
468
469 const DWORD flags{}; // Must be clear when passing a format string.
470 // MS's docs for the LOCALE_SMONTHNAME* say to include the day in a format.
471 // Educated guess: this works for the LOCALE_SABBREVMONTHNAME*, too, in so
472 // far as the abbreviated plain name might differ from abbreviated
473 // standalone one.
474 const wchar_t *const format = type == QLocale::LongFormat ? L"ddMMMM" : L"ddMMM";
475 wchar_t buf[255];
476 if (getDateFormat(flags, &st, format, buf, 255) > 2) {
477 // Elide the two digits of day number
478 return nullIfEmpty(correctDigits(QString::fromWCharArray(buf + 2)));
479 }
480 return {};
481}
482
483static QString fourDigitYear(int year)
484{
485 // Return year formatted as an (at least) four digit number:
486 return QStringLiteral("%1").arg(year, 4, 10, QChar(u'0'));
487}
488
489QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatted)
490{
491 // Replace our ersatz fakeYear (that MS formats faithfully) with the correct
492 // form of year. We know the two-digit short form of fakeYear can not be
493 // mistaken for the month or day-of-month in the formatted date.
494 Q_ASSERT(fakeYear >= 1970 && fakeYear <= 2400);
495 const bool matchTwo = year >= 0 && year % 100 == fakeYear % 100;
496 auto yearUsed = fourDigitYear(fakeYear);
497 QString sign(year < 0 ? 1 : 0, u'-');
498 auto trueYear = fourDigitYear(year < 0 ? -year : year);
499 if (formatted.contains(yearUsed))
500 return std::move(formatted).replace(yearUsed, sign + trueYear);
501
502 auto tail = QStringView{yearUsed}.last(2);
503 Q_ASSERT(!matchTwo || tail == QString(sign + trueYear.last(2)));
504 if (formatted.contains(tail)) {
505 if (matchTwo)
506 return std::move(formatted);
507 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(2));
508 }
509
510 // Localized digits (regardless of SAlways), perhaps ?
511 // First call to substituteDigits() ensures zero is initialized:
512 trueYear = substituteDigits(std::move(trueYear));
513 if (zero != u'0') {
514 yearUsed = substituteDigits(std::move(yearUsed));
515 if (year < 0)
516 sign = negativeSign().toString();
517
518 if (formatted.contains(yearUsed))
519 return std::move(formatted).replace(yearUsed, sign + trueYear);
520
521 const qsizetype twoDigits = 2 * zero.size();
522 tail = QStringView{yearUsed}.last(twoDigits);
523 if (formatted.contains(tail)) {
524 if (matchTwo)
525 return std::move(formatted);
526 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(twoDigits));
527 }
528 }
529 qWarning("Failed to fix up year in formatted date-string using %d for %d", fakeYear, year);
530 return std::move(formatted);
531}
532
533QVariant QSystemLocalePrivate::toString(QDate date, QLocale::FormatType type)
534{
535 SYSTEMTIME st = {};
536 const int year = date.year();
537 // st.wYear is unsigned; and GetDateFormat() is documented to not handle
538 // dates before 1601.
539 const bool fixup = year < 1601;
540 st.wYear = fixup ? QGregorianCalendar::yearSharingWeekDays(date) : year;
541 st.wMonth = date.month();
542 st.wDay = date.day();
543
544 Q_ASSERT(!fixup || st.wYear % 100 != st.wMonth);
545 Q_ASSERT(!fixup || st.wYear % 100 != st.wDay);
546 // i.e. yearFix() can trust a match of its fakeYear's last two digits to not
547 // be the month or day part of the formatted date.
548
549 DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
550 wchar_t buf[255];
551 if (getDateFormat(flags, &st, NULL, buf, 255)) {
552 QString text = QString::fromWCharArray(buf);
553 if (fixup)
554 text = yearFix(year, st.wYear, std::move(text));
555 return nullIfEmpty(correctDigits(std::move(text)));
556 }
557 return {};
558}
559
560QVariant QSystemLocalePrivate::toString(QTime time, QLocale::FormatType type)
561{
562 SYSTEMTIME st = {};
563 st.wHour = time.hour();
564 st.wMinute = time.minute();
565 st.wSecond = time.second();
566 st.wMilliseconds = 0;
567
568 DWORD flags = 0;
569 // keep the same conditional as timeFormat() above
570 const QString format = type == QLocale::ShortFormat
571 ? getLocaleInfo(LOCALE_SSHORTTIME).toString()
572 : QString();
573 auto formatStr = reinterpret_cast<const wchar_t *>(format.isEmpty() ? nullptr : format.utf16());
574
575 wchar_t buf[255];
576 if (getTimeFormat(flags, &st, formatStr, buf, int(std::size(buf))))
577 return nullIfEmpty(correctDigits(QString::fromWCharArray(buf)));
578 return {};
579}
580
581QVariant QSystemLocalePrivate::toString(const QDateTime &dt, QLocale::FormatType type)
582{
583 QVariant d = toString(dt.date(), type), t = toString(dt.time(), type);
584 if (d.typeId() == QMetaType::QString && t.typeId() == QMetaType::QString)
585 return QString(d.toString() + u' ' + t.toString());
586 return {};
587}
588
590{
591 wchar_t output[2];
592
593 if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
594 if (output[0] == L'1' && !output[1])
595 return QLocale::ImperialSystem;
596 }
597
598 return QLocale::MetricSystem;
599}
600
602{
603 return getLocaleInfo(LOCALE_SSORTLOCALE);
604}
605
607{
608 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
609
610 if (getLocaleInfo(LOCALE_S1159, output, 15))
611 return nullIfEmpty(QString::fromWCharArray(output));
612
613 return QVariant();
614}
615
617{
618 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
619
620 if (getLocaleInfo(LOCALE_S2359, output, 15))
621 return nullIfEmpty(QString::fromWCharArray(output));
622
623 return QVariant();
624}
625
627{
628 wchar_t output[4]; // maximum length including terminating zero character for Win2003+
629
630 if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
631 return QString::fromWCharArray(output).toUInt()+1;
632
633 return 1;
634}
635
636QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat format)
637{
638 wchar_t buf[13];
639 switch (format) {
640 case QLocale::CurrencySymbol:
641 // Some locales do have empty currency symbol. All the same, fall back
642 // to CLDR for confirmation if MS claims that applies.
643 if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
644 return nullIfEmpty(QString::fromWCharArray(buf));
645 break;
646 case QLocale::CurrencyIsoCode:
647 if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
648 return nullIfEmpty(QString::fromWCharArray(buf));
649 break;
650 case QLocale::CurrencyDisplayName: {
651 QVarLengthArray<wchar_t, 64> buf(64);
652 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
653 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
654 break;
655 buf.resize(255); // should be large enough, right?
656 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
657 break;
658 }
659 return nullIfEmpty(QString::fromWCharArray(buf.data()));
660 }
661 default:
662 break;
663 }
664 return QVariant();
665}
666
667QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToStringArgument &arg)
668{
669 QString value;
670 switch (arg.value.typeId()) {
671 case QMetaType::Int:
672 value = QLocaleData::c()->longLongToString(
673 arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
674 break;
675 case QMetaType::UInt:
676 value = QLocaleData::c()->unsLongLongToString(
677 arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
678 break;
679 case QMetaType::Double:
680 value = QLocaleData::c()->doubleToString(
681 arg.value.toDouble(), -1, QLocaleData::DFDecimal, -1, QLocale::OmitGroupSeparator);
682 break;
683 case QMetaType::LongLong:
684 value = QLocaleData::c()->longLongToString(
685 arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
686 break;
687 case QMetaType::ULongLong:
688 value = QLocaleData::c()->unsLongLongToString(
689 arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
690 break;
691 default:
692 return QVariant();
693 }
694
695 QVarLengthArray<wchar_t, 64> out(64);
696
697 QString decimalSep;
698 QString thousandSep;
699 CURRENCYFMT format;
700 CURRENCYFMT *pformat = NULL;
701 if (!arg.symbol.isEmpty()) {
702 format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
703 format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
704 decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP).toString();
705 format.lpDecimalSep = reinterpret_cast<wchar_t *>(decimalSep.data());
706 thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP).toString();
707 format.lpThousandSep = reinterpret_cast<wchar_t *>(thousandSep.data());
708 format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
709 format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
710 format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
711
712 // grouping is complicated and ugly:
713 // int(0) == "123456789.00" == string("0")
714 // int(3) == "123,456,789.00" == string("3;0")
715 // int(30) == "123456,789.00" == string("3;0;0")
716 // int(32) == "12,34,56,789.00" == string("3;2;0")
717 // int(320)== "1234,56,789.00" == string("3;2")
718 QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING).toString();
719 format.Grouping = groupingStr.remove(u';').toInt();
720 if (format.Grouping % 10 == 0) // magic
721 format.Grouping /= 10;
722 else
723 format.Grouping *= 10;
724 pformat = &format;
725 }
726
727 int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
728 pformat, out.data(), out.size());
729 if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
730 ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
731 pformat, out.data(), 0);
732 out.resize(ret);
733 getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
734 pformat, out.data(), out.size());
735 }
736
737 return nullIfEmpty(correctDigits(QString::fromWCharArray(out.data())));
738}
739
741{
742 QStringList result;
743#if QT_CONFIG(cpp_winrt)
744 using namespace winrt::Windows::System::UserProfile;
745 QT_TRY {
746 auto languages = GlobalizationPreferences::Languages();
747 for (const auto &lang : languages)
748 result << QString::fromStdString(winrt::to_string(lang));
749 } QT_CATCH(...) {
750 // pass, just fall back to WIN32 API implementation
751 }
752 if (!result.isEmpty())
753 return result; // else just fall back to WIN32 API implementation
754#endif // QT_CONFIG(cpp_winrt)
755 // mingw and clang still have to use Win32 API
756 unsigned long cnt = 0;
757 QVarLengthArray<wchar_t, 64> buf(64);
758# if !defined(QT_BOOTSTRAPPED) // Not present in MinGW 4.9/bootstrap builds.
759 unsigned long size = buf.size();
760 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
761 size = 0;
762 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
763 GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) {
764 buf.resize(size);
765 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size))
766 return {};
767 }
768 }
769# endif // !QT_BOOTSTRAPPED
770 result.reserve(cnt);
771 const wchar_t *str = buf.constData();
772 for (; cnt > 0; --cnt) {
773 QString s = QString::fromWCharArray(str);
774 if (s.isEmpty())
775 break; // something is wrong
776 result.append(s);
777 str += s.size() + 1;
778 }
779 return nullIfEmpty(std::move(result));
780}
781
783{
784 return getLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
785}
786
788{
789 return getLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
790}
791
792
794{
795 lcid = getDefaultWinId();
796 substitutionType = SUnknown;
797 zero.resize(0);
798}
799
800QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt)
801{
802 QString result;
803 qsizetype i = 0;
804
805 while (i < sys_fmt.size()) {
806 if (sys_fmt.at(i).unicode() == u'\'') {
807 QString text = qt_readEscapedFormatString(sys_fmt, &i);
808 if (text == "'"_L1)
809 result += "''"_L1;
810 else
811 result += u'\'' + text + u'\'';
812 continue;
813 }
814
815 QChar c = sys_fmt.at(i);
816 qsizetype repeat = qt_repeatCount(sys_fmt.mid(i));
817
818 switch (c.unicode()) {
819 // Date
820 case 'y':
821 if (repeat > 5)
822 repeat = 5;
823 else if (repeat == 3)
824 repeat = 2;
825 switch (repeat) {
826 case 1:
827 result += "yy"_L1; // "y" unsupported by Qt, use "yy"
828 break;
829 case 5:
830 result += "yyyy"_L1; // "yyyyy" same as "yyyy" on Windows
831 break;
832 default:
833 result += QString(repeat, u'y');
834 break;
835 }
836 break;
837 case 'g':
838 if (repeat > 2)
839 repeat = 2;
840 switch (repeat) {
841 case 2:
842 break; // no equivalent of "gg" in Qt
843 default:
844 result += u'g';
845 break;
846 }
847 break;
848 case 't':
849 if (repeat > 2)
850 repeat = 2;
851 result += "AP"_L1; // "t" unsupported, use "AP"
852 break;
853 default:
854 result += QString(repeat, c);
855 break;
856 }
857
858 i += repeat;
859 }
860
861 return result;
862}
863
864QLocale QSystemLocale::fallbackLocale() const
865{
866 return QLocale(QString::fromLatin1(getWinLocaleName()));
867}
868
869QVariant QSystemLocale::query(QueryType type, QVariant &&in) const
870{
871 QSystemLocalePrivate *d = systemLocalePrivate();
872 if (!d)
873 return QVariant();
874 switch(type) {
875 case DecimalPoint:
876 return d->decimalPoint();
877 case Grouping:
878 return d->groupingSizes();
879 case GroupSeparator:
880 return d->groupSeparator();
881 case NegativeSign:
882 return d->negativeSign();
883 case PositiveSign:
884 return d->positiveSign();
885 case DateFormatLong:
886 return d->dateFormat(QLocale::LongFormat);
887 case DateFormatShort:
888 return d->dateFormat(QLocale::ShortFormat);
889 case TimeFormatLong:
890 return d->timeFormat(QLocale::LongFormat);
891 case TimeFormatShort:
892 return d->timeFormat(QLocale::ShortFormat);
893 case DateTimeFormatLong:
894 return d->dateTimeFormat(QLocale::LongFormat);
895 case DateTimeFormatShort:
896 return d->dateTimeFormat(QLocale::ShortFormat);
897 case DayNameLong:
898 return d->dayName(in.toInt(), QLocale::LongFormat);
899 case DayNameShort:
900 return d->dayName(in.toInt(), QLocale::ShortFormat);
901 case DayNameNarrow:
902 return d->dayName(in.toInt(), QLocale::NarrowFormat);
903 case StandaloneDayNameLong:
904 case StandaloneDayNameShort:
905 case StandaloneDayNameNarrow:
906 // Windows does not provide standalone day names, so fall back to CLDR
907 return QVariant();
908 case MonthNameLong:
909 return d->monthName(in.toInt(), QLocale::LongFormat);
910 case StandaloneMonthNameLong:
911 return d->standaloneMonthName(in.toInt(), QLocale::LongFormat);
912 case MonthNameShort:
913 return d->monthName(in.toInt(), QLocale::ShortFormat);
914 case StandaloneMonthNameShort:
915 return d->standaloneMonthName(in.toInt(), QLocale::ShortFormat);
916 case MonthNameNarrow:
917 case StandaloneMonthNameNarrow:
918 // Windows provides no narrow month names, so we fall back to CLDR
919 return QVariant();
920 case DateToStringShort:
921 return d->toString(in.toDate(), QLocale::ShortFormat);
922 case DateToStringLong:
923 return d->toString(in.toDate(), QLocale::LongFormat);
924 case TimeToStringShort:
925 return d->toString(in.toTime(), QLocale::ShortFormat);
926 case TimeToStringLong:
927 return d->toString(in.toTime(), QLocale::LongFormat);
928 case DateTimeToStringShort:
929 return d->toString(in.toDateTime(), QLocale::ShortFormat);
930 case DateTimeToStringLong:
931 return d->toString(in.toDateTime(), QLocale::LongFormat);
932 case ZeroDigit:
933 return d->zeroDigit();
934 case LanguageId:
935 case ScriptId:
936 case TerritoryId: {
937 QLocaleId lid = QLocaleId::fromName(QString::fromLatin1(getWinLocaleName()));
938 if (type == LanguageId)
939 return lid.language_id;
940 if (type == ScriptId)
941 return lid.script_id ? lid.script_id : ushort(fallbackLocale().script());
942 return lid.territory_id ? lid.territory_id : ushort(fallbackLocale().territory());
943 }
944 case MeasurementSystem:
945 return d->measurementSystem();
946 case Collation:
947 return d->collation();
948 case AMText:
949 return d->amText();
950 case PMText:
951 return d->pmText();
952 case FirstDayOfWeek:
953 return d->firstDayOfWeek();
954 case CurrencySymbol:
955 return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
956 case CurrencyToString:
957 return d->toCurrencyString(in.value<CurrencyToStringArgument>());
958 case UILanguages:
959 return d->uiLanguages();
960 case LocaleChanged:
961 d->update();
962 break;
963 case NativeLanguageName:
964 return d->nativeLanguageName();
965 case NativeTerritoryName:
966 return d->nativeTerritoryName();
967 default:
968 break;
969 }
970 return QVariant();
971}
972#endif // QT_NO_SYSTEMLOCALE
973
976 char iso_name[6];
977};
978
979namespace {
980struct ByWindowsCode {
981 constexpr bool operator()(int lhs, WindowsToISOListElt rhs) const noexcept
982 { return lhs < int(rhs.windows_code); }
983 constexpr bool operator()(WindowsToISOListElt lhs, int rhs) const noexcept
984 { return int(lhs.windows_code) < rhs; }
985 constexpr bool operator()(WindowsToISOListElt lhs, WindowsToISOListElt rhs) const noexcept
986 { return lhs.windows_code < rhs.windows_code; }
987};
988} // unnamed namespace
989
991 { 0x0401, "ar_SA" },
992 { 0x0402, "bg" },
993 { 0x0403, "ca" },
994 { 0x0404, "zh_TW" },
995 { 0x0405, "cs" },
996 { 0x0406, "da" },
997 { 0x0407, "de" },
998 { 0x0408, "el" },
999 { 0x0409, "en_US" },
1000 { 0x040a, "es" },
1001 { 0x040b, "fi" },
1002 { 0x040c, "fr" },
1003 { 0x040d, "he" },
1004 { 0x040e, "hu" },
1005 { 0x040f, "is" },
1006 { 0x0410, "it" },
1007 { 0x0411, "ja" },
1008 { 0x0412, "ko" },
1009 { 0x0413, "nl" },
1010 { 0x0414, "no" },
1011 { 0x0414, "nb" }, // alternative spelling; lower_bound will find the first one
1012 { 0x0415, "pl" },
1013 { 0x0416, "pt_BR" },
1014 { 0x0418, "ro" },
1015 { 0x0419, "ru" },
1016 { 0x041a, "hr" },
1017 { 0x041c, "sq" },
1018 { 0x041d, "sv" },
1019 { 0x041e, "th" },
1020 { 0x041f, "tr" },
1021 { 0x0420, "ur" },
1022 { 0x0421, "in" },
1023 { 0x0422, "uk" },
1024 { 0x0423, "be" },
1025 { 0x0425, "et" },
1026 { 0x0426, "lv" },
1027 { 0x0427, "lt" },
1028 { 0x0429, "fa" },
1029 { 0x042a, "vi" },
1030 { 0x042d, "eu" },
1031 { 0x042f, "mk" },
1032 { 0x0436, "af" },
1033 { 0x0438, "fo" },
1034 { 0x0439, "hi" },
1035 { 0x043e, "ms" },
1036 { 0x0458, "mt" },
1037 { 0x0801, "ar_IQ" },
1038 { 0x0804, "zh_CN" },
1039 { 0x0807, "de_CH" },
1040 { 0x0809, "en_GB" },
1041 { 0x080a, "es_MX" },
1042 { 0x080c, "fr_BE" },
1043 { 0x0810, "it_CH" },
1044 { 0x0812, "ko" },
1045 { 0x0813, "nl_BE" },
1046 { 0x0814, "no" },
1047 { 0x0814, "nn" }, // alternative spelling; lower_bound will find the first one
1048 { 0x0816, "pt" },
1049 { 0x081a, "sr" },
1050 { 0x081d, "sv_FI" },
1051 { 0x0c01, "ar_EG" },
1052 { 0x0c04, "zh_HK" },
1053 { 0x0c07, "de_AT" },
1054 { 0x0c09, "en_AU" },
1055 { 0x0c0a, "es" },
1056 { 0x0c0c, "fr_CA" },
1057 { 0x0c1a, "sr" },
1058 { 0x1001, "ar_LY" },
1059 { 0x1004, "zh_SG" },
1060 { 0x1007, "de_LU" },
1061 { 0x1009, "en_CA" },
1062 { 0x100a, "es_GT" },
1063 { 0x100c, "fr_CH" },
1064 { 0x1401, "ar_DZ" },
1065 { 0x1407, "de_LI" },
1066 { 0x1409, "en_NZ" },
1067 { 0x140a, "es_CR" },
1068 { 0x140c, "fr_LU" },
1069 { 0x1801, "ar_MA" },
1070 { 0x1809, "en_IE" },
1071 { 0x180a, "es_PA" },
1072 { 0x1c01, "ar_TN" },
1073 { 0x1c09, "en_ZA" },
1074 { 0x1c0a, "es_DO" },
1075 { 0x2001, "ar_OM" },
1076 { 0x2009, "en_JM" },
1077 { 0x200a, "es_VE" },
1078 { 0x2401, "ar_YE" },
1079 { 0x2409, "en" },
1080 { 0x240a, "es_CO" },
1081 { 0x2801, "ar_SY" },
1082 { 0x2809, "en_BZ" },
1083 { 0x280a, "es_PE" },
1084 { 0x2c01, "ar_JO" },
1085 { 0x2c09, "en_TT" },
1086 { 0x2c0a, "es_AR" },
1087 { 0x3001, "ar_LB" },
1088 { 0x300a, "es_EC" },
1089 { 0x3401, "ar_KW" },
1090 { 0x340a, "es_CL" },
1091 { 0x3801, "ar_AE" },
1092 { 0x380a, "es_UY" },
1093 { 0x3c01, "ar_BH" },
1094 { 0x3c0a, "es_PY" },
1095 { 0x4001, "ar_QA" },
1096 { 0x400a, "es_BO" },
1097 { 0x440a, "es_SV" },
1098 { 0x480a, "es_HN" },
1099 { 0x4c0a, "es_NI" },
1100 { 0x500a, "es_PR" }
1101};
1102
1103static_assert(q20::is_sorted(std::begin(windows_to_iso_list), std::end(windows_to_iso_list),
1104 ByWindowsCode{}));
1105
1106static const char *winLangCodeToIsoName(int code)
1107{
1108 int cmp = code - windows_to_iso_list[0].windows_code;
1109 if (cmp < 0)
1110 return nullptr;
1111
1112 if (cmp == 0)
1114
1115 const auto it = std::lower_bound(std::begin(windows_to_iso_list),
1117 code,
1118 ByWindowsCode{});
1119 if (it != std::end(windows_to_iso_list) && !ByWindowsCode{}(code, *it))
1120 return it->iso_name;
1121
1122 return nullptr;
1123
1124}
1125
1126LCID qt_inIsoNametoLCID(const char *name)
1127{
1128 if (!name)
1129 return LOCALE_USER_DEFAULT;
1130 if (std::strlen(name) >= sizeof(WindowsToISOListElt::iso_name))
1131 return LOCALE_USER_DEFAULT; // cannot possibly match (too long)
1132
1133 // normalize separators:
1134 char n[sizeof(WindowsToISOListElt::iso_name)];
1135 // we know it will fit (we checked at the top of the function)
1136 strncpy(n, name, sizeof(n));
1137 char *c = n;
1138 while (*c) {
1139 if (*c == '-')
1140 *c = '_';
1141 ++c;
1142 }
1143
1144 for (const WindowsToISOListElt &i : windows_to_iso_list) {
1145 if (!memcmp(n, i.iso_name, sizeof(WindowsToISOListElt::iso_name)))
1146 return i.windows_code;
1147 }
1148 return LOCALE_USER_DEFAULT;
1149}
1150
1151
1152static QString winIso639LangName(LCID id)
1153{
1154 QString result;
1155
1156 // Windows returns the wrong ISO639 for some languages, we need to detect them here using
1157 // the language code
1158 QString lang_code;
1159 wchar_t out[256];
1160 if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
1161 lang_code = QString::fromWCharArray(out);
1162
1163 if (!lang_code.isEmpty()) {
1164 const QByteArray latin1 = std::move(lang_code).toLatin1();
1165 const auto [i, used] = qstrntoull(latin1.data(), latin1.size(), 16);
1166 if (used >= latin1.size() || (used > 0 && latin1[used] == '\0')) {
1167 switch (i) {
1168 case 0x814:
1169 result = u"nn"_s; // Nynorsk
1170 break;
1171 default:
1172 break;
1173 }
1174 }
1175 }
1176
1177 if (!result.isEmpty())
1178 return result;
1179
1180 // not one of the problematic languages - do the usual lookup
1181 if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
1182 result = QString::fromWCharArray(out);
1183
1184 return result;
1185}
1186
1187static QString winIso3116CtryName(LCID id)
1188{
1189 QString result;
1190
1191 wchar_t out[256];
1192 if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
1193 result = QString::fromWCharArray(out);
1194
1195 return result;
1196}
1197
1199{
1200 if (id == LOCALE_USER_DEFAULT) {
1201 const auto [name, lcid] = scanLangEnv();
1202 if (!name.isEmpty())
1203 return name;
1204 if (lcid)
1205 return winLangCodeToIsoName(lcid);
1206
1207 id = GetUserDefaultLCID();
1208 }
1209
1210 QString resultusage = winIso639LangName(id);
1211 QString country = winIso3116CtryName(id);
1212 if (!country.isEmpty())
1213 resultusage += u'_' + country;
1214
1215 return std::move(resultusage).toLatin1();
1216}
1217
1218// Helper for plugins/platforms/windows/
1219Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
1220{
1221 return QLocale(QString::fromLatin1(getWinLocaleName(id)));
1222}
1223
1224#if !QT_CONFIG(icu)
1225
1226static QString localeConvertString(const QString &localeID, const QString &str, bool *ok,
1227 DWORD flags)
1228{
1229 Q_ASSERT(ok);
1230 LCID lcid = LocaleNameToLCID(reinterpret_cast<const wchar_t *>(localeID.constData()), 0);
1231 // First compute the size of the output string
1232 const int size = LCMapStringW(lcid, flags, reinterpret_cast<const wchar_t *>(str.constData()),
1233 str.size(), 0, 0);
1234 QString buf(size, Qt::Uninitialized);
1235 if (lcid == 0 || size == 0
1236 || LCMapStringW(lcid, flags, reinterpret_cast<const wchar_t *>(str.constData()), str.size(),
1237 reinterpret_cast<wchar_t *>(buf.data()), buf.size()) == 0) {
1238 *ok = false;
1239 return QString();
1240 }
1241
1242 *ok = true;
1243
1244 return buf;
1245}
1246
1247QString QLocalePrivate::toLower(const QString &str, bool *ok) const
1248{
1249 return localeConvertString(QString::fromUtf8(bcp47Name()), str, ok,
1250 LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING);
1251}
1252
1253QString QLocalePrivate::toUpper(const QString &str, bool *ok) const
1254{
1255 return localeConvertString(QString::fromUtf8(bcp47Name()), str, ok,
1256 LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING);
1257}
1258
1259#endif
1260
1261QT_END_NAMESPACE
Combined button and popup list for selecting options.
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:1825
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 &)