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