Merge remote-tracking branch 'origin/master' into api_changes
[profile/ivi/qtbase.git] / src / corelib / tools / qlocale_win.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qlocale_p.h"
43 #include "qlocale_tools_p.h"
44
45 #include "qstringlist.h"
46 #include "qvariant.h"
47 #include "qdatetime.h"
48
49 #include "private/qsystemlibrary_p.h"
50
51 #include "qdebug.h"
52
53 #ifdef Q_OS_WIN
54 #   include <qt_windows.h>
55 #   include <time.h>
56 #endif
57
58 QT_BEGIN_NAMESPACE
59
60 static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT);
61 static const char *winLangCodeToIsoName(int code);
62 static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT);
63 static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT);
64
65 #ifndef QT_NO_SYSTEMLOCALE
66
67 #ifndef MUI_LANGUAGE_NAME
68 #define MUI_LANGUAGE_NAME 0x8
69 #endif
70 #ifndef LOCALE_SSHORTESTDAYNAME1
71 #  define LOCALE_SSHORTESTDAYNAME1 0x0060
72 #  define LOCALE_SSHORTESTDAYNAME2 0x0061
73 #  define LOCALE_SSHORTESTDAYNAME3 0x0062
74 #  define LOCALE_SSHORTESTDAYNAME4 0x0063
75 #  define LOCALE_SSHORTESTDAYNAME5 0x0064
76 #  define LOCALE_SSHORTESTDAYNAME6 0x0065
77 #  define LOCALE_SSHORTESTDAYNAME7 0x0066
78 #endif
79 #ifndef LOCALE_SNATIVELANGUAGENAME
80 #  define LOCALE_SNATIVELANGUAGENAME 0x00000004
81 #endif
82 #ifndef LOCALE_SNATIVECOUNTRYNAME
83 #  define LOCALE_SNATIVECOUNTRYNAME 0x00000008
84 #endif
85
86 struct QSystemLocalePrivate
87 {
88     QSystemLocalePrivate();
89
90     QChar zeroDigit();
91     QChar decimalPoint();
92     QChar groupSeparator();
93     QChar negativeSign();
94     QChar positiveSign();
95     QVariant dateFormat(QLocale::FormatType);
96     QVariant timeFormat(QLocale::FormatType);
97     QVariant dateTimeFormat(QLocale::FormatType);
98     QVariant dayName(int, QLocale::FormatType);
99     QVariant monthName(int, QLocale::FormatType);
100     QVariant toString(const QDate &, QLocale::FormatType);
101     QVariant toString(const QTime &, QLocale::FormatType);
102     QVariant toString(const QDateTime &, QLocale::FormatType);
103     QVariant measurementSystem();
104     QVariant amText();
105     QVariant pmText();
106     QVariant firstDayOfWeek();
107     QVariant currencySymbol(QLocale::CurrencySymbolFormat);
108     QVariant toCurrencyString(const QSystemLocale::CurrencyToStringArgument &);
109     QVariant uiLanguages();
110     QVariant nativeLanguageName();
111     QVariant nativeCountryName();
112
113     void update();
114
115 private:
116     QByteArray langEnvVar;
117
118     enum SubstitutionType {
119         SUnknown,
120         SContext,
121         SAlways,
122         SNever
123     };
124
125     // cached values:
126     LCID lcid;
127     SubstitutionType substitutionType;
128     QChar zero;
129
130     QString getLocaleInfo(LCTYPE type, int maxlen = 0);
131     int getLocaleInfo_int(LCTYPE type, int maxlen = 0);
132     QChar getLocaleInfo_qchar(LCTYPE type);
133
134     SubstitutionType substitution();
135     QString &substituteDigits(QString &string);
136
137     static QString winToQtFormat(const QString &sys_fmt);
138
139 };
140 Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
141
142 QSystemLocalePrivate::QSystemLocalePrivate()
143     : substitutionType(SUnknown)
144 {
145     langEnvVar = qgetenv("LANG");
146     lcid = GetUserDefaultLCID();
147 }
148
149 QString QSystemLocalePrivate::getLocaleInfo(LCTYPE type, int maxlen)
150 {
151     QVarLengthArray<wchar_t, 64> buf(maxlen ? maxlen : 64);
152     if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
153         return QString();
154     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
155         int cnt = GetLocaleInfo(lcid, type, 0, 0);
156         if (cnt == 0)
157             return QString();
158         buf.resize(cnt);
159         if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
160             return QString();
161     }
162     return QString::fromWCharArray(buf.data());
163 }
164
165 int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type, int maxlen)
166 {
167     QString str = getLocaleInfo(type, maxlen);
168     bool ok = false;
169     int v = str.toInt(&ok);
170     return ok ? v : 0;
171 }
172
173 QChar QSystemLocalePrivate::getLocaleInfo_qchar(LCTYPE type)
174 {
175     QString str = getLocaleInfo(type);
176     return str.isEmpty() ? QChar() : str.at(0);
177 }
178
179 QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
180 {
181     if (substitutionType == SUnknown) {
182         wchar_t buf[8];
183         if (!GetLocaleInfo(lcid, LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
184             substitutionType = QSystemLocalePrivate::SNever;
185             return substitutionType;
186         }
187         if (buf[0] == '1')
188             substitutionType = QSystemLocalePrivate::SNever;
189         else if (buf[0] == '0')
190             substitutionType = QSystemLocalePrivate::SContext;
191         else if (buf[0] == '2')
192             substitutionType = QSystemLocalePrivate::SAlways;
193         else {
194             wchar_t digits[11];
195             if (!GetLocaleInfo(lcid, LOCALE_SNATIVEDIGITS, digits, 11)) {
196                 substitutionType = QSystemLocalePrivate::SNever;
197                 return substitutionType;
198             }
199             const wchar_t zero = digits[0];
200             if (buf[0] == zero + 2)
201                 substitutionType = QSystemLocalePrivate::SAlways;
202             else
203                 substitutionType = QSystemLocalePrivate::SNever;
204         }
205     }
206     return substitutionType;
207 }
208
209 QString &QSystemLocalePrivate::substituteDigits(QString &string)
210 {
211     ushort zero = zeroDigit().unicode();
212     ushort *qch = (ushort *)string.data();
213     for (ushort *end = qch + string.size(); qch != end; ++qch) {
214         if (*qch >= '0' && *qch <= '9')
215             *qch = zero + (*qch - '0');
216     }
217     return string;
218 }
219
220 QChar QSystemLocalePrivate::zeroDigit()
221 {
222     if (zero.isNull())
223         zero = getLocaleInfo_qchar(LOCALE_SNATIVEDIGITS);
224     return zero;
225 }
226
227 QChar QSystemLocalePrivate::decimalPoint()
228 {
229     return getLocaleInfo_qchar(LOCALE_SDECIMAL);
230 }
231
232 QChar QSystemLocalePrivate::groupSeparator()
233 {
234     return getLocaleInfo_qchar(LOCALE_STHOUSAND);
235 }
236
237 QChar QSystemLocalePrivate::negativeSign()
238 {
239     return getLocaleInfo_qchar(LOCALE_SNEGATIVESIGN);
240 }
241
242 QChar QSystemLocalePrivate::positiveSign()
243 {
244     return getLocaleInfo_qchar(LOCALE_SPOSITIVESIGN);
245 }
246
247 QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type)
248 {
249     switch (type) {
250     case QLocale::ShortFormat:
251         return winToQtFormat(getLocaleInfo(LOCALE_SSHORTDATE));
252     case QLocale::LongFormat:
253         return winToQtFormat(getLocaleInfo(LOCALE_SLONGDATE));
254     case QLocale::NarrowFormat:
255         break;
256     }
257     return QVariant();
258 }
259
260 QVariant QSystemLocalePrivate::timeFormat(QLocale::FormatType type)
261 {
262     switch (type) {
263     case QLocale::ShortFormat:
264         return winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT)); //###
265     case QLocale::LongFormat:
266         return winToQtFormat(getLocaleInfo(LOCALE_STIMEFORMAT));
267     case QLocale::NarrowFormat:
268         break;
269     }
270     return QVariant();
271 }
272
273 QVariant QSystemLocalePrivate::dateTimeFormat(QLocale::FormatType type)
274 {
275     return QString(dateFormat(type).toString() + QLatin1Char(' ') + timeFormat(type).toString());
276 }
277
278 QVariant QSystemLocalePrivate::dayName(int day, QLocale::FormatType type)
279 {
280     static const LCTYPE short_day_map[]
281         = { LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
282             LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
283             LOCALE_SABBREVDAYNAME6, LOCALE_SABBREVDAYNAME7 };
284
285     static const LCTYPE long_day_map[]
286         = { LOCALE_SDAYNAME1, LOCALE_SDAYNAME2,
287             LOCALE_SDAYNAME3, LOCALE_SDAYNAME4, LOCALE_SDAYNAME5,
288             LOCALE_SDAYNAME6, LOCALE_SDAYNAME7 };
289
290     static const LCTYPE narrow_day_map[]
291         = { LOCALE_SSHORTESTDAYNAME1, LOCALE_SSHORTESTDAYNAME2,
292             LOCALE_SSHORTESTDAYNAME3, LOCALE_SSHORTESTDAYNAME4,
293             LOCALE_SSHORTESTDAYNAME5, LOCALE_SSHORTESTDAYNAME6,
294             LOCALE_SSHORTESTDAYNAME7 };
295
296     day -= 1;
297
298     if (type == QLocale::LongFormat)
299         return getLocaleInfo(long_day_map[day]);
300     else if (type == QLocale::NarrowFormat && QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA)
301         return getLocaleInfo(narrow_day_map[day]);
302     return getLocaleInfo(short_day_map[day]);
303 }
304
305 QVariant QSystemLocalePrivate::monthName(int month, QLocale::FormatType type)
306 {
307     static const LCTYPE short_month_map[]
308         = { LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
309             LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
310             LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
311             LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12 };
312
313     static const LCTYPE long_month_map[]
314         = { LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
315             LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
316             LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
317             LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12 };
318
319     month -= 1;
320     if (month < 0 || month > 11)
321     return QString();
322
323     LCTYPE lctype = (type == QLocale::ShortFormat || type == QLocale::NarrowFormat)
324             ? short_month_map[month] : long_month_map[month];
325     return getLocaleInfo(lctype);
326 }
327
328 QVariant QSystemLocalePrivate::toString(const QDate &date, QLocale::FormatType type)
329 {
330     SYSTEMTIME st;
331     memset(&st, 0, sizeof(SYSTEMTIME));
332     st.wYear = date.year();
333     st.wMonth = date.month();
334     st.wDay = date.day();
335
336     DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
337     wchar_t buf[255];
338     if (GetDateFormat(lcid, flags, &st, NULL, buf, 255)) {
339         QString format = QString::fromWCharArray(buf);
340         if (substitution() == SAlways)
341             substituteDigits(format);
342         return format;
343     }
344     return QString();
345 }
346
347 QVariant QSystemLocalePrivate::toString(const QTime &time, QLocale::FormatType)
348 {
349     SYSTEMTIME st;
350     memset(&st, 0, sizeof(SYSTEMTIME));
351     st.wHour = time.hour();
352     st.wMinute = time.minute();
353     st.wSecond = time.second();
354     st.wMilliseconds = 0;
355
356     DWORD flags = 0;
357
358     wchar_t buf[255];
359     if (GetTimeFormat(lcid, flags, &st, NULL, buf, 255)) {
360         QString format = QString::fromWCharArray(buf);
361         if (substitution() == SAlways)
362             substituteDigits(format);
363         return format;
364     }
365     return QString();
366 }
367
368 QVariant QSystemLocalePrivate::toString(const QDateTime &dt, QLocale::FormatType type)
369 {
370     return QString(toString(dt.date(), type).toString() + QLatin1Char(' ') + toString(dt.time(), type).toString());
371 }
372
373 QVariant QSystemLocalePrivate::measurementSystem()
374 {
375     wchar_t output[2];
376
377     if (GetLocaleInfo(lcid, LOCALE_IMEASURE, output, 2)) {
378         QString iMeasure = QString::fromWCharArray(output);
379         if (iMeasure == QLatin1String("1")) {
380             return QLocale::ImperialSystem;
381         }
382     }
383
384     return QLocale::MetricSystem;
385 }
386
387 QVariant QSystemLocalePrivate::amText()
388 {
389     wchar_t output[15]; // maximum length including  terminating zero character for Win2003+
390
391     if (GetLocaleInfo(lcid, LOCALE_S1159, output, 15)) {
392         return QString::fromWCharArray(output);
393     }
394
395     return QVariant();
396 }
397
398 QVariant QSystemLocalePrivate::pmText()
399 {
400     wchar_t output[15]; // maximum length including  terminating zero character for Win2003+
401
402     if (GetLocaleInfo(lcid, LOCALE_S2359, output, 15)) {
403         return QString::fromWCharArray(output);
404     }
405
406     return QVariant();
407 }
408
409 QVariant QSystemLocalePrivate::firstDayOfWeek()
410 {
411     wchar_t output[4]; // maximum length including  terminating zero character for Win2003+
412
413     if (GetLocaleInfo(lcid, LOCALE_IFIRSTDAYOFWEEK, output, 4))
414         return QString::fromWCharArray(output).toUInt()+1;
415
416     return 1;
417 }
418
419 QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat format)
420 {
421     wchar_t buf[13];
422     switch (format) {
423     case QLocale::CurrencySymbol:
424         if (GetLocaleInfo(lcid, LOCALE_SCURRENCY, buf, 13))
425             return QString::fromWCharArray(buf);
426         break;
427     case QLocale::CurrencyIsoCode:
428         if (GetLocaleInfo(lcid, LOCALE_SINTLSYMBOL, buf, 9))
429             return QString::fromWCharArray(buf);
430         break;
431     case QLocale::CurrencyDisplayName: {
432         QVarLengthArray<wchar_t, 64> buf(64);
433         if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
434             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
435                 break;
436             buf.resize(255); // should be large enough, right?
437             if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
438                 break;
439         }
440         return QString::fromWCharArray(buf.data());
441     }
442     default:
443         break;
444     }
445     return QVariant();
446 }
447
448 QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToStringArgument &arg)
449 {
450     QString value;
451     switch (arg.value.type()) {
452     case QVariant::Int:
453         value = QLocalePrivate::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'),
454                                                  arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
455         break;
456     case QVariant::UInt:
457         value = QLocalePrivate::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'),
458                                                     arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator);
459         break;
460     case QVariant::Double:
461         value = QLocalePrivate::doubleToString(QLatin1Char('0'), QLatin1Char('+'), QLatin1Char('-'),
462                                                QLatin1Char(' '), QLatin1Char(','), QLatin1Char('.'),
463                                                arg.value.toDouble(), -1, QLocalePrivate::DFDecimal, -1, QLocale::OmitGroupSeparator);
464         break;
465     case QVariant::LongLong:
466         value = QLocalePrivate::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'),
467                                                  arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
468         break;
469     case QVariant::ULongLong:
470         value = QLocalePrivate::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'),
471                                                     arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator);
472         break;
473     default:
474         return QVariant();
475     }
476
477     QVarLengthArray<wchar_t, 64> out(64);
478
479     QString decimalSep;
480     QString thousandSep;
481     CURRENCYFMT format;
482     CURRENCYFMT *pformat = NULL;
483     if (!arg.symbol.isEmpty()) {
484         format.NumDigits = getLocaleInfo_int(lcid, LOCALE_ICURRDIGITS);
485         format.LeadingZero = getLocaleInfo_int(lcid, LOCALE_ILZERO);
486         decimalSep = getLocaleInfo(lcid, LOCALE_SMONDECIMALSEP);
487         format.lpDecimalSep = (wchar_t *)decimalSep.utf16();
488         thousandSep = getLocaleInfo(lcid, LOCALE_SMONTHOUSANDSEP);
489         format.lpThousandSep = (wchar_t *)thousandSep.utf16();
490         format.NegativeOrder = getLocaleInfo_int(lcid, LOCALE_INEGCURR);
491         format.PositiveOrder = getLocaleInfo_int(lcid, LOCALE_ICURRENCY);
492         format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
493
494         // grouping is complicated and ugly:
495         // int(0)  == "123456789.00"    == string("0")
496         // int(3)  == "123,456,789.00"  == string("3;0")
497         // int(30) == "123456,789.00"   == string("3;0;0")
498         // int(32) == "12,34,56,789.00" == string("3;2;0")
499         // int(320)== "1234,56,789.00"  == string("3;2")
500         QString groupingStr = getLocaleInfo(lcid, LOCALE_SMONGROUPING);
501         format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt();
502         if (format.Grouping % 10 == 0) // magic
503             format.Grouping /= 10;
504         else
505             format.Grouping *= 10;
506         pformat = &format;
507     }
508
509     int ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
510                                   pformat, out.data(), out.size());
511     if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
512         ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
513                                   pformat, out.data(), 0);
514         out.resize(ret);
515         ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
516                             pformat, out.data(), out.size());
517     }
518
519     value = QString::fromWCharArray(out.data());
520     if (substitution() == SAlways)
521         substituteDigits( value);
522     return value;
523 }
524
525 QVariant QSystemLocalePrivate::uiLanguages()
526 {
527     if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) {
528         typedef BOOL (WINAPI *GetUserPreferredUILanguagesFunc) (
529                     DWORD dwFlags,
530                     PULONG pulNumLanguages,
531                     PWSTR pwszLanguagesBuffer,
532                     PULONG pcchLanguagesBuffer);
533         static GetUserPreferredUILanguagesFunc GetUserPreferredUILanguages_ptr = 0;
534         if (!GetUserPreferredUILanguages_ptr) {
535             QSystemLibrary lib(QLatin1String("kernel32"));
536             if (lib.load())
537                 GetUserPreferredUILanguages_ptr = (GetUserPreferredUILanguagesFunc)lib.resolve("GetUserPreferredUILanguages");
538         }
539         if (GetUserPreferredUILanguages_ptr) {
540             unsigned long cnt = 0;
541             QVarLengthArray<wchar_t, 64> buf(64);
542             unsigned long size = buf.size();
543             if (!GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
544                 size = 0;
545                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
546                     GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) {
547                     buf.resize(size);
548                     if (!GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size))
549                         return QStringList();
550                 }
551             }
552             QStringList result;
553             result.reserve(cnt);
554             const wchar_t *str = buf.constData();
555             for (; cnt > 0; --cnt) {
556                 QString s = QString::fromWCharArray(str);
557                 if (s.isEmpty())
558                     break; // something is wrong
559                 result.append(s);
560                 str += s.size()+1;
561             }
562             return result;
563         }
564     }
565
566     // old Windows before Vista
567     return QStringList(QString::fromLatin1(winLangCodeToIsoName(GetUserDefaultUILanguage())));
568 }
569
570 QVariant QSystemLocalePrivate::nativeLanguageName()
571 {
572     if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
573         return getLocaleInfo(LOCALE_SNATIVELANGNAME);
574     return getLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
575 }
576
577 QVariant QSystemLocalePrivate::nativeCountryName()
578 {
579     if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
580         return getLocaleInfo(LOCALE_SNATIVECTRYNAME);
581     return getLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
582 }
583
584
585 void QSystemLocalePrivate::update()
586 {
587     lcid = GetUserDefaultLCID();
588     substitutionType = SUnknown;
589     zero = QChar();
590 }
591
592 QString QSystemLocalePrivate::winToQtFormat(const QString &sys_fmt)
593 {
594     QString result;
595     int i = 0;
596
597     while (i < sys_fmt.size()) {
598         if (sys_fmt.at(i).unicode() == QLatin1Char('\'')) {
599             QString text = qt_readEscapedFormatString(sys_fmt, &i);
600             if (text == QLatin1String("'"))
601                 result += QLatin1String("''");
602             else
603                 result += QString(QLatin1Char('\'') + text + QLatin1Char('\''));
604             continue;
605         }
606
607         QChar c = sys_fmt.at(i);
608         int repeat = qt_repeatCount(sys_fmt, i);
609
610         switch (c.unicode()) {
611             // Date
612             case 'y':
613                 if (repeat > 5)
614                     repeat = 5;
615                 else if (repeat == 3)
616                     repeat = 2;
617                 switch (repeat) {
618                     case 1:
619                         result += QLatin1String("yy"); // "y" unsupported by Qt, use "yy"
620                         break;
621                     case 5:
622                         result += QLatin1String("yyyy"); // "yyyyy" same as "yyyy" on Windows
623                         break;
624                     default:
625                         result += QString(repeat, QLatin1Char('y'));
626                         break;
627                 }
628                 break;
629             case 'g':
630                 if (repeat > 2)
631                     repeat = 2;
632                 switch (repeat) {
633                     case 2:
634                         break; // no equivalent of "gg" in Qt
635                     default:
636                         result += QLatin1Char('g');
637                         break;
638                 }
639                 break;
640             case 't':
641                 if (repeat > 2)
642                     repeat = 2;
643                 result += QLatin1String("AP"); // "t" unsupported, use "AP"
644                 break;
645             default:
646                 result += QString(repeat, c);
647                 break;
648         }
649
650         i += repeat;
651     }
652
653     return result;
654 }
655
656 QLocale QSystemLocale::fallbackLocale() const
657 {
658     return QLocale(QString::fromLatin1(getWinLocaleName()));
659 }
660
661 QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const
662 {
663     QSystemLocalePrivate *d = systemLocalePrivate();
664     switch(type) {
665     case DecimalPoint:
666         return d->decimalPoint();
667     case GroupSeparator:
668         return d->groupSeparator();
669     case NegativeSign:
670         return d->negativeSign();
671     case PositiveSign:
672         return d->positiveSign();
673     case DateFormatLong:
674         return d->dateFormat(QLocale::LongFormat);
675     case DateFormatShort:
676         return d->dateFormat(QLocale::ShortFormat);
677     case TimeFormatLong:
678         return d->timeFormat(QLocale::LongFormat);
679     case TimeFormatShort:
680         return d->timeFormat(QLocale::ShortFormat);
681     case DateTimeFormatLong:
682         return d->dateTimeFormat(QLocale::LongFormat);
683     case DateTimeFormatShort:
684         return d->dateTimeFormat(QLocale::ShortFormat);
685     case DayNameLong:
686         return d->dayName(in.toInt(), QLocale::LongFormat);
687     case DayNameShort:
688         return d->dayName(in.toInt(), QLocale::ShortFormat);
689     case MonthNameLong:
690         return d->monthName(in.toInt(), QLocale::LongFormat);
691     case MonthNameShort:
692         return d->monthName(in.toInt(), QLocale::ShortFormat);
693     case DateToStringShort:
694         return d->toString(in.toDate(), QLocale::ShortFormat);
695     case DateToStringLong:
696         return d->toString(in.toDate(), QLocale::LongFormat);
697     case TimeToStringShort:
698         return d->toString(in.toTime(), QLocale::ShortFormat);
699     case TimeToStringLong:
700         return d->toString(in.toTime(), QLocale::LongFormat);
701     case DateTimeToStringShort:
702         return d->toString(in.toDateTime(), QLocale::ShortFormat);
703     case DateTimeToStringLong:
704         return d->toString(in.toDateTime(), QLocale::LongFormat);
705     case ZeroDigit:
706         return d->zeroDigit();
707     case LanguageId:
708     case CountryId: {
709         QString locale = QString::fromLatin1(getWinLocaleName());
710         QLocale::Language lang;
711         QLocale::Script script;
712         QLocale::Country cntry;
713         QLocalePrivate::getLangAndCountry(locale, lang, script, cntry);
714         if (type == LanguageId)
715             return lang;
716         if (cntry == QLocale::AnyCountry)
717             return fallbackLocale().country();
718         return cntry;
719     }
720     case ScriptId:
721         return QVariant(QLocale::AnyScript);
722     case MeasurementSystem:
723         return d->measurementSystem();
724     case AMText:
725         return d->amText();
726     case PMText:
727         return d->pmText();
728     case FirstDayOfWeek:
729         return d->firstDayOfWeek();
730     case CurrencySymbol:
731         return d->currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
732     case CurrencyToString:
733         return d->toCurrencyString(in.value<QSystemLocale::CurrencyToStringArgument>());
734     case UILanguages:
735         return d->uiLanguages();
736     case LocaleChanged:
737         d->update();
738         break;
739     case NativeLanguageName:
740         return d->nativeLanguageName();
741     case NativeCountryName:
742         return d->nativeCountryName();
743     default:
744         break;
745     }
746     return QVariant();
747 }
748 #endif // QT_NO_SYSTEMLOCALE
749
750 struct WindowsToISOListElt {
751     ushort windows_code;
752     char iso_name[6];
753 };
754
755 /* NOTE: This array should be sorted by the first column! */
756 static const WindowsToISOListElt windows_to_iso_list[] = {
757     { 0x0401, "ar_SA" },
758     { 0x0402, "bg\0  " },
759     { 0x0403, "ca\0  " },
760     { 0x0404, "zh_TW" },
761     { 0x0405, "cs\0  " },
762     { 0x0406, "da\0  " },
763     { 0x0407, "de\0  " },
764     { 0x0408, "el\0  " },
765     { 0x0409, "en_US" },
766     { 0x040a, "es\0  " },
767     { 0x040b, "fi\0  " },
768     { 0x040c, "fr\0  " },
769     { 0x040d, "he\0  " },
770     { 0x040e, "hu\0  " },
771     { 0x040f, "is\0  " },
772     { 0x0410, "it\0  " },
773     { 0x0411, "ja\0  " },
774     { 0x0412, "ko\0  " },
775     { 0x0413, "nl\0  " },
776     { 0x0414, "no\0  " },
777     { 0x0415, "pl\0  " },
778     { 0x0416, "pt_BR" },
779     { 0x0418, "ro\0  " },
780     { 0x0419, "ru\0  " },
781     { 0x041a, "hr\0  " },
782     { 0x041c, "sq\0  " },
783     { 0x041d, "sv\0  " },
784     { 0x041e, "th\0  " },
785     { 0x041f, "tr\0  " },
786     { 0x0420, "ur\0  " },
787     { 0x0421, "in\0  " },
788     { 0x0422, "uk\0  " },
789     { 0x0423, "be\0  " },
790     { 0x0425, "et\0  " },
791     { 0x0426, "lv\0  " },
792     { 0x0427, "lt\0  " },
793     { 0x0429, "fa\0  " },
794     { 0x042a, "vi\0  " },
795     { 0x042d, "eu\0  " },
796     { 0x042f, "mk\0  " },
797     { 0x0436, "af\0  " },
798     { 0x0438, "fo\0  " },
799     { 0x0439, "hi\0  " },
800     { 0x043e, "ms\0  " },
801     { 0x0458, "mt\0  " },
802     { 0x0801, "ar_IQ" },
803     { 0x0804, "zh_CN" },
804     { 0x0807, "de_CH" },
805     { 0x0809, "en_GB" },
806     { 0x080a, "es_MX" },
807     { 0x080c, "fr_BE" },
808     { 0x0810, "it_CH" },
809     { 0x0812, "ko\0  " },
810     { 0x0813, "nl_BE" },
811     { 0x0814, "no\0  " },
812     { 0x0816, "pt\0  " },
813     { 0x081a, "sr\0  " },
814     { 0x081d, "sv_FI" },
815     { 0x0c01, "ar_EG" },
816     { 0x0c04, "zh_HK" },
817     { 0x0c07, "de_AT" },
818     { 0x0c09, "en_AU" },
819     { 0x0c0a, "es\0  " },
820     { 0x0c0c, "fr_CA" },
821     { 0x0c1a, "sr\0  " },
822     { 0x1001, "ar_LY" },
823     { 0x1004, "zh_SG" },
824     { 0x1007, "de_LU" },
825     { 0x1009, "en_CA" },
826     { 0x100a, "es_GT" },
827     { 0x100c, "fr_CH" },
828     { 0x1401, "ar_DZ" },
829     { 0x1407, "de_LI" },
830     { 0x1409, "en_NZ" },
831     { 0x140a, "es_CR" },
832     { 0x140c, "fr_LU" },
833     { 0x1801, "ar_MA" },
834     { 0x1809, "en_IE" },
835     { 0x180a, "es_PA" },
836     { 0x1c01, "ar_TN" },
837     { 0x1c09, "en_ZA" },
838     { 0x1c0a, "es_DO" },
839     { 0x2001, "ar_OM" },
840     { 0x2009, "en_JM" },
841     { 0x200a, "es_VE" },
842     { 0x2401, "ar_YE" },
843     { 0x2409, "en\0  " },
844     { 0x240a, "es_CO" },
845     { 0x2801, "ar_SY" },
846     { 0x2809, "en_BZ" },
847     { 0x280a, "es_PE" },
848     { 0x2c01, "ar_JO" },
849     { 0x2c09, "en_TT" },
850     { 0x2c0a, "es_AR" },
851     { 0x3001, "ar_LB" },
852     { 0x300a, "es_EC" },
853     { 0x3401, "ar_KW" },
854     { 0x340a, "es_CL" },
855     { 0x3801, "ar_AE" },
856     { 0x380a, "es_UY" },
857     { 0x3c01, "ar_BH" },
858     { 0x3c0a, "es_PY" },
859     { 0x4001, "ar_QA" },
860     { 0x400a, "es_BO" },
861     { 0x440a, "es_SV" },
862     { 0x480a, "es_HN" },
863     { 0x4c0a, "es_NI" },
864     { 0x500a, "es_PR" }
865 };
866
867 static const int windows_to_iso_count
868     = sizeof(windows_to_iso_list)/sizeof(WindowsToISOListElt);
869
870 static const char *winLangCodeToIsoName(int code)
871 {
872     int cmp = code - windows_to_iso_list[0].windows_code;
873     if (cmp < 0)
874         return 0;
875
876     if (cmp == 0)
877         return windows_to_iso_list[0].iso_name;
878
879     int begin = 0;
880     int end = windows_to_iso_count;
881
882     while (end - begin > 1) {
883         uint mid = (begin + end)/2;
884
885         const WindowsToISOListElt *elt = windows_to_iso_list + mid;
886         int cmp = code - elt->windows_code;
887         if (cmp < 0)
888             end = mid;
889         else if (cmp > 0)
890             begin = mid;
891         else
892             return elt->iso_name;
893     }
894
895     return 0;
896
897 }
898
899 static QString winIso639LangName(LCID id)
900 {
901     QString result;
902
903     // Windows returns the wrong ISO639 for some languages, we need to detect them here using
904     // the language code
905     QString lang_code;
906     wchar_t out[256];
907     if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255)) // ### shouldn't use them according to msdn
908         lang_code = QString::fromWCharArray(out);
909
910     if (!lang_code.isEmpty()) {
911         const char *endptr;
912         bool ok;
913         QByteArray latin1_lang_code = lang_code.toLatin1();
914         int i = qstrtoull(latin1_lang_code, &endptr, 16, &ok);
915         if (ok && *endptr == '\0') {
916             switch (i) {
917                 case 0x814:
918                     result = QLatin1String("nn"); // Nynorsk
919                     break;
920                 default:
921                     break;
922             }
923         }
924     }
925
926     if (!result.isEmpty())
927         return result;
928
929     // not one of the problematic languages - do the usual lookup
930     if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME , out, 255))
931         result = QString::fromWCharArray(out);
932
933     return result;
934 }
935
936 static QString winIso3116CtryName(LCID id)
937 {
938     QString result;
939
940     wchar_t out[256];
941     if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
942         result = QString::fromWCharArray(out);
943
944     return result;
945 }
946
947 static QByteArray getWinLocaleName(LCID id)
948 {
949     QByteArray result;
950     if (id == LOCALE_USER_DEFAULT) {
951         static QByteArray langEnvVar = qgetenv("LANG");
952         result = langEnvVar;
953         QString lang, script, cntry;
954         if ( result == "C" || (!result.isEmpty()
955                 && qt_splitLocaleName(QString::fromLocal8Bit(result), lang, script, cntry)) ) {
956             long id = 0;
957             bool ok = false;
958             id = qstrtoll(result.data(), 0, 0, &ok);
959             if ( !ok || id == 0 || id < INT_MIN || id > INT_MAX )
960                 return result;
961             else
962                 return winLangCodeToIsoName( (int)id );
963         }
964     }
965
966 #if defined(Q_OS_WINCE)
967     result = winLangCodeToIsoName(id != LOCALE_USER_DEFAULT ? id : GetUserDefaultLCID());
968 #else
969     if (id == LOCALE_USER_DEFAULT)
970         id = GetUserDefaultLCID();
971     QString resultuage = winIso639LangName(id);
972     QString country = winIso3116CtryName(id);
973     result = resultuage.toLatin1();
974     if (!country.isEmpty()) {
975         result += '_';
976         result += country.toLatin1();
977     }
978 #endif
979
980     return result;
981 }
982
983 Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
984 {
985     return QLocale(QString::fromLatin1(getWinLocaleName(id)));
986 }
987
988 QT_END_NAMESPACE