6a128cc920ff2d917e4f5fd72e33550674ba9aa8
[WebKit-https.git] / Source / WebCore / platform / text / win / LocaleWin.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "LocaleWin.h"
33
34 #include "DateComponents.h"
35 #include "DateTimeFormat.h"
36 #include "LocalizedStrings.h"
37 #include <limits>
38 #include <windows.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/DateMath.h>
41 #include <wtf/HashMap.h>
42 #include <wtf/Language.h>
43 #include <wtf/text/StringBuilder.h>
44 #include <wtf/text/StringHash.h>
45 #include <wtf/text/win/WCharStringExtras.h>
46
47 namespace WebCore {
48 using namespace std;
49
50 typedef HashMap<String, LCID, ASCIICaseInsensitiveHash> NameToLCIDMap;
51
52 static String extractLanguageCode(const String& locale)
53 {
54     size_t dashPosition = locale.find('-');
55     if (dashPosition == notFound)
56         return locale;
57     return locale.left(dashPosition);
58 }
59
60 static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, const String& locale)
61 {
62     if (equalIgnoringASCIICase(extractLanguageCode(locale), userDefaultLanguageCode))
63         return userDefaultLCID;
64     return LocaleNameToLCID(stringToNullTerminatedWChar(locale).data(), 0);
65 }
66
67 static LCID LCIDFromLocale(const AtomicString& locale)
68 {
69     // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
70     const size_t languageCodeBufferSize = 9;
71     WCHAR lowercaseLanguageCode[languageCodeBufferSize];
72     ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lowercaseLanguageCode, languageCodeBufferSize);
73     String userDefaultLanguageCode = nullTerminatedWCharToString(lowercaseLanguageCode);
74
75     LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, String(locale));
76     if (!lcid)
77         lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, defaultLanguage());
78     return lcid;
79 }
80
81 std::unique_ptr<Locale> Locale::create(const AtomicString& locale)
82 {
83     return std::make_unique<LocaleWin>(LCIDFromLocale(locale));
84 }
85
86 inline LocaleWin::LocaleWin(LCID lcid)
87     : m_lcid(lcid)
88     , m_didInitializeNumberData(false)
89 {
90 }
91
92 LocaleWin::~LocaleWin() = default;
93
94 String LocaleWin::getLocaleInfoString(LCTYPE type)
95 {
96     int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type, 0, 0);
97     if (bufferSizeWithNUL <= 0)
98         return String();
99     StringVector<UChar> buffer(bufferSizeWithNUL);
100     ::GetLocaleInfo(m_lcid, type, buffer.data(), bufferSizeWithNUL);
101     buffer.shrink(bufferSizeWithNUL - 1);
102     return String::adopt(WTFMove(buffer));
103 }
104
105 void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
106 {
107     ::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
108 }
109
110 void LocaleWin::ensureShortMonthLabels()
111 {
112 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
113     if (!m_shortMonthLabels.isEmpty())
114         return;
115     const LCTYPE types[12] = {
116         LOCALE_SABBREVMONTHNAME1,
117         LOCALE_SABBREVMONTHNAME2,
118         LOCALE_SABBREVMONTHNAME3,
119         LOCALE_SABBREVMONTHNAME4,
120         LOCALE_SABBREVMONTHNAME5,
121         LOCALE_SABBREVMONTHNAME6,
122         LOCALE_SABBREVMONTHNAME7,
123         LOCALE_SABBREVMONTHNAME8,
124         LOCALE_SABBREVMONTHNAME9,
125         LOCALE_SABBREVMONTHNAME10,
126         LOCALE_SABBREVMONTHNAME11,
127         LOCALE_SABBREVMONTHNAME12,
128     };
129     m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
130     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
131         m_shortMonthLabels.append(getLocaleInfoString(types[i]));
132         if (m_shortMonthLabels.last().isEmpty()) {
133             m_shortMonthLabels.shrink(0);
134             m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
135             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
136                 m_shortMonthLabels.append(WTF::monthName[m]);
137             return;
138         }
139     }
140 #endif
141 }
142
143 // -------------------------------- Tokenized date format
144
145 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
146 static unsigned countContinuousLetters(const String& format, unsigned index)
147 {
148     unsigned count = 1;
149     UChar reference = format[index];
150     while (index + 1 < format.length()) {
151         if (format[++index] != reference)
152             break;
153         ++count;
154     }
155     return count;
156 }
157
158 static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted)
159 {
160     if (literalBuffer.length() <= 0)
161         return;
162     DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted);
163     literalBuffer.clear();
164 }
165
166 // This function converts Windows date/time pattern format [1][2] into LDML date
167 // format pattern [3].
168 //
169 // i.e.
170 //   We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
171 //   Windows and LDML.
172 //   We need to convert the following patterns:
173 //     t -> a
174 //     tt -> a
175 //     ddd -> EEE
176 //     dddd -> EEEE
177 //     g -> G
178 //     gg -> ignore
179 //
180 // [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
181 // [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
182 // [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
183 static String convertWindowsDateTimeFormat(const String& format)
184 {
185     StringBuilder converted;
186     StringBuilder literalBuffer;
187     bool inQuote = false;
188     bool lastQuoteCanBeLiteral = false;
189     for (unsigned i = 0; i < format.length(); ++i) {
190         UChar ch = format[i];
191         if (inQuote) {
192             if (ch == '\'') {
193                 inQuote = false;
194                 ASSERT(i);
195                 if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
196                     literalBuffer.append('\'');
197                     lastQuoteCanBeLiteral = false;
198                 } else
199                     lastQuoteCanBeLiteral = true;
200             } else
201                 literalBuffer.append(ch);
202             continue;
203         }
204
205         if (ch == '\'') {
206             inQuote = true;
207             if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
208                 literalBuffer.append(ch);
209                 lastQuoteCanBeLiteral = false;
210             } else
211                 lastQuoteCanBeLiteral = true;
212         } else if (isASCIIAlpha(ch)) {
213             commitLiteralToken(literalBuffer, converted);
214             unsigned symbolStart = i;
215             unsigned count = countContinuousLetters(format, i);
216             i += count - 1;
217             if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y')
218                 converted.append(format, symbolStart, count);
219             else if (ch == 'd') {
220                 if (count <= 2)
221                     converted.append(format, symbolStart, count);
222                 else if (count == 3)
223                     converted.append("EEE");
224                 else
225                     converted.append("EEEE");
226             } else if (ch == 'g') {
227                 if (count == 1)
228                     converted.append('G');
229                 else {
230                     // gg means imperial era in Windows.
231                     // Just ignore it.
232                 }
233             } else if (ch == 't')
234                 converted.append('a');
235             else
236                 literalBuffer.append(format, symbolStart, count);
237         } else
238             literalBuffer.append(ch);
239     }
240     commitLiteralToken(literalBuffer, converted);
241     return converted.toString();
242 }
243 #endif
244
245 void LocaleWin::ensureMonthLabels()
246 {
247 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
248     if (!m_monthLabels.isEmpty())
249         return;
250     const LCTYPE types[12] = {
251         LOCALE_SMONTHNAME1,
252         LOCALE_SMONTHNAME2,
253         LOCALE_SMONTHNAME3,
254         LOCALE_SMONTHNAME4,
255         LOCALE_SMONTHNAME5,
256         LOCALE_SMONTHNAME6,
257         LOCALE_SMONTHNAME7,
258         LOCALE_SMONTHNAME8,
259         LOCALE_SMONTHNAME9,
260         LOCALE_SMONTHNAME10,
261         LOCALE_SMONTHNAME11,
262         LOCALE_SMONTHNAME12,
263     };
264     m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
265     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
266         m_monthLabels.append(getLocaleInfoString(types[i]));
267         if (m_monthLabels.last().isEmpty()) {
268             m_monthLabels.shrink(0);
269             m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
270             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
271                 m_monthLabels.append(WTF::monthFullName[m]);
272             return;
273         }
274     }
275 #endif
276 }
277
278 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
279 const Vector<String>& LocaleWin::monthLabels()
280 {
281     ensureMonthLabels();
282     return m_monthLabels;
283 }
284
285 String LocaleWin::dateFormat()
286 {
287     if (m_dateFormat.isNull())
288         m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
289     return m_dateFormat;
290 }
291
292 String LocaleWin::dateFormat(const String& windowsFormat)
293 {
294     return convertWindowsDateTimeFormat(windowsFormat);
295 }
296
297 String LocaleWin::monthFormat()
298 {
299     if (m_monthFormat.isNull())
300         m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
301     return m_monthFormat;
302 }
303
304 String LocaleWin::shortMonthFormat()
305 {
306     if (m_shortMonthFormat.isNull())
307         m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
308     return m_shortMonthFormat;
309 }
310
311 String LocaleWin::timeFormat()
312 {
313     if (m_timeFormatWithSeconds.isNull())
314         m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
315     return m_timeFormatWithSeconds;
316 }
317
318 String LocaleWin::shortTimeFormat()
319 {
320     if (!m_timeFormatWithoutSeconds.isNull())
321         return m_timeFormatWithoutSeconds;
322     String format = getLocaleInfoString(LOCALE_SSHORTTIME);
323     // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
324     if (format.isEmpty()) {
325         format = getLocaleInfoString(LOCALE_STIMEFORMAT);
326         StringBuilder builder;
327         builder.append(getLocaleInfoString(LOCALE_STIME));
328         builder.append("ss");
329         size_t pos = format.reverseFind(builder.toString());
330         if (pos != notFound)
331             format.remove(pos, builder.length());
332     }
333     m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
334     return m_timeFormatWithoutSeconds;
335 }
336
337 String LocaleWin::dateTimeFormatWithSeconds()
338 {
339     if (!m_dateTimeFormatWithSeconds.isNull())
340         return m_dateTimeFormatWithSeconds;
341     StringBuilder builder;
342     builder.append(dateFormat());
343     builder.append(' ');
344     builder.append(timeFormat());
345     m_dateTimeFormatWithSeconds = builder.toString();
346     return m_dateTimeFormatWithSeconds;
347 }
348
349 String LocaleWin::dateTimeFormatWithoutSeconds()
350 {
351     if (!m_dateTimeFormatWithoutSeconds.isNull())
352         return m_dateTimeFormatWithoutSeconds;
353     StringBuilder builder;
354     builder.append(dateFormat());
355     builder.append(' ');
356     builder.append(shortTimeFormat());
357     m_dateTimeFormatWithoutSeconds = builder.toString();
358     return m_dateTimeFormatWithoutSeconds;
359 }
360
361 const Vector<String>& LocaleWin::shortMonthLabels()
362 {
363     ensureShortMonthLabels();
364     return m_shortMonthLabels;
365 }
366
367 const Vector<String>& LocaleWin::standAloneMonthLabels()
368 {
369     // Windows doesn't provide a way to get stand-alone month labels.
370     return monthLabels();
371 }
372
373 const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
374 {
375     // Windows doesn't provide a way to get stand-alone month labels.
376     return shortMonthLabels();
377 }
378
379 const Vector<String>& LocaleWin::timeAMPMLabels()
380 {
381     if (m_timeAMPMLabels.isEmpty()) {
382         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
383         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
384     }
385     return m_timeAMPMLabels;
386 }
387 #endif
388
389 void LocaleWin::initializeLocaleData()
390 {
391     if (m_didInitializeNumberData)
392         return;
393
394     Vector<String, DecimalSymbolsSize> symbols;
395     enum DigitSubstitution {
396         DigitSubstitutionContext = 0,
397         DigitSubstitution0to9 = 1,
398         DigitSubstitutionNative = 2,
399     };
400     DWORD digitSubstitution = DigitSubstitution0to9;
401     getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
402     if (digitSubstitution == DigitSubstitution0to9) {
403         symbols.append("0");
404         symbols.append("1");
405         symbols.append("2");
406         symbols.append("3");
407         symbols.append("4");
408         symbols.append("5");
409         symbols.append("6");
410         symbols.append("7");
411         symbols.append("8");
412         symbols.append("9");
413     } else {
414         String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
415         ASSERT(digits.length() >= 10);
416         for (unsigned i = 0; i < 10; ++i)
417             symbols.append(digits.substring(i, 1));
418     }
419     ASSERT(symbols.size() == DecimalSeparatorIndex);
420     symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
421     ASSERT(symbols.size() == GroupSeparatorIndex);
422     symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
423     ASSERT(symbols.size() == DecimalSymbolsSize);
424
425     String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
426     enum NegativeFormat {
427         NegativeFormatParenthesis = 0,
428         NegativeFormatSignPrefix = 1,
429         NegativeFormatSignSpacePrefix = 2,
430         NegativeFormatSignSuffix = 3,
431         NegativeFormatSpaceSignSuffix = 4,
432     };
433     DWORD negativeFormat = NegativeFormatSignPrefix;
434     getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
435     String negativePrefix = emptyString();
436     String negativeSuffix = emptyString();
437     switch (negativeFormat) {
438     case NegativeFormatParenthesis:
439         negativePrefix = "(";
440         negativeSuffix = ")";
441         break;
442     case NegativeFormatSignSpacePrefix:
443         negativePrefix = negativeSign + " ";
444         break;
445     case NegativeFormatSignSuffix:
446         negativeSuffix = negativeSign;
447         break;
448     case NegativeFormatSpaceSignSuffix:
449         negativeSuffix = " " + negativeSign;
450         break;
451     case NegativeFormatSignPrefix:
452         FALLTHROUGH;
453     default:
454         negativePrefix = negativeSign;
455         break;
456     }
457     m_didInitializeNumberData = true;
458     setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);
459 }
460
461 }