65d1928b4770a7c6135a22ae55dcf9cb91f30b4b
[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 "Language.h"
37 #include "LocalizedStrings.h"
38 #include <limits>
39 #include <windows.h>
40 #include <wtf/CurrentTime.h>
41 #include <wtf/DateMath.h>
42 #include <wtf/HashMap.h>
43 #include <wtf/text/StringBuilder.h>
44 #include <wtf/text/StringHash.h>
45
46 using namespace std;
47
48 namespace WebCore {
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, String& locale)
61 {
62     if (equalIgnoringASCIICase(extractLanguageCode(locale), userDefaultLanguageCode))
63         return userDefaultLCID;
64     return LocaleNameToLCID(locale.charactersWithNullTermination().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 = String(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()
93 {
94 }
95
96 String LocaleWin::getLocaleInfoString(LCTYPE type)
97 {
98     int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type, 0, 0);
99     if (bufferSizeWithNUL <= 0)
100         return String();
101     Vector<UChar> buffer(bufferSizeWithNUL);
102     ::GetLocaleInfo(m_lcid, type, buffer.data(), bufferSizeWithNUL);
103     buffer.shrink(bufferSizeWithNUL - 1);
104     return String::adopt(buffer);
105 }
106
107 void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
108 {
109     ::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
110 }
111
112 void LocaleWin::ensureShortMonthLabels()
113 {
114 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
115     if (!m_shortMonthLabels.isEmpty())
116         return;
117     const LCTYPE types[12] = {
118         LOCALE_SABBREVMONTHNAME1,
119         LOCALE_SABBREVMONTHNAME2,
120         LOCALE_SABBREVMONTHNAME3,
121         LOCALE_SABBREVMONTHNAME4,
122         LOCALE_SABBREVMONTHNAME5,
123         LOCALE_SABBREVMONTHNAME6,
124         LOCALE_SABBREVMONTHNAME7,
125         LOCALE_SABBREVMONTHNAME8,
126         LOCALE_SABBREVMONTHNAME9,
127         LOCALE_SABBREVMONTHNAME10,
128         LOCALE_SABBREVMONTHNAME11,
129         LOCALE_SABBREVMONTHNAME12,
130     };
131     m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
132     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
133         m_shortMonthLabels.append(getLocaleInfoString(types[i]));
134         if (m_shortMonthLabels.last().isEmpty()) {
135             m_shortMonthLabels.shrink(0);
136             m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
137             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
138                 m_shortMonthLabels.append(WTF::monthName[m]);
139             return;
140         }
141     }
142 #endif
143 }
144
145 // -------------------------------- Tokenized date format
146
147 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
148 static unsigned countContinuousLetters(const String& format, unsigned index)
149 {
150     unsigned count = 1;
151     UChar reference = format[index];
152     while (index + 1 < format.length()) {
153         if (format[++index] != reference)
154             break;
155         ++count;
156     }
157     return count;
158 }
159
160 static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted)
161 {
162     if (literalBuffer.length() <= 0)
163         return;
164     DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted);
165     literalBuffer.clear();
166 }
167
168 // This function converts Windows date/time pattern format [1][2] into LDML date
169 // format pattern [3].
170 //
171 // i.e.
172 //   We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
173 //   Windows and LDML.
174 //   We need to convert the following patterns:
175 //     t -> a
176 //     tt -> a
177 //     ddd -> EEE
178 //     dddd -> EEEE
179 //     g -> G
180 //     gg -> ignore
181 //
182 // [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
183 // [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
184 // [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
185 static String convertWindowsDateTimeFormat(const String& format)
186 {
187     StringBuilder converted;
188     StringBuilder literalBuffer;
189     bool inQuote = false;
190     bool lastQuoteCanBeLiteral = false;
191     for (unsigned i = 0; i < format.length(); ++i) {
192         UChar ch = format[i];
193         if (inQuote) {
194             if (ch == '\'') {
195                 inQuote = false;
196                 ASSERT(i);
197                 if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
198                     literalBuffer.append('\'');
199                     lastQuoteCanBeLiteral = false;
200                 } else
201                     lastQuoteCanBeLiteral = true;
202             } else
203                 literalBuffer.append(ch);
204             continue;
205         }
206
207         if (ch == '\'') {
208             inQuote = true;
209             if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
210                 literalBuffer.append(ch);
211                 lastQuoteCanBeLiteral = false;
212             } else
213                 lastQuoteCanBeLiteral = true;
214         } else if (isASCIIAlpha(ch)) {
215             commitLiteralToken(literalBuffer, converted);
216             unsigned symbolStart = i;
217             unsigned count = countContinuousLetters(format, i);
218             i += count - 1;
219             if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y')
220                 converted.append(format, symbolStart, count);
221             else if (ch == 'd') {
222                 if (count <= 2)
223                     converted.append(format, symbolStart, count);
224                 else if (count == 3)
225                     converted.append("EEE");
226                 else
227                     converted.append("EEEE");
228             } else if (ch == 'g') {
229                 if (count == 1)
230                     converted.append('G');
231                 else {
232                     // gg means imperial era in Windows.
233                     // Just ignore it.
234                 }
235             } else if (ch == 't')
236                 converted.append('a');
237             else
238                 literalBuffer.append(format, symbolStart, count);
239         } else
240             literalBuffer.append(ch);
241     }
242     commitLiteralToken(literalBuffer, converted);
243     return converted.toString();
244 }
245 #endif
246
247 void LocaleWin::ensureMonthLabels()
248 {
249 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
250     if (!m_monthLabels.isEmpty())
251         return;
252     const LCTYPE types[12] = {
253         LOCALE_SMONTHNAME1,
254         LOCALE_SMONTHNAME2,
255         LOCALE_SMONTHNAME3,
256         LOCALE_SMONTHNAME4,
257         LOCALE_SMONTHNAME5,
258         LOCALE_SMONTHNAME6,
259         LOCALE_SMONTHNAME7,
260         LOCALE_SMONTHNAME8,
261         LOCALE_SMONTHNAME9,
262         LOCALE_SMONTHNAME10,
263         LOCALE_SMONTHNAME11,
264         LOCALE_SMONTHNAME12,
265     };
266     m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
267     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
268         m_monthLabels.append(getLocaleInfoString(types[i]));
269         if (m_monthLabels.last().isEmpty()) {
270             m_monthLabels.shrink(0);
271             m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
272             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
273                 m_monthLabels.append(WTF::monthFullName[m]);
274             return;
275         }
276     }
277 #endif
278 }
279
280 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
281 const Vector<String>& LocaleWin::monthLabels()
282 {
283     ensureMonthLabels();
284     return m_monthLabels;
285 }
286
287 String LocaleWin::dateFormat()
288 {
289     if (m_dateFormat.isNull())
290         m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
291     return m_dateFormat;
292 }
293
294 String LocaleWin::dateFormat(const String& windowsFormat)
295 {
296     return convertWindowsDateTimeFormat(windowsFormat);
297 }
298
299 String LocaleWin::monthFormat()
300 {
301     if (m_monthFormat.isNull())
302         m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
303     return m_monthFormat;
304 }
305
306 String LocaleWin::shortMonthFormat()
307 {
308     if (m_shortMonthFormat.isNull())
309         m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
310     return m_shortMonthFormat;
311 }
312
313 String LocaleWin::timeFormat()
314 {
315     if (m_timeFormatWithSeconds.isNull())
316         m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
317     return m_timeFormatWithSeconds;
318 }
319
320 String LocaleWin::shortTimeFormat()
321 {
322     if (!m_timeFormatWithoutSeconds.isNull())
323         return m_timeFormatWithoutSeconds;
324     String format = getLocaleInfoString(LOCALE_SSHORTTIME);
325     // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
326     if (format.isEmpty()) {
327         format = getLocaleInfoString(LOCALE_STIMEFORMAT);
328         StringBuilder builder;
329         builder.append(getLocaleInfoString(LOCALE_STIME));
330         builder.append("ss");
331         size_t pos = format.reverseFind(builder.toString());
332         if (pos != notFound)
333             format.remove(pos, builder.length());
334     }
335     m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
336     return m_timeFormatWithoutSeconds;
337 }
338
339 String LocaleWin::dateTimeFormatWithSeconds()
340 {
341     if (!m_dateTimeFormatWithSeconds.isNull())
342         return m_dateTimeFormatWithSeconds;
343     StringBuilder builder;
344     builder.append(dateFormat());
345     builder.append(' ');
346     builder.append(timeFormat());
347     m_dateTimeFormatWithSeconds = builder.toString();
348     return m_dateTimeFormatWithSeconds;
349 }
350
351 String LocaleWin::dateTimeFormatWithoutSeconds()
352 {
353     if (!m_dateTimeFormatWithoutSeconds.isNull())
354         return m_dateTimeFormatWithoutSeconds;
355     StringBuilder builder;
356     builder.append(dateFormat());
357     builder.append(' ');
358     builder.append(shortTimeFormat());
359     m_dateTimeFormatWithoutSeconds = builder.toString();
360     return m_dateTimeFormatWithoutSeconds;
361 }
362
363 const Vector<String>& LocaleWin::shortMonthLabels()
364 {
365     ensureShortMonthLabels();
366     return m_shortMonthLabels;
367 }
368
369 const Vector<String>& LocaleWin::standAloneMonthLabels()
370 {
371     // Windows doesn't provide a way to get stand-alone month labels.
372     return monthLabels();
373 }
374
375 const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
376 {
377     // Windows doesn't provide a way to get stand-alone month labels.
378     return shortMonthLabels();
379 }
380
381 const Vector<String>& LocaleWin::timeAMPMLabels()
382 {
383     if (m_timeAMPMLabels.isEmpty()) {
384         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
385         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
386     }
387     return m_timeAMPMLabels;
388 }
389 #endif
390
391 void LocaleWin::initializeLocaleData()
392 {
393     if (m_didInitializeNumberData)
394         return;
395
396     Vector<String, DecimalSymbolsSize> symbols;
397     enum DigitSubstitution {
398         DigitSubstitutionContext = 0,
399         DigitSubstitution0to9 = 1,
400         DigitSubstitutionNative = 2,
401     };
402     DWORD digitSubstitution = DigitSubstitution0to9;
403     getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
404     if (digitSubstitution == DigitSubstitution0to9) {
405         symbols.append("0");
406         symbols.append("1");
407         symbols.append("2");
408         symbols.append("3");
409         symbols.append("4");
410         symbols.append("5");
411         symbols.append("6");
412         symbols.append("7");
413         symbols.append("8");
414         symbols.append("9");
415     } else {
416         String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
417         ASSERT(digits.length() >= 10);
418         for (unsigned i = 0; i < 10; ++i)
419             symbols.append(digits.substring(i, 1));
420     }
421     ASSERT(symbols.size() == DecimalSeparatorIndex);
422     symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
423     ASSERT(symbols.size() == GroupSeparatorIndex);
424     symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
425     ASSERT(symbols.size() == DecimalSymbolsSize);
426
427     String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
428     enum NegativeFormat {
429         NegativeFormatParenthesis = 0,
430         NegativeFormatSignPrefix = 1,
431         NegativeFormatSignSpacePrefix = 2,
432         NegativeFormatSignSuffix = 3,
433         NegativeFormatSpaceSignSuffix = 4,
434     };
435     DWORD negativeFormat = NegativeFormatSignPrefix;
436     getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
437     String negativePrefix = emptyString();
438     String negativeSuffix = emptyString();
439     switch (negativeFormat) {
440     case NegativeFormatParenthesis:
441         negativePrefix = "(";
442         negativeSuffix = ")";
443         break;
444     case NegativeFormatSignSpacePrefix:
445         negativePrefix = negativeSign + " ";
446         break;
447     case NegativeFormatSignSuffix:
448         negativeSuffix = negativeSign;
449         break;
450     case NegativeFormatSpaceSignSuffix:
451         negativeSuffix = " " + negativeSign;
452         break;
453     case NegativeFormatSignPrefix:
454         FALLTHROUGH;
455     default:
456         negativePrefix = negativeSign;
457         break;
458     }
459     m_didInitializeNumberData = true;
460     setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);
461 }
462
463 }