Qt 6.x
The Qt SDK
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
5#include "qlocale_p.h"
6#include "qlocale_tools_p.h"
7
8#include "qstringlist.h"
9#include "qvariant.h"
10#include "qdatetime.h"
11#include "qdebug.h"
12
13#include "QtCore/private/qgregoriancalendar_p.h" // for yearSharingWeekDays()
14
15#include <q20algorithm.h>
16
17#ifdef Q_OS_WIN
18# include <qt_windows.h>
19# include <time.h>
20#endif
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
34static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT);
35static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT);
36static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT);
37
38#ifndef QT_NO_SYSTEMLOCALE
39
40#ifndef MUI_LANGUAGE_NAME
41#define MUI_LANGUAGE_NAME 0x8
42#endif
43#ifndef LOCALE_SSHORTESTDAYNAME1
44# define LOCALE_SSHORTESTDAYNAME1 0x0060
45# define LOCALE_SSHORTESTDAYNAME2 0x0061
46# define LOCALE_SSHORTESTDAYNAME3 0x0062
47# define LOCALE_SSHORTESTDAYNAME4 0x0063
48# define LOCALE_SSHORTESTDAYNAME5 0x0064
49# define LOCALE_SSHORTESTDAYNAME6 0x0065
50# define LOCALE_SSHORTESTDAYNAME7 0x0066
51#endif
52#ifndef LOCALE_SNATIVELANGUAGENAME
53# define LOCALE_SNATIVELANGUAGENAME 0x00000004
54#endif
55#ifndef LOCALE_SNATIVECOUNTRYNAME
56# define LOCALE_SNATIVECOUNTRYNAME 0x00000008
57#endif
58#ifndef LOCALE_SSHORTTIME
59# define LOCALE_SSHORTTIME 0x00000079
60#endif
61
63{
65
90
91 void update();
92
93private:
94 enum SubstitutionType {
95 SUnknown,
96 SContext,
97 SAlways,
98 SNever
99 };
100
101 // cached values:
102 LCID lcid;
103 SubstitutionType substitutionType;
104 QString zero; // cached value for zeroDigit()
105
106 int getLocaleInfo(LCTYPE type, LPWSTR data, int size);
107 QVariant getLocaleInfo(LCTYPE type);
108 int getLocaleInfo_int(LCTYPE type);
109
110 int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size);
111 int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size);
112 int getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size);
113
114 SubstitutionType substitution();
115 QString substituteDigits(QString &&string);
116 QString yearFix(int year, int fakeYear, QString &&formatted);
117
118 static QString winToQtFormat(QStringView sys_fmt);
119
120};
121Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
122
124 : substitutionType(SUnknown)
125{
126 lcid = GetUserDefaultLCID();
127}
128
129inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size)
130{
131 return GetCurrencyFormat(lcid, flags, value, format, data, size);
132}
133
134inline int QSystemLocalePrivate::getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size)
135{
136 return GetDateFormat(lcid, flags, date, format, data, size);
137}
138
139inline int QSystemLocalePrivate::getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size)
140{
141 return GetTimeFormat(lcid, flags, date, format, data, size);
142}
143
144inline int QSystemLocalePrivate::getLocaleInfo(LCTYPE type, LPWSTR data, int size)
145{
146 return GetLocaleInfo(lcid, type, data, size);
147}
148
149QVariant QSystemLocalePrivate::getLocaleInfo(LCTYPE type)
150{
151 // https://docs.microsoft.com/en-us/windows/win32/intl/locale-spositivesign
152 // says empty for LOCALE_SPOSITIVESIGN means "+", although GetLocaleInfo()
153 // is documented to return 0 only on failure, so it's not clear how it
154 // returns empty to mean this; hence the two checks for it below.
155 const QString plus = QStringLiteral("+");
157 // Need to distinguish empty QString packaged as (non-null) QVariant from null QVariant:
158 if (!getLocaleInfo(type, buf.data(), buf.size())) {
159 const auto lastError = GetLastError();
160 if (type == LOCALE_SPOSITIVESIGN && lastError == ERROR_SUCCESS)
161 return plus;
162 if (lastError != ERROR_INSUFFICIENT_BUFFER)
163 return {};
164 int cnt = getLocaleInfo(type, 0, 0);
165 if (cnt == 0)
166 return {};
167 buf.resize(cnt);
168 if (!getLocaleInfo(type, buf.data(), buf.size()))
169 return {};
170 }
171 if (type == LOCALE_SPOSITIVESIGN && !buf[0])
172 return plus;
173 return QString::fromWCharArray(buf.data());
174}
175
176int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type)
177{
178 DWORD value;
179 int r = GetLocaleInfo(lcid, type | LOCALE_RETURN_NUMBER,
180 reinterpret_cast<wchar_t *>(&value),
181 sizeof(value) / sizeof(wchar_t));
182 return r == sizeof(value) / sizeof(wchar_t) ? value : 0;
183}
184
185QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
186{
187 if (substitutionType == SUnknown) {
188 wchar_t buf[8];
189 if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
190 substitutionType = QSystemLocalePrivate::SNever;
191 return substitutionType;
192 }
193 if (buf[0] == '1')
194 substitutionType = QSystemLocalePrivate::SNever;
195 else if (buf[0] == '0')
196 substitutionType = QSystemLocalePrivate::SContext;
197 else if (buf[0] == '2')
198 substitutionType = QSystemLocalePrivate::SAlways;
199 else {
200 wchar_t digits[11]; // See zeroDigit() for why 11.
201 if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
202 substitutionType = QSystemLocalePrivate::SNever;
203 return substitutionType;
204 }
205 if (buf[0] == digits[0] + 2)
206 substitutionType = QSystemLocalePrivate::SAlways;
207 else
208 substitutionType = QSystemLocalePrivate::SNever;
209 }
210 }
211 return substitutionType;
212}
213
214QString QSystemLocalePrivate::substituteDigits(QString &&string)
215{
216 zeroDigit(); // Ensure zero is set.
217 switch (zero.size()) {
218 case 1: {
219 ushort z = zero.at(0).unicode();
220 if (z == '0') // Nothing to do
221 break;
222 Q_ASSERT(z > '9');
223 ushort *const qch = reinterpret_cast<ushort *>(string.data());
224 for (qsizetype i = 0, stop = string.size(); i < stop; ++i) {
225 ushort &ch = qch[i];
226 if (ch >= '0' && ch <= '9')
227 ch = unicodeForDigit(ch - '0', z);
228 }
229 break;
230 }
231 case 2: {
232 // Surrogate pair (high, low):
233 char32_t z = QChar::surrogateToUcs4(zero.at(0), zero.at(1));
234 for (int i = 0; i < 10; i++) {
235 char32_t digit = unicodeForDigit(i, z);
236 const QChar s[2] = { QChar::highSurrogate(digit), QChar::lowSurrogate(digit) };
237 string.replace(QString(QLatin1Char('0' + i)), QString(s, 2));
238 }
239 break;
240 }
241 default:
242 Q_ASSERT(!"Expected zero digit to be a single UCS2 code-point or a surrogate pair");
243 case 0: // Apparently this locale info was not available.
244 break;
245 }
246 return std::move(string);
247}
248
250{
251 if (zero.isEmpty()) {
252 /* Ten digits plus a terminator.
253
254 https://docs.microsoft.com/en-us/windows/win32/intl/locale-snative-constants
255 "Native equivalents of ASCII 0 through 9. The maximum number of
256 characters allowed for this string is eleven, including a terminating
257 null character."
258 */
259 wchar_t digits[11];
260 if (getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
261 // assert all(digits[i] == i + digits[0] for i in range(1, 10)),
262 // assumed above (unless digits[0] is 0x3007; see QTBUG-85409).
264 }
265 }
266 return zero;
267}
268
270{
271 return getLocaleInfo(LOCALE_SDECIMAL);
272}
273
275{
276 return getLocaleInfo(LOCALE_STHOUSAND);
277}
278
280{
281 return getLocaleInfo(LOCALE_SNEGATIVESIGN);
282}
283
285{
286 return getLocaleInfo(LOCALE_SPOSITIVESIGN);
287}
288
290{
291 switch (type) {
293 return winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE).toString());
295 return winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE).toString());
297 break;
298 }
299 return QVariant();
300}
301
303{
304 switch (type) {
306 return winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME).toString());
308 return winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT).toString());
310 break;
311 }
312 return {};
313}
314
316{
317 return QString(dateFormat(type).toString() + u' ' + timeFormat(type).toString());
318}
319
321{
322 if (day < 1 || day > 7)
323 return QString();
324
325 static const LCTYPE short_day_map[]
326 = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
327 LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
328 LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 };
329
330 static const LCTYPE long_day_map[]
331 = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2,
332 LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5,
333 LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 };
334
335 static const LCTYPE narrow_day_map[]
340
341 day -= 1;
342
344 return getLocaleInfo(long_day_map[day]);
346 return getLocaleInfo(narrow_day_map[day]);
347 return getLocaleInfo(short_day_map[day]);
348}
349
351{
352 static const LCTYPE short_month_map[]
353 = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
354 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
355 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
356 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 };
357
358 static const LCTYPE long_month_map[]
359 = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
360 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
361 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
362 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12 };
363
364 if (month < 1 || month > 12)
365 return {};
366
367 // Month is Jan = 1, ... Dec = 12; adjust by 1 to match array indexing from 0:
368 return getLocaleInfo(
369 (type == QLocale::LongFormat ? long_month_map : short_month_map)[month - 1]);
370}
371
373{
374 SYSTEMTIME st = {};
375 st.wYear = 2001;
376 st.wMonth = month;
377 st.wDay = 10;
378
379 const DWORD flags{}; // Must be clear when passing a format string.
380 // MS's docs for the LOCALE_SMONTHNAME* say to include the day in a format.
381 // Educated guess: this works for the LOCALE_SABBREVMONTHNAME*, too, in so
382 // far as the abbreviated plain name might differ from abbreviated
383 // standalone one.
384 const wchar_t *const format = type == QLocale::LongFormat ? L"ddMMMM" : L"ddMMM";
385 wchar_t buf[255];
386 if (getDateFormat(flags, &st, format, buf, 255) > 2) {
387 // Elide the two digits of day number
389 if (substitution() == SAlways)
390 text = substituteDigits(std::move(text));
391 return text;
392 }
393 return {};
394}
395
396static QString fourDigitYear(int year)
397{
398 // Return year formatted as an (at least) four digit number:
399 return QStringLiteral("%1").arg(year, 4, 10, QChar(u'0'));
400}
401
402QString QSystemLocalePrivate::yearFix(int year, int fakeYear, QString &&formatted)
403{
404 // Replace our ersatz fakeYear (that MS formats faithfully) with the correct
405 // form of year. We know the two-digit short form of fakeYear can not be
406 // mistaken for the month or day-of-month in the formatted date.
407 Q_ASSERT(fakeYear >= 1970 && fakeYear <= 2400);
408 const bool matchTwo = year >= 0 && year % 100 == fakeYear % 100;
409 auto yearUsed = fourDigitYear(fakeYear);
410 QString sign(year < 0 ? 1 : 0, u'-');
411 auto trueYear = fourDigitYear(year < 0 ? -year : year);
412 if (formatted.contains(yearUsed))
413 return std::move(formatted).replace(yearUsed, sign + trueYear);
414
415 auto tail = QStringView{yearUsed}.last(2);
416 Q_ASSERT(!matchTwo || tail == QString(sign + trueYear.last(2)));
417 if (formatted.contains(tail)) {
418 if (matchTwo)
419 return std::move(formatted);
420 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(2));
421 }
422
423 // Localized digits, perhaps ?
424 // First call to substituteDigits() ensures zero is initialized:
425 trueYear = substituteDigits(std::move(trueYear));
426 if (zero != u'0') {
427 yearUsed = substituteDigits(std::move(yearUsed));
428 if (year < 0)
430
431 if (formatted.contains(yearUsed))
432 return std::move(formatted).replace(yearUsed, sign + trueYear);
433
434 const qsizetype twoDigits = 2 * zero.size();
435 tail = QStringView{yearUsed}.last(twoDigits);
436 if (formatted.contains(tail)) {
437 if (matchTwo)
438 return std::move(formatted);
439 return std::move(formatted).replace(tail.toString(), sign + trueYear.last(twoDigits));
440 }
441 }
442 qWarning("Failed to fix up year in formatted date-string using %d for %d", fakeYear, year);
443 return std::move(formatted);
444}
445
447{
448 SYSTEMTIME st = {};
449 const int year = date.year();
450 // st.wYear is unsigned; and GetDateFormat() is documented to not handle
451 // dates before 1601.
452 const bool fixup = year < 1601;
453 st.wYear = fixup ? QGregorianCalendar::yearSharingWeekDays(date) : year;
454 st.wMonth = date.month();
455 st.wDay = date.day();
456
457 Q_ASSERT(!fixup || st.wYear % 100 != st.wMonth);
458 Q_ASSERT(!fixup || st.wYear % 100 != st.wDay);
459 // i.e. yearFix() can trust a match of its fakeYear's last two digits to not
460 // be the month or day part of the formatted date.
461
462 DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
463 wchar_t buf[255];
464 if (getDateFormat(flags, &st, NULL, buf, 255)) {
466 if (fixup)
467 text = yearFix(year, st.wYear, std::move(text));
468 if (substitution() == SAlways)
469 text = substituteDigits(std::move(text));
470 return text;
471 }
472 return {};
473}
474
476{
477 SYSTEMTIME st = {};
478 st.wHour = time.hour();
479 st.wMinute = time.minute();
480 st.wSecond = time.second();
481 st.wMilliseconds = 0;
482
483 DWORD flags = 0;
484 // keep the same conditional as timeFormat() above
486 ? getLocaleInfo(LOCALE_SSHORTTIME).toString()
487 : QString();
488 auto formatStr = reinterpret_cast<const wchar_t *>(format.isEmpty() ? nullptr : format.utf16());
489
490 wchar_t buf[255];
491 if (getTimeFormat(flags, &st, formatStr, buf, int(std::size(buf)))) {
493 if (substitution() == SAlways)
494 text = substituteDigits(std::move(text));
495 return text;
496 }
497 return {};
498}
499
501{
502 return QString(toString(dt.date(), type).toString() + u' '
503 + toString(dt.time(), type).toString());
504}
505
507{
508 wchar_t output[2];
509
510 if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
511 if (output[0] == L'1' && !output[1])
513 }
514
516}
517
519{
520 return getLocaleInfo(LOCALE_SSORTLOCALE);
521}
522
524{
525 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
526
527 if (getLocaleInfo(LOCALE_S1159, output, 15))
529
530 return QVariant();
531}
532
534{
535 wchar_t output[15]; // maximum length including terminating zero character for Win2003+
536
537 if (getLocaleInfo(LOCALE_S2359, output, 15))
539
540 return QVariant();
541}
542
544{
545 wchar_t output[4]; // maximum length including terminating zero character for Win2003+
546
547 if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
549
550 return 1;
551}
552
554{
555 wchar_t buf[13];
556 switch (format) {
558 if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
560 break;
562 if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
564 break;
567 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
568 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
569 break;
570 buf.resize(255); // should be large enough, right?
571 if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
572 break;
573 }
574 return QString::fromWCharArray(buf.data());
575 }
576 default:
577 break;
578 }
579 return QVariant();
580}
581
583{
585 switch (arg.value.typeId()) {
586 case QMetaType::Int:
588 arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
589 break;
590 case QMetaType::UInt:
592 arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
593 break;
594 case QMetaType::Double:
596 arg.value.toDouble(), -1, QLocaleData::DFDecimal, -1, QLocale::OmitGroupSeparator);
597 break;
598 case QMetaType::LongLong:
600 arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
601 break;
602 case QMetaType::ULongLong:
604 arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
605 break;
606 default:
607 return QVariant();
608 }
609
611
612 QString decimalSep;
613 QString thousandSep;
614 CURRENCYFMT format;
615 CURRENCYFMT *pformat = NULL;
616 if (!arg.symbol.isEmpty()) {
617 format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
618 format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
619 decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP).toString();
620 format.lpDecimalSep = (wchar_t *)decimalSep.utf16();
621 thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP).toString();
622 format.lpThousandSep = (wchar_t *)thousandSep.utf16();
623 format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
624 format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
625 format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
626
627 // grouping is complicated and ugly:
628 // int(0) == "123456789.00" == string("0")
629 // int(3) == "123,456,789.00" == string("3;0")
630 // int(30) == "123456,789.00" == string("3;0;0")
631 // int(32) == "12,34,56,789.00" == string("3;2;0")
632 // int(320)== "1234,56,789.00" == string("3;2")
633 QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING).toString();
634 format.Grouping = groupingStr.remove(u';').toInt();
635 if (format.Grouping % 10 == 0) // magic
636 format.Grouping /= 10;
637 else
638 format.Grouping *= 10;
639 pformat = &format;
640 }
641
642 int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
643 pformat, out.data(), out.size());
644 if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
645 ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
646 pformat, out.data(), 0);
647 out.resize(ret);
648 getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
649 pformat, out.data(), out.size());
650 }
651
653 if (substitution() == SAlways)
654 value = substituteDigits(std::move(value));
655 return value;
656}
657
659{
661#if QT_CONFIG(cpp_winrt)
662 using namespace winrt;
663 using namespace Windows::System::UserProfile;
664 QT_TRY {
665 auto languages = GlobalizationPreferences::Languages();
666 for (const auto &lang : languages)
667 result << QString::fromStdString(winrt::to_string(lang));
668 } QT_CATCH(...) {
669 // pass, just fall back to WIN32 API implementation
670 }
671 if (!result.isEmpty())
672 return result; // else just fall back to WIN32 API implementation
673#endif // QT_CONFIG(cpp_winrt)
674 // mingw and clang still have to use Win32 API
675 unsigned long cnt = 0;
677# if !defined(QT_BOOTSTRAPPED) // Not present in MinGW 4.9/bootstrap builds.
678 unsigned long size = buf.size();
679 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
680 size = 0;
681 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
682 GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) {
683 buf.resize(size);
684 if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size))
685 return QStringList();
686 }
687 }
688# endif // !QT_BOOTSTRAPPED
689 result.reserve(cnt);
690 const wchar_t *str = buf.constData();
691 for (; cnt > 0; --cnt) {
693 if (s.isEmpty())
694 break; // something is wrong
695 result.append(s);
696 str += s.size() + 1;
697 }
698 return result;
699}
700
702{
703 return getLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
704}
705
707{
708 return getLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
709}
710
711
713{
714 lcid = GetUserDefaultLCID();
715 substitutionType = SUnknown;
716 zero.resize(0);
717}
718
719QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt)
720{
722 qsizetype i = 0;
723
724 while (i < sys_fmt.size()) {
725 if (sys_fmt.at(i).unicode() == u'\'') {
727 if (text == "'"_L1)
728 result += "''"_L1;
729 else
730 result += u'\'' + text + u'\'';
731 continue;
732 }
733
734 QChar c = sys_fmt.at(i);
735 qsizetype repeat = qt_repeatCount(sys_fmt.mid(i));
736
737 switch (c.unicode()) {
738 // Date
739 case 'y':
740 if (repeat > 5)
741 repeat = 5;
742 else if (repeat == 3)
743 repeat = 2;
744 switch (repeat) {
745 case 1:
746 result += "yy"_L1; // "y" unsupported by Qt, use "yy"
747 break;
748 case 5:
749 result += "yyyy"_L1; // "yyyyy" same as "yyyy" on Windows
750 break;
751 default:
752 result += QString(repeat, u'y');
753 break;
754 }
755 break;
756 case 'g':
757 if (repeat > 2)
758 repeat = 2;
759 switch (repeat) {
760 case 2:
761 break; // no equivalent of "gg" in Qt
762 default:
763 result += u'g';
764 break;
765 }
766 break;
767 case 't':
768 if (repeat > 2)
769 repeat = 2;
770 result += "AP"_L1; // "t" unsupported, use "AP"
771 break;
772 default:
773 result += QString(repeat, c);
774 break;
775 }
776
777 i += repeat;
778 }
779
780 return result;
781}
782
784{
786}
787
789{
790 QSystemLocalePrivate *d = systemLocalePrivate();
791 switch(type) {
792 case DecimalPoint:
793 return d->decimalPoint();
794 case GroupSeparator:
795 return d->groupSeparator();
796 case NegativeSign:
797 return d->negativeSign();
798 case PositiveSign:
799 return d->positiveSign();
800 case DateFormatLong:
801 return d->dateFormat(QLocale::LongFormat);
802 case DateFormatShort:
803 return d->dateFormat(QLocale::ShortFormat);
804 case TimeFormatLong:
805 return d->timeFormat(QLocale::LongFormat);
806 case TimeFormatShort:
807 return d->timeFormat(QLocale::ShortFormat);
809 return d->dateTimeFormat(QLocale::LongFormat);
811 return d->dateTimeFormat(QLocale::ShortFormat);
812 case DayNameLong:
813 return d->dayName(in.toInt(), QLocale::LongFormat);
814 case DayNameShort:
815 return d->dayName(in.toInt(), QLocale::ShortFormat);
816 case DayNameNarrow:
817 return d->dayName(in.toInt(), QLocale::NarrowFormat);
821 // Windows does not provide standalone day names, so fall back to CLDR
822 return QVariant();
823 case MonthNameLong:
824 return d->monthName(in.toInt(), QLocale::LongFormat);
826 return d->standaloneMonthName(in.toInt(), QLocale::LongFormat);
827 case MonthNameShort:
828 return d->monthName(in.toInt(), QLocale::ShortFormat);
830 return d->standaloneMonthName(in.toInt(), QLocale::ShortFormat);
831 case MonthNameNarrow:
833 // Windows provides no narrow month names, so we fall back to CLDR
834 return QVariant();
836 return d->toString(in.toDate(), QLocale::ShortFormat);
837 case DateToStringLong:
838 return d->toString(in.toDate(), QLocale::LongFormat);
840 return d->toString(in.toTime(), QLocale::ShortFormat);
841 case TimeToStringLong:
842 return d->toString(in.toTime(), QLocale::LongFormat);
844 return d->toString(in.toDateTime(), QLocale::ShortFormat);
846 return d->toString(in.toDateTime(), QLocale::LongFormat);
847 case ZeroDigit:
848 return d->zeroDigit();
849 case LanguageId:
850 case ScriptId:
851 case TerritoryId: {
853 if (type == LanguageId)
854 return lid.language_id;
855 if (type == ScriptId)
856 return lid.script_id ? lid.script_id : ushort(fallbackLocale().script());
857 return lid.territory_id ? lid.territory_id : ushort(fallbackLocale().territory());
858 }
860 return d->measurementSystem();
861 case Collation:
862 return d->collation();
863 case AMText:
864 return d->amText();
865 case PMText:
866 return d->pmText();
867 case FirstDayOfWeek:
868 return d->firstDayOfWeek();
869 case CurrencySymbol:
870 return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
871 case CurrencyToString:
872 return d->toCurrencyString(in.value<QSystemLocale::CurrencyToStringArgument>());
873 case UILanguages:
874 return d->uiLanguages();
875 case LocaleChanged:
876 d->update();
877 break;
879 return d->nativeLanguageName();
881 return d->nativeTerritoryName();
882 default:
883 break;
884 }
885 return QVariant();
886}
887#endif // QT_NO_SYSTEMLOCALE
888
891 char iso_name[6];
892};
893
894namespace {
895struct ByWindowsCode {
896 constexpr bool operator()(int lhs, WindowsToISOListElt rhs) const noexcept
897 { return lhs < int(rhs.windows_code); }
898 constexpr bool operator()(WindowsToISOListElt lhs, int rhs) const noexcept
899 { return int(lhs.windows_code) < rhs; }
900 constexpr bool operator()(WindowsToISOListElt lhs, WindowsToISOListElt rhs) const noexcept
901 { return lhs.windows_code < rhs.windows_code; }
902};
903} // unnamed namespace
904
906 { 0x0401, "ar_SA" },
907 { 0x0402, "bg\0 " },
908 { 0x0403, "ca\0 " },
909 { 0x0404, "zh_TW" },
910 { 0x0405, "cs\0 " },
911 { 0x0406, "da\0 " },
912 { 0x0407, "de\0 " },
913 { 0x0408, "el\0 " },
914 { 0x0409, "en_US" },
915 { 0x040a, "es\0 " },
916 { 0x040b, "fi\0 " },
917 { 0x040c, "fr\0 " },
918 { 0x040d, "he\0 " },
919 { 0x040e, "hu\0 " },
920 { 0x040f, "is\0 " },
921 { 0x0410, "it\0 " },
922 { 0x0411, "ja\0 " },
923 { 0x0412, "ko\0 " },
924 { 0x0413, "nl\0 " },
925 { 0x0414, "no\0 " },
926 { 0x0415, "pl\0 " },
927 { 0x0416, "pt_BR" },
928 { 0x0418, "ro\0 " },
929 { 0x0419, "ru\0 " },
930 { 0x041a, "hr\0 " },
931 { 0x041c, "sq\0 " },
932 { 0x041d, "sv\0 " },
933 { 0x041e, "th\0 " },
934 { 0x041f, "tr\0 " },
935 { 0x0420, "ur\0 " },
936 { 0x0421, "in\0 " },
937 { 0x0422, "uk\0 " },
938 { 0x0423, "be\0 " },
939 { 0x0425, "et\0 " },
940 { 0x0426, "lv\0 " },
941 { 0x0427, "lt\0 " },
942 { 0x0429, "fa\0 " },
943 { 0x042a, "vi\0 " },
944 { 0x042d, "eu\0 " },
945 { 0x042f, "mk\0 " },
946 { 0x0436, "af\0 " },
947 { 0x0438, "fo\0 " },
948 { 0x0439, "hi\0 " },
949 { 0x043e, "ms\0 " },
950 { 0x0458, "mt\0 " },
951 { 0x0801, "ar_IQ" },
952 { 0x0804, "zh_CN" },
953 { 0x0807, "de_CH" },
954 { 0x0809, "en_GB" },
955 { 0x080a, "es_MX" },
956 { 0x080c, "fr_BE" },
957 { 0x0810, "it_CH" },
958 { 0x0812, "ko\0 " },
959 { 0x0813, "nl_BE" },
960 { 0x0814, "no\0 " },
961 { 0x0816, "pt\0 " },
962 { 0x081a, "sr\0 " },
963 { 0x081d, "sv_FI" },
964 { 0x0c01, "ar_EG" },
965 { 0x0c04, "zh_HK" },
966 { 0x0c07, "de_AT" },
967 { 0x0c09, "en_AU" },
968 { 0x0c0a, "es\0 " },
969 { 0x0c0c, "fr_CA" },
970 { 0x0c1a, "sr\0 " },
971 { 0x1001, "ar_LY" },
972 { 0x1004, "zh_SG" },
973 { 0x1007, "de_LU" },
974 { 0x1009, "en_CA" },
975 { 0x100a, "es_GT" },
976 { 0x100c, "fr_CH" },
977 { 0x1401, "ar_DZ" },
978 { 0x1407, "de_LI" },
979 { 0x1409, "en_NZ" },
980 { 0x140a, "es_CR" },
981 { 0x140c, "fr_LU" },
982 { 0x1801, "ar_MA" },
983 { 0x1809, "en_IE" },
984 { 0x180a, "es_PA" },
985 { 0x1c01, "ar_TN" },
986 { 0x1c09, "en_ZA" },
987 { 0x1c0a, "es_DO" },
988 { 0x2001, "ar_OM" },
989 { 0x2009, "en_JM" },
990 { 0x200a, "es_VE" },
991 { 0x2401, "ar_YE" },
992 { 0x2409, "en\0 " },
993 { 0x240a, "es_CO" },
994 { 0x2801, "ar_SY" },
995 { 0x2809, "en_BZ" },
996 { 0x280a, "es_PE" },
997 { 0x2c01, "ar_JO" },
998 { 0x2c09, "en_TT" },
999 { 0x2c0a, "es_AR" },
1000 { 0x3001, "ar_LB" },
1001 { 0x300a, "es_EC" },
1002 { 0x3401, "ar_KW" },
1003 { 0x340a, "es_CL" },
1004 { 0x3801, "ar_AE" },
1005 { 0x380a, "es_UY" },
1006 { 0x3c01, "ar_BH" },
1007 { 0x3c0a, "es_PY" },
1008 { 0x4001, "ar_QA" },
1009 { 0x400a, "es_BO" },
1010 { 0x440a, "es_SV" },
1011 { 0x480a, "es_HN" },
1012 { 0x4c0a, "es_NI" },
1013 { 0x500a, "es_PR" }
1014};
1015
1016static_assert(q20::is_sorted(std::begin(windows_to_iso_list), std::end(windows_to_iso_list),
1017 ByWindowsCode{}));
1018
1019static const char *winLangCodeToIsoName(int code)
1020{
1021 int cmp = code - windows_to_iso_list[0].windows_code;
1022 if (cmp < 0)
1023 return nullptr;
1024
1025 if (cmp == 0)
1026 return windows_to_iso_list[0].iso_name;
1027
1028 const auto it = std::lower_bound(std::begin(windows_to_iso_list),
1029 std::end(windows_to_iso_list),
1030 code,
1031 ByWindowsCode{});
1032 if (it != std::end(windows_to_iso_list) && !ByWindowsCode{}(code, *it))
1033 return it->iso_name;
1034
1035 return nullptr;
1036
1037}
1038
1039LCID qt_inIsoNametoLCID(const char *name)
1040{
1041 if (!name)
1042 return LOCALE_USER_DEFAULT;
1043 // handle norwegian manually, the list above will fail
1044 if (!strncmp(name, "nb", 2))
1045 return 0x0414;
1046 if (!strncmp(name, "nn", 2))
1047 return 0x0814;
1048
1049 char n[64];
1050 strncpy(n, name, sizeof(n));
1051 n[sizeof(n)-1] = 0;
1052 char *c = n;
1053 while (*c) {
1054 if (*c == '-')
1055 *c = '_';
1056 ++c;
1057 }
1058
1060 if (!strcmp(n, i.iso_name))
1061 return i.windows_code;
1062 }
1063 return LOCALE_USER_DEFAULT;
1064}
1065
1066
1068{
1070
1071 // Windows returns the wrong ISO639 for some languages, we need to detect them here using
1072 // the language code
1073 QString lang_code;
1074 wchar_t out[256];
1075 if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
1076 lang_code = QString::fromWCharArray(out);
1077
1078 if (!lang_code.isEmpty()) {
1079 const QByteArray latin1 = std::move(lang_code).toLatin1();
1080 const auto [i, used] = qstrntoull(latin1.data(), latin1.size(), 16);
1081 if (used >= latin1.size() || (used > 0 && latin1[used] == '\0')) {
1082 switch (i) {
1083 case 0x814:
1084 result = u"nn"_s; // Nynorsk
1085 break;
1086 default:
1087 break;
1088 }
1089 }
1090 }
1091
1092 if (!result.isEmpty())
1093 return result;
1094
1095 // not one of the problematic languages - do the usual lookup
1096 if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
1098
1099 return result;
1100}
1101
1103{
1105
1106 wchar_t out[256];
1107 if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
1109
1110 return result;
1111}
1112
1114{
1116 if (id == LOCALE_USER_DEFAULT) {
1117 static const QByteArray langEnvVar = qgetenv("LANG");
1118 result = langEnvVar;
1119 if (result == "C"
1121 // See if we have a Windows locale code instead of a locale name:
1122 auto [id, used] = qstrntoll(result.data(), result.size(), 0);
1123 if (used <= 0 || id == 0 || id < INT_MIN || id > INT_MAX) // Assume real locale name
1124 return result;
1125 return winLangCodeToIsoName(int(id));
1126 }
1127 }
1128
1129 if (id == LOCALE_USER_DEFAULT)
1130 id = GetUserDefaultLCID();
1131 QString resultusage = winIso639LangName(id);
1132 QString country = winIso3116CtryName(id);
1133 if (!country.isEmpty())
1134 resultusage += u'_' + country;
1135
1136 return std::move(resultusage).toLatin1();
1137}
1138
1139Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
1140{
1142}
1143
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
\inmodule QtCore
Definition qchar.h:48
static constexpr char32_t surrogateToUcs4(char16_t high, char16_t low) noexcept
Converts a UTF16 surrogate pair with the given high and low values to it's UCS-4-encoded code point.
Definition qchar.h:508
static constexpr char16_t highSurrogate(char32_t ucs4) noexcept
Returns the high surrogate part of a UCS-4-encoded code point.
Definition qchar.h:518
constexpr char16_t unicode() const noexcept
Returns the numeric Unicode value of the QChar.
Definition qchar.h:458
static constexpr char16_t lowSurrogate(char32_t ucs4) noexcept
Returns the low surrogate part of a UCS-4-encoded code point.
Definition qchar.h:522
\inmodule QtCore\reentrant
Definition qdatetime.h:257
QTime time() const
Returns the time part of the datetime.
QDate date() const
Returns the date part of the datetime.
\inmodule QtCore \reentrant
Definition qdatetime.h:27
int month() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int day() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static int yearSharingWeekDays(QDate date)
@ MetricSystem
Definition qlocale.h:858
@ ImperialSystem
Definition qlocale.h:861
CurrencySymbolFormat
Definition qlocale.h:883
@ CurrencySymbol
Definition qlocale.h:885
@ CurrencyIsoCode
Definition qlocale.h:884
@ CurrencyDisplayName
Definition qlocale.h:886
@ LongFormat
Definition qlocale.h:865
@ NarrowFormat
Definition qlocale.h:865
@ ShortFormat
Definition qlocale.h:865
@ OmitGroupSeparator
Definition qlocale.h:869
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
constexpr QStringView last(qsizetype n) const noexcept
constexpr QChar at(qsizetype n) const noexcept
Returns the character at position n in this string view.
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:660
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6737
static QString fromStdString(const std::string &s)
Definition qstring.h:1322
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
uint toUInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an {unsigned int} using base base, which is 10 by default and must be...
Definition qstring.h:662
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
void resize(qsizetype size)
Sets the size of the string to size characters.
Definition qstring.cpp:2654
@ DateTimeToStringShort
Definition qlocale_p.h:135
@ StandaloneMonthNameLong
Definition qlocale_p.h:153
@ StandaloneDayNameNarrow
Definition qlocale_p.h:158
@ StandaloneMonthNameNarrow
Definition qlocale_p.h:155
@ StandaloneDayNameShort
Definition qlocale_p.h:157
@ StandaloneDayNameLong
Definition qlocale_p.h:156
@ StandaloneMonthNameShort
Definition qlocale_p.h:154
virtual QVariant query(QueryType type, QVariant in=QVariant()) const
virtual QLocale fallbackLocale() const
\inmodule QtCore \reentrant
Definition qdatetime.h:189
int hour() const
Returns the hour part (0 to 23) of the time.
int minute() const
Returns the minute part (0 to 59) of the time.
int second() const
Returns the second part (0 to 59) of the time.
\inmodule QtCore
Definition qvariant.h:64
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
QString str
[2]
QString text
QDate date
[1]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr bool is_sorted(ForwardIterator first, ForwardIterator last, BinaryPredicate p={})
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define QT_CATCH(A)
#define QT_TRY
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
qsizetype qt_repeatCount(QStringView s)
Definition qlocale.cpp:673
bool qt_splitLocaleName(QStringView name, QStringView *lang, QStringView *script, QStringView *land)
Definition qlocale.cpp:565
QString qt_readEscapedFormatString(QStringView format, qsizetype *idx)
Definition qlocale.cpp:623
static constexpr int digits(int number)
QSimpleParsedNumber< qlonglong > qstrntoll(const char *begin, qsizetype size, int base)
QSimpleParsedNumber< qulonglong > qstrntoull(const char *begin, qsizetype size, int base)
UcsInt unicodeForDigit(uint digit, UcsInt zero)
static QByteArray getWinLocaleName(LCID id=LOCALE_USER_DEFAULT)
static QString fourDigitYear(int year)
#define LOCALE_SSHORTESTDAYNAME2
#define LOCALE_SSHORTTIME
#define LOCALE_SSHORTESTDAYNAME4
#define LOCALE_SSHORTESTDAYNAME3
static QString winIso3116CtryName(LCID id=LOCALE_USER_DEFAULT)
#define LOCALE_SSHORTESTDAYNAME7
static constexpr WindowsToISOListElt windows_to_iso_list[]
#define LOCALE_SSHORTESTDAYNAME5
static QString winIso639LangName(LCID id=LOCALE_USER_DEFAULT)
#define LOCALE_SNATIVELANGUAGENAME
Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
#define LOCALE_SSHORTESTDAYNAME6
#define LOCALE_SNATIVECOUNTRYNAME
#define MUI_LANGUAGE_NAME
LCID qt_inIsoNametoLCID(const char *name)
static const char * winLangCodeToIsoName(int code)
#define LOCALE_SSHORTESTDAYNAME1
#define qWarning
Definition qlogging.h:162
return ret
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLenum GLuint id
[7]
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLfloat n
GLint GLsizei GLsizei GLenum format
const GLubyte * c
GLuint in
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned short ushort
Definition qtypes.h:28
QT_BEGIN_NAMESPACE typedef uchar * output
static int sign(int x)
QTextStream out(stdout)
[7]
QObject::connect nullptr
\inmodule QtCore \reentrant
Definition qchar.h:17
QString doubleToString(double d, int precision=-1, DoubleForm form=DFSignificantDigits, int width=-1, unsigned flags=NoFlags) const
Definition qlocale.cpp:3546
static const QLocaleData * c()
Definition qlocale.cpp:832
QString longLongToString(qint64 l, int precision=-1, int base=10, int width=-1, unsigned flags=NoFlags) const
Definition qlocale.cpp:3785
QString unsLongLongToString(quint64 l, int precision=-1, int base=10, int width=-1, unsigned flags=NoFlags) const
Definition qlocale.cpp:3800
ushort script_id
Definition qlocale_p.h:210
ushort territory_id
Definition qlocale_p.h:210
ushort language_id
Definition qlocale_p.h:210
static Q_AUTOTEST_EXPORT QLocaleId fromName(QStringView name)
Definition qlocale.cpp:609
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 standaloneMonthName(int, QLocale::FormatType)
QVariant nativeTerritoryName()
QVariant dateTimeFormat(QLocale::FormatType)
QVariant measurementSystem()
QVariant timeFormat(QLocale::FormatType)
QVariant toCurrencyString(const QSystemLocale::CurrencyToStringArgument &)