1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Copyright (C) 2016 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qlocale_p.h"
42 #include "qlocale_tools_p.h"
43 
44 #include "qstringlist.h"
45 #include "qvariant.h"
46 #include "qdatetime.h"
47 #include "qdebug.h"
48 
49 #ifdef Q_OS_WIN
50 #   include <qt_windows.h>
51 #   include <time.h>
52 #endif
53 
54 #ifdef Q_OS_WINRT
55 #include <qfunctions_winrt.h>
56 
57 #include <wrl.h>
58 #include <windows.foundation.h>
59 #include <windows.foundation.collections.h>
60 #include <windows.system.userprofile.h>
61 #endif // Q_OS_WINRT
62 
63 QT_BEGIN_NAMESPACE
64 
65 #ifndef Q_OS_WINRT
66 static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT);
67 static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT);
68 static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT);
69 #else // !Q_OS_WINRT
70 using namespace Microsoft::WRL;
71 using namespace Microsoft::WRL::Wrappers;
72 using namespace ABI::Windows::Foundation;
73 using namespace ABI::Windows::System::UserProfile;
74 
75 static QByteArray getWinLocaleName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
76 static const char *winLangCodeToIsoName(int code);
77 static QString winIso639LangName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
78 static QString winIso3116CtryName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
79 #endif // Q_OS_WINRT
80 
81 #ifndef QT_NO_SYSTEMLOCALE
82 
83 #ifndef MUI_LANGUAGE_NAME
84 #define MUI_LANGUAGE_NAME 0x8
85 #endif
86 #ifndef LOCALE_SSHORTESTDAYNAME1
87 #  define LOCALE_SSHORTESTDAYNAME1 0x0060
88 #  define LOCALE_SSHORTESTDAYNAME2 0x0061
89 #  define LOCALE_SSHORTESTDAYNAME3 0x0062
90 #  define LOCALE_SSHORTESTDAYNAME4 0x0063
91 #  define LOCALE_SSHORTESTDAYNAME5 0x0064
92 #  define LOCALE_SSHORTESTDAYNAME6 0x0065
93 #  define LOCALE_SSHORTESTDAYNAME7 0x0066
94 #endif
95 #ifndef LOCALE_SNATIVELANGUAGENAME
96 #  define LOCALE_SNATIVELANGUAGENAME 0x00000004
97 #endif
98 #ifndef LOCALE_SNATIVECOUNTRYNAME
99 #  define LOCALE_SNATIVECOUNTRYNAME 0x00000008
100 #endif
101 #ifndef LOCALE_SSHORTTIME
102 #  define LOCALE_SSHORTTIME 0x00000079
103 #endif
104 
105 struct QSystemLocalePrivate
106 {
107     QSystemLocalePrivate();
108 
109     QVariant zeroDigit();
110     QVariant decimalPoint();
111     QVariant groupSeparator();
112     QVariant negativeSign();
113     QVariant positiveSign();
114     QVariant dateFormat(QLocale::FormatType);
115     QVariant timeFormat(QLocale::FormatType);
116     QVariant dateTimeFormat(QLocale::FormatType);
117     QVariant dayName(int, QLocale::FormatType);
118     QVariant monthName(int, QLocale::FormatType);
119     QVariant toString(QDate, QLocale::FormatType);
120     QVariant toString(QTime, QLocale::FormatType);
121     QVariant toString(const QDateTime &, QLocale::FormatType);
122     QVariant measurementSystem();
123     QVariant collation();
124     QVariant amText();
125     QVariant pmText();
126     QVariant firstDayOfWeek();
127     QVariant currencySymbol(QLocale::CurrencySymbolFormat);
128     QVariant toCurrencyString(const QSystemLocale::CurrencyToStringArgument &);
129     QVariant uiLanguages();
130     QVariant nativeLanguageName();
131     QVariant nativeCountryName();
132 
133     void update();
134 
135 private:
136     enum SubstitutionType {
137         SUnknown,
138         SContext,
139         SAlways,
140         SNever
141     };
142 
143     // cached values:
144 #ifndef Q_OS_WINRT
145     LCID lcid;
146 #else
147     WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
148 #endif
149     SubstitutionType substitutionType;
150     QString zero; // cached value for zeroDigit()
151 
152     int getLocaleInfo(LCTYPE type, LPWSTR data, int size);
153     // Need to distinguish empty QString packaged as (non-null) QVariant from null QVariant:
154     template <typename T = QString>
155     T getLocaleInfo(LCTYPE type, int maxlen = 0);
156     int getLocaleInfo_int(LCTYPE type, int maxlen = 0);
157 
158     int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size);
159     int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size);
160     int getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size);
161 
162     SubstitutionType substitution();
163     QString &substituteDigits(QString &string);
164 
165     static QString winToQtFormat(QStringView sys_fmt);
166 
167 };
Q_GLOBAL_STATIC(QSystemLocalePrivate,systemLocalePrivate)168 Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
169 
170 QSystemLocalePrivate::QSystemLocalePrivate()
171     : substitutionType(SUnknown)
172 {
173 #ifndef Q_OS_WINRT
174     lcid = GetUserDefaultLCID();
175 #else
176     GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
177 #endif
178 }
179 
getCurrencyFormat(DWORD flags,LPCWSTR value,const CURRENCYFMTW * format,LPWSTR data,int size)180 inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size)
181 {
182 #ifndef Q_OS_WINRT
183     return GetCurrencyFormat(lcid, flags, value, format, data, size);
184 #else
185     return GetCurrencyFormatEx(lcName, flags, value, format, data, size);
186 #endif
187 }
188 
getDateFormat(DWORD flags,const SYSTEMTIME * date,LPCWSTR format,LPWSTR data,int size)189 inline int QSystemLocalePrivate::getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size)
190 {
191 #ifndef Q_OS_WINRT
192     return GetDateFormat(lcid, flags, date, format, data, size);
193 #else
194     return GetDateFormatEx(lcName, flags, date, format, data, size, NULL);
195 #endif
196 }
197 
getTimeFormat(DWORD flags,const SYSTEMTIME * date,LPCWSTR format,LPWSTR data,int size)198 inline int QSystemLocalePrivate::getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size)
199 {
200 #ifndef Q_OS_WINRT
201     return GetTimeFormat(lcid, flags, date, format, data, size);
202 #else
203     return GetTimeFormatEx(lcName, flags, date, format, data, size);
204 #endif
205 }
206 
getLocaleInfo(LCTYPE type,LPWSTR data,int size)207 inline int QSystemLocalePrivate::getLocaleInfo(LCTYPE type, LPWSTR data, int size)
208 {
209 #ifndef Q_OS_WINRT
210     return GetLocaleInfo(lcid, type, data, size);
211 #else
212     return GetLocaleInfoEx(lcName, type, data, size);
213 #endif
214 }
215 
216 template<typename T>
getLocaleInfo(LCTYPE type,int maxlen)217 T QSystemLocalePrivate::getLocaleInfo(LCTYPE type, int maxlen)
218 {
219     // https://docs.microsoft.com/en-us/windows/win32/intl/locale-spositivesign
220     // says empty for LOCALE_SPOSITIVESIGN means "+", although GetLocaleInfo()
221     // is documented to return 0 only on failure, so it's not clear how it
222     // returns empty to mean this; hence the two checks for it below.
223     const QString plus = QStringLiteral("+");
224     QVarLengthArray<wchar_t, 64> buf(maxlen ? maxlen : 64);
225     if (!getLocaleInfo(type, buf.data(), buf.size())) {
226         const auto lastError = GetLastError();
227         if (type == LOCALE_SPOSITIVESIGN && lastError == ERROR_SUCCESS)
228             return plus;
229         if (lastError != ERROR_INSUFFICIENT_BUFFER)
230             return {};
231         int cnt = getLocaleInfo(type, 0, 0);
232         if (cnt == 0)
233             return {};
234         buf.resize(cnt);
235         if (!getLocaleInfo(type, buf.data(), buf.size()))
236             return {};
237     }
238     if (type == LOCALE_SPOSITIVESIGN && !buf[0])
239         return plus;
240     return QString::fromWCharArray(buf.data());
241 }
242 
getLocaleInfo_int(LCTYPE type,int maxlen)243 int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type, int maxlen)
244 {
245     QString str = getLocaleInfo(type, maxlen);
246     bool ok = false;
247     int v = str.toInt(&ok);
248     return ok ? v : 0;
249 }
250 
substitution()251 QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
252 {
253     if (substitutionType == SUnknown) {
254         wchar_t buf[8];
255         if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
256             substitutionType = QSystemLocalePrivate::SNever;
257             return substitutionType;
258         }
259         if (buf[0] == '1')
260             substitutionType = QSystemLocalePrivate::SNever;
261         else if (buf[0] == '0')
262             substitutionType = QSystemLocalePrivate::SContext;
263         else if (buf[0] == '2')
264             substitutionType = QSystemLocalePrivate::SAlways;
265         else {
266             wchar_t digits[11]; // See zeroDigit() for why 11.
267             if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
268                 substitutionType = QSystemLocalePrivate::SNever;
269                 return substitutionType;
270             }
271             if (buf[0] == digits[0] + 2)
272                 substitutionType = QSystemLocalePrivate::SAlways;
273             else
274                 substitutionType = QSystemLocalePrivate::SNever;
275         }
276     }
277     return substitutionType;
278 }
279 
substituteDigits(QString & string)280 QString &QSystemLocalePrivate::substituteDigits(QString &string)
281 {
282     zeroDigit(); // Ensure zero is set.
283     switch (zero.size()) {
284     case 1: {
285         const ushort offset = zero.at(0).unicode() - '0';
286         if (!offset) // Nothing to do
287             break;
288         Q_ASSERT(offset > 9);
289         ushort *const qch = reinterpret_cast<ushort *>(string.data());
290         for (int i = 0, stop = string.size(); i < stop; ++i) {
291             ushort &ch = qch[i];
292             if (ch >= '0' && ch <= '9')
293                 ch += offset;
294         }
295         break;
296     }
297     case 2: {
298         // Surrogate pair (high, low):
299         uint digit = QChar::surrogateToUcs4(zero.at(0), zero.at(1));
300         for (int i = 0; i < 10; i++) {
301             const QChar s[2] = { QChar::highSurrogate(digit + i), QChar::lowSurrogate(digit + i) };
302             string.replace(QString(QLatin1Char('0' + i)), QString(s, 2));
303         }
304         break;
305     }
306     default:
307         Q_ASSERT(!"Expected zero digit to be a single UCS2 code-point or a surrogate pair");
308     case 0: // Apparently this locale info was not available.
309         break;
310     }
311     return string;
312 }
313 
zeroDigit()314 QVariant QSystemLocalePrivate::zeroDigit()
315 {
316     if (zero.isEmpty()) {
317         /* Ten digits plus a terminator.
318 
319            https://docs.microsoft.com/en-us/windows/win32/intl/locale-snative-constants
320            "Native equivalents of ASCII 0 through 9. The maximum number of
321            characters allowed for this string is eleven, including a terminating
322            null character."
323          */
324         wchar_t digits[11];
325         if (getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
326             // assert all(digits[i] == i + digits[0] for i in range(1, 10)), assumed above
327             zero = QString::fromWCharArray(digits, 1);
328         }
329     }
330     return zero;
331 }
332 
decimalPoint()333 QVariant QSystemLocalePrivate::decimalPoint()
334 {
335     return getLocaleInfo<QVariant>(LOCALE_SDECIMAL);
336 }
337 
groupSeparator()338 QVariant QSystemLocalePrivate::groupSeparator()
339 {
340     return getLocaleInfo<QVariant>(LOCALE_STHOUSAND);
341 }
342 
negativeSign()343 QVariant QSystemLocalePrivate::negativeSign()
344 {
345     return getLocaleInfo<QVariant>(LOCALE_SNEGATIVESIGN);
346 }
347 
positiveSign()348 QVariant QSystemLocalePrivate::positiveSign()
349 {
350     return getLocaleInfo<QVariant>(LOCALE_SPOSITIVESIGN);
351 }
352 
dateFormat(QLocale::FormatType type)353 QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type)
354 {
355     switch (type) {
356     case QLocale::ShortFormat:
357         return winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE));
358     case QLocale::LongFormat:
359         return winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE));
360     case QLocale::NarrowFormat:
361         break;
362     }
363     return QVariant();
364 }
365 
timeFormat(QLocale::FormatType type)366 QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type)
367 {
368     switch (type) {
369     case QLocale::ShortFormat:
370         return winToQtFormat(getLocaleInfo(LOCALE_SSHORTTIME));
371     case QLocale::LongFormat:
372         return winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT));
373     case QLocale::NarrowFormat:
374         break;
375     }
376     return QVariant();
377 }
378 
dateTimeFormat(QLocale::FormatType type)379 QVariant QSystemLocalePrivate::dateTimeFormat(QLocale::FormatType type)
380 {
381     return QString(dateFormat(type).toString() + QLatin1Char(' ') + timeFormat(type).toString());
382 }
383 
dayName(int day,QLocale::FormatType type)384 QVariant QSystemLocalePrivate::dayName(int day, QLocale::FormatType type)
385 {
386     if (day < 1 || day > 7)
387         return QString();
388 
389     static const LCTYPE short_day_map[]
390         = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
391             LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
392             LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 };
393 
394     static const LCTYPE long_day_map[]
395         = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2,
396             LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5,
397             LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 };
398 
399     static const LCTYPE narrow_day_map[]
400         = { LOCALE_SSHORTESTDAYNAME1, LOCALE_SSHORTESTDAYNAME2,
401             LOCALE_SSHORTESTDAYNAME3, LOCALE_SSHORTESTDAYNAME4,
402             LOCALE_SSHORTESTDAYNAME5, LOCALE_SSHORTESTDAYNAME6,
403             LOCALE_SSHORTESTDAYNAME7 };
404 
405     day -= 1;
406 
407     if (type == QLocale::LongFormat)
408         return getLocaleInfo<QVariant>(long_day_map[day]);
409     if (type == QLocale::NarrowFormat)
410         return getLocaleInfo<QVariant>(narrow_day_map[day]);
411     return getLocaleInfo<QVariant>(short_day_map[day]);
412 }
413 
monthName(int month,QLocale::FormatType type)414 QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type)
415 {
416     static const LCTYPE short_month_map[]
417         = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
418             LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
419             LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
420             LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 };
421 
422     static const LCTYPE long_month_map[]
423         = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
424             LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
425             LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
426             LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12 };
427 
428     month -= 1;
429     if (month < 0 || month > 11)
430         return QString();
431 
432     LCTYPE lctype = (type == QLocale::ShortFormat || type == QLocale::NarrowFormat)
433             ? short_month_map[month] : long_month_map[month];
434     return getLocaleInfo<QVariant>(lctype);
435 }
436 
toString(QDate date,QLocale::FormatType type)437 QVariant QSystemLocalePrivate::toString(QDate date, QLocale::FormatType type)
438 {
439     SYSTEMTIME st;
440     memset(&st, 0, sizeof(SYSTEMTIME));
441     st.wYear = date.year();
442     st.wMonth = date.month();
443     st.wDay = date.day();
444 
445     DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
446     wchar_t buf[255];
447     if (getDateFormat(flags, &st, NULL, buf, 255)) {
448         QString format = QString::fromWCharArray(buf);
449         if (substitution() == SAlways)
450             substituteDigits(format);
451         return format;
452     }
453     return QString();
454 }
455 
toString(QTime time,QLocale::FormatType type)456 QVariant QSystemLocalePrivate::toString(QTime time, QLocale::FormatType type)
457 {
458     SYSTEMTIME st;
459     memset(&st, 0, sizeof(SYSTEMTIME));
460     st.wHour = time.hour();
461     st.wMinute = time.minute();
462     st.wSecond = time.second();
463     st.wMilliseconds = 0;
464 
465     DWORD flags = 0;
466     // keep the same conditional as timeFormat() above
467     if (type == QLocale::ShortFormat)
468         flags = TIME_NOSECONDS;
469 
470     wchar_t buf[255];
471     if (getTimeFormat(flags, &st, NULL, buf, 255)) {
472         QString format = QString::fromWCharArray(buf);
473         if (substitution() == SAlways)
474             substituteDigits(format);
475         return format;
476     }
477     return QString();
478 }
479 
toString(const QDateTime & dt,QLocale::FormatType type)480 QVariant QSystemLocalePrivate::toString(const QDateTime &dt, QLocale::FormatType type)
481 {
482     return QString(toString(dt.date(), type).toString() + QLatin1Char(' ') + toString(dt.time(), type).toString());
483 }
484 
measurementSystem()485 QVariant QSystemLocalePrivate::measurementSystem()
486 {
487     wchar_t output[2];
488 
489     if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
490         QString iMeasure = QString::fromWCharArray(output);
491         if (iMeasure == QLatin1String("1")) {
492             return QLocale::ImperialSystem;
493         }
494     }
495 
496     return QLocale::MetricSystem;
497 }
498 
collation()499 QVariant QSystemLocalePrivate::collation()
500 {
501     return getLocaleInfo<QVariant>(LOCALE_SSORTLOCALE);
502 }
503 
amText()504 QVariant QSystemLocalePrivate::amText()
505 {
506     wchar_t output[15]; // maximum length including  terminating zero character for Win2003+
507 
508     if (getLocaleInfo(LOCALE_S1159, output, 15)) {
509         return QString::fromWCharArray(output);
510     }
511 
512     return QVariant();
513 }
514 
pmText()515 QVariant QSystemLocalePrivate::pmText()
516 {
517     wchar_t output[15]; // maximum length including  terminating zero character for Win2003+
518 
519     if (getLocaleInfo(LOCALE_S2359, output, 15)) {
520         return QString::fromWCharArray(output);
521     }
522 
523     return QVariant();
524 }
525 
firstDayOfWeek()526 QVariant QSystemLocalePrivate::firstDayOfWeek()
527 {
528     wchar_t output[4]; // maximum length including  terminating zero character for Win2003+
529 
530     if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
531         return QString::fromWCharArray(output).toUInt()+1;
532 
533     return 1;
534 }
535 
currencySymbol(QLocale::CurrencySymbolFormat format)536 QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat format)
537 {
538     wchar_t buf[13];
539     switch (format) {
540     case QLocale::CurrencySymbol:
541         if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
542             return QString::fromWCharArray(buf);
543         break;
544     case QLocale::CurrencyIsoCode:
545         if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
546             return QString::fromWCharArray(buf);
547         break;
548     case QLocale::CurrencyDisplayName: {
549         QVarLengthArray<wchar_t, 64> buf(64);
550         if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
551             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
552                 break;
553             buf.resize(255); // should be large enough, right?
554             if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
555                 break;
556         }
557         return QString::fromWCharArray(buf.data());
558     }
559     default:
560         break;
561     }
562     return QVariant();
563 }
564 
toCurrencyString(const QSystemLocale::CurrencyToStringArgument & arg)565 QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToStringArgument &arg)
566 {
567     QString value;
568     switch (arg.value.type()) {
569     case QVariant::Int:
570         value = QLocaleData::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'),
571                                                  arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
572         break;
573     case QVariant::UInt:
574         value = QLocaleData::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'),
575                                                     arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
576         break;
577     case QVariant::Double:
578         value = QLocaleData::doubleToString(QLatin1Char('0'), QLatin1Char('+'), QLatin1Char('-'),
579                                                QLatin1Char(' '), QLatin1Char(','), QLatin1Char('.'),
580                                                arg.value.toDouble(), -1, QLocaleData::DFDecimal, -1, QLocale::OmitGroupSeparator);
581         break;
582     case QVariant::LongLong:
583         value = QLocaleData::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'),
584                                                  arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
585         break;
586     case QVariant::ULongLong:
587         value = QLocaleData::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'),
588                                                     arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
589         break;
590     default:
591         return QVariant();
592     }
593 
594     QVarLengthArray<wchar_t, 64> out(64);
595 
596     QString decimalSep;
597     QString thousandSep;
598     CURRENCYFMT format;
599     CURRENCYFMT *pformat = NULL;
600     if (!arg.symbol.isEmpty()) {
601         format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
602         format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
603         decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP);
604         format.lpDecimalSep = (wchar_t *)decimalSep.utf16();
605         thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP);
606         format.lpThousandSep = (wchar_t *)thousandSep.utf16();
607         format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
608         format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
609         format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
610 
611         // grouping is complicated and ugly:
612         // int(0)  == "123456789.00"    == string("0")
613         // int(3)  == "123,456,789.00"  == string("3;0")
614         // int(30) == "123456,789.00"   == string("3;0;0")
615         // int(32) == "12,34,56,789.00" == string("3;2;0")
616         // int(320)== "1234,56,789.00"  == string("3;2")
617         QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING);
618         format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt();
619         if (format.Grouping % 10 == 0) // magic
620             format.Grouping /= 10;
621         else
622             format.Grouping *= 10;
623         pformat = &format;
624     }
625 
626     int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
627                                   pformat, out.data(), out.size());
628     if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
629         ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
630                                   pformat, out.data(), 0);
631         out.resize(ret);
632         getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
633                             pformat, out.data(), out.size());
634     }
635 
636     value = QString::fromWCharArray(out.data());
637     if (substitution() == SAlways)
638         substituteDigits( value);
639     return value;
640 }
641 
uiLanguages()642 QVariant QSystemLocalePrivate::uiLanguages()
643 {
644 #ifndef Q_OS_WINRT
645     unsigned long cnt = 0;
646     QVarLengthArray<wchar_t, 64> buf(64);
647 #  if !defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE) // Not present in MinGW 4.9/bootstrap builds.
648     unsigned long size = buf.size();
649     if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
650         size = 0;
651         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
652                 GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) {
653             buf.resize(size);
654             if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size))
655                 return QStringList();
656         }
657     }
658 #  endif // !QT_BOOTSTRAPPED && !QT_BUILD_QMAKE
659     QStringList result;
660     result.reserve(cnt);
661     const wchar_t *str = buf.constData();
662     for (; cnt > 0; --cnt) {
663         QString s = QString::fromWCharArray(str);
664         if (s.isEmpty())
665             break; // something is wrong
666         result.append(s);
667         str += s.size() + 1;
668     }
669     return result;
670 #else // !Q_OS_WINRT
671     QStringList result;
672 
673     ComPtr<IGlobalizationPreferencesStatics> preferences;
674     HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences).Get(), &preferences);
675     if (FAILED(hr)) {
676         qWarning("Could not obtain ApplicationLanguagesStatic");
677         return QStringList();
678     }
679 
680     ComPtr<ABI::Windows::Foundation::Collections::IVectorView<HSTRING> > languageList;
681     // Languages is a ranked list of "long names" (e.g. en-US) of preferred languages
682     hr = preferences->get_Languages(&languageList);
683     Q_ASSERT_SUCCEEDED(hr);
684     unsigned int size;
685     hr = languageList->get_Size(&size);
686     Q_ASSERT_SUCCEEDED(hr);
687     result.reserve(size);
688     for (unsigned int i = 0; i < size; ++i) {
689         HString language;
690         hr = languageList->GetAt(i, language.GetAddressOf());
691         Q_ASSERT_SUCCEEDED(hr);
692         UINT32 length;
693         PCWSTR rawString = language.GetRawBuffer(&length);
694         result << QString::fromWCharArray(rawString, length);
695     }
696 
697     return result;
698 #endif // Q_OS_WINRT
699 }
700 
nativeLanguageName()701 QVariant QSystemLocalePrivate::nativeLanguageName()
702 {
703     return getLocaleInfo<QVariant>(LOCALE_SNATIVELANGUAGENAME);
704 }
705 
nativeCountryName()706 QVariant QSystemLocalePrivate::nativeCountryName()
707 {
708     return getLocaleInfo<QVariant>(LOCALE_SNATIVECOUNTRYNAME);
709 }
710 
711 
update()712 void QSystemLocalePrivate::update()
713 {
714 #ifndef Q_OS_WINRT
715     lcid = GetUserDefaultLCID();
716 #else
717     GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
718 #endif
719     substitutionType = SUnknown;
720     zero.resize(0);
721 }
722 
winToQtFormat(QStringView sys_fmt)723 QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt)
724 {
725     QString result;
726     int i = 0;
727 
728     while (i < sys_fmt.size()) {
729         if (sys_fmt.at(i).unicode() == QLatin1Char('\'')) {
730             QString text = qt_readEscapedFormatString(sys_fmt, &i);
731             if (text == QLatin1String("'"))
732                 result += QLatin1String("''");
733             else
734                 result += QLatin1Char('\'') + text + QLatin1Char('\'');
735             continue;
736         }
737 
738         QChar c = sys_fmt.at(i);
739         int repeat = qt_repeatCount(sys_fmt.mid(i));
740 
741         switch (c.unicode()) {
742             // Date
743             case 'y':
744                 if (repeat > 5)
745                     repeat = 5;
746                 else if (repeat == 3)
747                     repeat = 2;
748                 switch (repeat) {
749                     case 1:
750                         result += QLatin1String("yy"); // "y" unsupported by Qt, use "yy"
751                         break;
752                     case 5:
753                         result += QLatin1String("yyyy"); // "yyyyy" same as "yyyy" on Windows
754                         break;
755                     default:
756                         result += QString(repeat, QLatin1Char('y'));
757                         break;
758                 }
759                 break;
760             case 'g':
761                 if (repeat > 2)
762                     repeat = 2;
763                 switch (repeat) {
764                     case 2:
765                         break; // no equivalent of "gg" in Qt
766                     default:
767                         result += QLatin1Char('g');
768                         break;
769                 }
770                 break;
771             case 't':
772                 if (repeat > 2)
773                     repeat = 2;
774                 result += QLatin1String("AP"); // "t" unsupported, use "AP"
775                 break;
776             default:
777                 result += QString(repeat, c);
778                 break;
779         }
780 
781         i += repeat;
782     }
783 
784     return result;
785 }
786 
fallbackUiLocale() const787 QLocale QSystemLocale::fallbackUiLocale() const
788 {
789     return QLocale(QString::fromLatin1(getWinLocaleName()));
790 }
791 
query(QueryType type,QVariant in) const792 QVariant QSystemLocale::query(QueryType type, QVariant in) const
793 {
794     QSystemLocalePrivate *d = systemLocalePrivate();
795     switch(type) {
796     case DecimalPoint:
797         return d->decimalPoint();
798     case GroupSeparator:
799         return d->groupSeparator();
800     case NegativeSign:
801         return d->negativeSign();
802     case PositiveSign:
803         return d->positiveSign();
804     case DateFormatLong:
805         return d->dateFormat(QLocale::LongFormat);
806     case DateFormatShort:
807         return d->dateFormat(QLocale::ShortFormat);
808     case TimeFormatLong:
809         return d->timeFormat(QLocale::LongFormat);
810     case TimeFormatShort:
811         return d->timeFormat(QLocale::ShortFormat);
812     case DateTimeFormatLong:
813         return d->dateTimeFormat(QLocale::LongFormat);
814     case DateTimeFormatShort:
815         return d->dateTimeFormat(QLocale::ShortFormat);
816     case DayNameLong:
817         return d->dayName(in.toInt(), QLocale::LongFormat);
818     case DayNameShort:
819         return d->dayName(in.toInt(), QLocale::ShortFormat);
820     case MonthNameLong:
821     case StandaloneMonthNameLong:
822         return d->monthName(in.toInt(), QLocale::LongFormat);
823     case MonthNameShort:
824     case StandaloneMonthNameShort:
825         return d->monthName(in.toInt(), QLocale::ShortFormat);
826     case DateToStringShort:
827         return d->toString(in.toDate(), QLocale::ShortFormat);
828     case DateToStringLong:
829         return d->toString(in.toDate(), QLocale::LongFormat);
830     case TimeToStringShort:
831         return d->toString(in.toTime(), QLocale::ShortFormat);
832     case TimeToStringLong:
833         return d->toString(in.toTime(), QLocale::LongFormat);
834     case DateTimeToStringShort:
835         return d->toString(in.toDateTime(), QLocale::ShortFormat);
836     case DateTimeToStringLong:
837         return d->toString(in.toDateTime(), QLocale::LongFormat);
838     case ZeroDigit:
839         return d->zeroDigit();
840     case LanguageId:
841     case ScriptId:
842     case CountryId: {
843         QString locale = QString::fromLatin1(getWinLocaleName());
844         QLocale::Language lang;
845         QLocale::Script script;
846         QLocale::Country cntry;
847         QLocalePrivate::getLangAndCountry(locale, lang, script, cntry);
848         if (type == LanguageId)
849             return lang;
850         if (type == ScriptId)
851             return script == QLocale::AnyScript ? fallbackUiLocale().script() : script;
852         if (cntry == QLocale::AnyCountry)
853             return fallbackUiLocale().country();
854         return cntry;
855     }
856     case MeasurementSystem:
857         return d->measurementSystem();
858     case Collation:
859         return d->collation();
860     case AMText:
861         return d->amText();
862     case PMText:
863         return d->pmText();
864     case FirstDayOfWeek:
865         return d->firstDayOfWeek();
866     case CurrencySymbol:
867         return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
868     case CurrencyToString:
869         return d->toCurrencyString(in.value<QSystemLocale::CurrencyToStringArgument>());
870     case UILanguages:
871         return d->uiLanguages();
872     case LocaleChanged:
873         d->update();
874         break;
875     case NativeLanguageName:
876         return d->nativeLanguageName();
877     case NativeCountryName:
878         return d->nativeCountryName();
879     default:
880         break;
881     }
882     return QVariant();
883 }
884 #endif // QT_NO_SYSTEMLOCALE
885 
886 struct WindowsToISOListElt {
887     ushort windows_code;
888     char iso_name[6];
889 };
890 
891 /* NOTE: This array should be sorted by the first column! */
892 static const WindowsToISOListElt windows_to_iso_list[] = {
893     { 0x0401, "ar_SA" },
894     { 0x0402, "bg\0  " },
895     { 0x0403, "ca\0  " },
896     { 0x0404, "zh_TW" },
897     { 0x0405, "cs\0  " },
898     { 0x0406, "da\0  " },
899     { 0x0407, "de\0  " },
900     { 0x0408, "el\0  " },
901     { 0x0409, "en_US" },
902     { 0x040a, "es\0  " },
903     { 0x040b, "fi\0  " },
904     { 0x040c, "fr\0  " },
905     { 0x040d, "he\0  " },
906     { 0x040e, "hu\0  " },
907     { 0x040f, "is\0  " },
908     { 0x0410, "it\0  " },
909     { 0x0411, "ja\0  " },
910     { 0x0412, "ko\0  " },
911     { 0x0413, "nl\0  " },
912     { 0x0414, "no\0  " },
913     { 0x0415, "pl\0  " },
914     { 0x0416, "pt_BR" },
915     { 0x0418, "ro\0  " },
916     { 0x0419, "ru\0  " },
917     { 0x041a, "hr\0  " },
918     { 0x041c, "sq\0  " },
919     { 0x041d, "sv\0  " },
920     { 0x041e, "th\0  " },
921     { 0x041f, "tr\0  " },
922     { 0x0420, "ur\0  " },
923     { 0x0421, "in\0  " },
924     { 0x0422, "uk\0  " },
925     { 0x0423, "be\0  " },
926     { 0x0425, "et\0  " },
927     { 0x0426, "lv\0  " },
928     { 0x0427, "lt\0  " },
929     { 0x0429, "fa\0  " },
930     { 0x042a, "vi\0  " },
931     { 0x042d, "eu\0  " },
932     { 0x042f, "mk\0  " },
933     { 0x0436, "af\0  " },
934     { 0x0438, "fo\0  " },
935     { 0x0439, "hi\0  " },
936     { 0x043e, "ms\0  " },
937     { 0x0458, "mt\0  " },
938     { 0x0801, "ar_IQ" },
939     { 0x0804, "zh_CN" },
940     { 0x0807, "de_CH" },
941     { 0x0809, "en_GB" },
942     { 0x080a, "es_MX" },
943     { 0x080c, "fr_BE" },
944     { 0x0810, "it_CH" },
945     { 0x0812, "ko\0  " },
946     { 0x0813, "nl_BE" },
947     { 0x0814, "no\0  " },
948     { 0x0816, "pt\0  " },
949     { 0x081a, "sr\0  " },
950     { 0x081d, "sv_FI" },
951     { 0x0c01, "ar_EG" },
952     { 0x0c04, "zh_HK" },
953     { 0x0c07, "de_AT" },
954     { 0x0c09, "en_AU" },
955     { 0x0c0a, "es\0  " },
956     { 0x0c0c, "fr_CA" },
957     { 0x0c1a, "sr\0  " },
958     { 0x1001, "ar_LY" },
959     { 0x1004, "zh_SG" },
960     { 0x1007, "de_LU" },
961     { 0x1009, "en_CA" },
962     { 0x100a, "es_GT" },
963     { 0x100c, "fr_CH" },
964     { 0x1401, "ar_DZ" },
965     { 0x1407, "de_LI" },
966     { 0x1409, "en_NZ" },
967     { 0x140a, "es_CR" },
968     { 0x140c, "fr_LU" },
969     { 0x1801, "ar_MA" },
970     { 0x1809, "en_IE" },
971     { 0x180a, "es_PA" },
972     { 0x1c01, "ar_TN" },
973     { 0x1c09, "en_ZA" },
974     { 0x1c0a, "es_DO" },
975     { 0x2001, "ar_OM" },
976     { 0x2009, "en_JM" },
977     { 0x200a, "es_VE" },
978     { 0x2401, "ar_YE" },
979     { 0x2409, "en\0  " },
980     { 0x240a, "es_CO" },
981     { 0x2801, "ar_SY" },
982     { 0x2809, "en_BZ" },
983     { 0x280a, "es_PE" },
984     { 0x2c01, "ar_JO" },
985     { 0x2c09, "en_TT" },
986     { 0x2c0a, "es_AR" },
987     { 0x3001, "ar_LB" },
988     { 0x300a, "es_EC" },
989     { 0x3401, "ar_KW" },
990     { 0x340a, "es_CL" },
991     { 0x3801, "ar_AE" },
992     { 0x380a, "es_UY" },
993     { 0x3c01, "ar_BH" },
994     { 0x3c0a, "es_PY" },
995     { 0x4001, "ar_QA" },
996     { 0x400a, "es_BO" },
997     { 0x440a, "es_SV" },
998     { 0x480a, "es_HN" },
999     { 0x4c0a, "es_NI" },
1000     { 0x500a, "es_PR" }
1001 };
1002 
1003 static const int windows_to_iso_count
1004     = sizeof(windows_to_iso_list)/sizeof(WindowsToISOListElt);
1005 
winLangCodeToIsoName(int code)1006 static const char *winLangCodeToIsoName(int code)
1007 {
1008     int cmp = code - windows_to_iso_list[0].windows_code;
1009     if (cmp < 0)
1010         return 0;
1011 
1012     if (cmp == 0)
1013         return windows_to_iso_list[0].iso_name;
1014 
1015     int begin = 0;
1016     int end = windows_to_iso_count;
1017 
1018     while (end - begin > 1) {
1019         uint mid = (begin + end)/2;
1020 
1021         const WindowsToISOListElt *elt = windows_to_iso_list + mid;
1022         int cmp = code - elt->windows_code;
1023         if (cmp < 0)
1024             end = mid;
1025         else if (cmp > 0)
1026             begin = mid;
1027         else
1028             return elt->iso_name;
1029     }
1030 
1031     return 0;
1032 
1033 }
1034 
qt_inIsoNametoLCID(const char * name)1035 LCID qt_inIsoNametoLCID(const char *name)
1036 {
1037     // handle norwegian manually, the list above will fail
1038     if (!strncmp(name, "nb", 2))
1039         return 0x0414;
1040     if (!strncmp(name, "nn", 2))
1041         return 0x0814;
1042 
1043     char n[64];
1044     strncpy(n, name, sizeof(n));
1045     n[sizeof(n)-1] = 0;
1046     char *c = n;
1047     while (*c) {
1048         if (*c == '-')
1049             *c = '_';
1050         ++c;
1051     }
1052 
1053     for (const WindowsToISOListElt &i : windows_to_iso_list) {
1054         if (!strcmp(n, i.iso_name))
1055             return i.windows_code;
1056     }
1057     return LOCALE_USER_DEFAULT;
1058 }
1059 
1060 
1061 #ifndef Q_OS_WINRT
winIso639LangName(LCID id)1062 static QString winIso639LangName(LCID id)
1063 #else
1064 static QString winIso639LangName(LPWSTR id)
1065 #endif
1066 {
1067     QString result;
1068 
1069     // Windows returns the wrong ISO639 for some languages, we need to detect them here using
1070     // the language code
1071     QString lang_code;
1072     wchar_t out[256];
1073 #ifndef Q_OS_WINRT
1074     if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
1075 #else
1076     if (GetLocaleInfoEx(id, LOCALE_ILANGUAGE, out, 255))
1077 #endif
1078         lang_code = QString::fromWCharArray(out);
1079 
1080     if (!lang_code.isEmpty()) {
1081         const char *endptr;
1082         bool ok;
1083         QByteArray latin1_lang_code = std::move(lang_code).toLatin1();
1084         int i = qstrtoull(latin1_lang_code, &endptr, 16, &ok);
1085         if (ok && *endptr == '\0') {
1086             switch (i) {
1087                 case 0x814:
1088                     result = QLatin1String("nn"); // Nynorsk
1089                     break;
1090                 default:
1091                     break;
1092             }
1093         }
1094     }
1095 
1096     if (!result.isEmpty())
1097         return result;
1098 
1099     // not one of the problematic languages - do the usual lookup
1100 #ifndef Q_OS_WINRT
1101     if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
1102 #else
1103     if (GetLocaleInfoEx(id, LOCALE_SISO639LANGNAME, out, 255))
1104 #endif
1105         result = QString::fromWCharArray(out);
1106 
1107     return result;
1108 }
1109 
1110 #ifndef Q_OS_WINRT
winIso3116CtryName(LCID id)1111 static QString winIso3116CtryName(LCID id)
1112 #else
1113 static QString winIso3116CtryName(LPWSTR id)
1114 #endif
1115 {
1116     QString result;
1117 
1118     wchar_t out[256];
1119 #ifndef Q_OS_WINRT
1120     if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
1121 #else
1122     if (GetLocaleInfoEx(id, LOCALE_SISO3166CTRYNAME, out, 255))
1123 #endif
1124         result = QString::fromWCharArray(out);
1125 
1126     return result;
1127 }
1128 
1129 #ifndef Q_OS_WINRT
getWinLocaleName(LCID id)1130 static QByteArray getWinLocaleName(LCID id)
1131 #else
1132 static QByteArray getWinLocaleName(LPWSTR id)
1133 #endif
1134 {
1135     QByteArray result;
1136 #ifndef Q_OS_WINRT
1137     if (id == LOCALE_USER_DEFAULT) {
1138 #else
1139     if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
1140 #endif
1141         static QByteArray langEnvVar = qgetenv("LANG");
1142         result = langEnvVar;
1143         QString lang, script, cntry;
1144         if ( result == "C" || (!result.isEmpty()
1145                 && qt_splitLocaleName(QString::fromLocal8Bit(result), lang, script, cntry)) ) {
1146             long id = 0;
1147             bool ok = false;
1148             id = qstrtoll(result.data(), 0, 0, &ok);
1149             if ( !ok || id == 0 || id < INT_MIN || id > INT_MAX )
1150                 return result;
1151             return winLangCodeToIsoName(int(id));
1152         }
1153     }
1154 
1155 #ifndef Q_OS_WINRT
1156     if (id == LOCALE_USER_DEFAULT)
1157         id = GetUserDefaultLCID();
1158 #else // !Q_OS_WINRT
1159     WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
1160     if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
1161         GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
1162         id = lcName;
1163     }
1164 #endif // Q_OS_WINRT
1165     QString resultusage = winIso639LangName(id);
1166     QString country = winIso3116CtryName(id);
1167     if (!country.isEmpty())
1168         resultusage += QLatin1Char('_') + country;
1169 
1170     return std::move(resultusage).toLatin1();
1171 }
1172 
1173 Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
1174 {
1175 #ifndef Q_OS_WINRT
1176     return QLocale(QString::fromLatin1(getWinLocaleName(id)));
1177 #else // !Q_OS_WINRT
1178     WCHAR name[LOCALE_NAME_MAX_LENGTH];
1179     LCIDToLocaleName(id, name, LOCALE_NAME_MAX_LENGTH, 0);
1180     return QLocale(QString::fromLatin1(getWinLocaleName(name)));
1181 #endif // Q_OS_WINRT
1182 }
1183 
1184 QT_END_NAMESPACE
1185