2
* Copyright (C) 2012 Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are
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
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.
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.
32
#include "LocaleWin.h"
34
#include "DateComponents.h"
35
#include "DateTimeFormat.h"
37
#include "LocalizedStrings.h"
40
#include <wtf/CurrentTime.h>
41
#include <wtf/DateMath.h>
42
#include <wtf/OwnPtr.h>
43
#include <wtf/PassOwnPtr.h>
44
#include <wtf/text/StringBuilder.h>
50
typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD);
52
static String extractLanguageCode(const String& locale)
54
size_t dashPosition = locale.find('-');
55
if (dashPosition == notFound)
57
return locale.left(dashPosition);
60
static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, String& locale)
62
String localeLanguageCode = extractLanguageCode(locale);
63
if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode))
64
return userDefaultLCID;
65
return localeNameToLCID(locale.charactersWithNullTermination(), 0);
68
static LCID LCIDFromLocale(const AtomicString& locale)
70
// LocaleNameToLCID() is available since Windows Vista.
71
LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID"));
72
if (!localeNameToLCID)
73
return LOCALE_USER_DEFAULT;
75
// According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
76
const size_t languageCodeBufferSize = 9;
77
WCHAR lowercaseLanguageCode[languageCodeBufferSize];
78
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lowercaseLanguageCode, languageCodeBufferSize);
79
String userDefaultLanguageCode = String(lowercaseLanguageCode);
81
LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, String(locale));
83
lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage());
87
PassOwnPtr<Locale> Locale::create(const AtomicString& locale)
89
return LocaleWin::create(LCIDFromLocale(locale));
92
inline LocaleWin::LocaleWin(LCID lcid)
94
, m_didInitializeNumberData(false)
96
#if ENABLE(CALENDAR_PICKER)
98
getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, value);
99
// 0:Monday, ..., 6:Sunday.
100
// We need 1 for Monday, 0 for Sunday.
101
m_firstDayOfWeek = (value + 1) % 7;
105
PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid)
107
return adoptPtr(new LocaleWin(lcid));
110
LocaleWin::~LocaleWin()
114
String LocaleWin::getLocaleInfoString(LCTYPE type)
116
int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type, 0, 0);
117
if (bufferSizeWithNUL <= 0)
119
Vector<UChar> buffer(bufferSizeWithNUL);
120
::GetLocaleInfo(m_lcid, type, buffer.data(), bufferSizeWithNUL);
121
buffer.shrink(bufferSizeWithNUL - 1);
122
return String::adopt(buffer);
125
void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
127
::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
130
void LocaleWin::ensureShortMonthLabels()
132
if (!m_shortMonthLabels.isEmpty())
134
const LCTYPE types[12] = {
135
LOCALE_SABBREVMONTHNAME1,
136
LOCALE_SABBREVMONTHNAME2,
137
LOCALE_SABBREVMONTHNAME3,
138
LOCALE_SABBREVMONTHNAME4,
139
LOCALE_SABBREVMONTHNAME5,
140
LOCALE_SABBREVMONTHNAME6,
141
LOCALE_SABBREVMONTHNAME7,
142
LOCALE_SABBREVMONTHNAME8,
143
LOCALE_SABBREVMONTHNAME9,
144
LOCALE_SABBREVMONTHNAME10,
145
LOCALE_SABBREVMONTHNAME11,
146
LOCALE_SABBREVMONTHNAME12,
148
m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
149
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
150
m_shortMonthLabels.append(getLocaleInfoString(types[i]));
151
if (m_shortMonthLabels.last().isEmpty()) {
152
m_shortMonthLabels.shrink(0);
153
m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
154
for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
155
m_shortMonthLabels.append(WTF::monthName[m]);
161
// -------------------------------- Tokenized date format
163
static unsigned countContinuousLetters(const String& format, unsigned index)
166
UChar reference = format[index];
167
while (index + 1 < format.length()) {
168
if (format[++index] != reference)
175
static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted)
177
if (literalBuffer.length() <= 0)
179
DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted);
180
literalBuffer.clear();
183
// This function converts Windows date/time pattern format [1][2] into LDML date
184
// format pattern [3].
187
// We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
189
// We need to convert the following patterns:
197
// [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
198
// [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
199
// [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
200
static String convertWindowsDateTimeFormat(const String& format)
202
StringBuilder converted;
203
StringBuilder literalBuffer;
204
bool inQuote = false;
205
bool lastQuoteCanBeLiteral = false;
206
for (unsigned i = 0; i < format.length(); ++i) {
207
UChar ch = format[i];
212
if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
213
literalBuffer.append('\'');
214
lastQuoteCanBeLiteral = false;
216
lastQuoteCanBeLiteral = true;
218
literalBuffer.append(ch);
224
if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
225
literalBuffer.append(ch);
226
lastQuoteCanBeLiteral = false;
228
lastQuoteCanBeLiteral = true;
229
} else if (isASCIIAlpha(ch)) {
230
commitLiteralToken(literalBuffer, converted);
231
unsigned symbolStart = i;
232
unsigned count = countContinuousLetters(format, i);
234
if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y')
235
converted.append(format, symbolStart, count);
236
else if (ch == 'd') {
238
converted.append(format, symbolStart, count);
240
converted.append("EEE");
242
converted.append("EEEE");
243
} else if (ch == 'g') {
245
converted.append('G');
247
// gg means imperial era in Windows.
250
} else if (ch == 't')
251
converted.append('a');
253
literalBuffer.append(format, symbolStart, count);
255
literalBuffer.append(ch);
257
commitLiteralToken(literalBuffer, converted);
258
return converted.toString();
261
void LocaleWin::ensureMonthLabels()
263
if (!m_monthLabels.isEmpty())
265
const LCTYPE types[12] = {
279
m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
280
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
281
m_monthLabels.append(getLocaleInfoString(types[i]));
282
if (m_monthLabels.last().isEmpty()) {
283
m_monthLabels.shrink(0);
284
m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
285
for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
286
m_monthLabels.append(WTF::monthFullName[m]);
292
void LocaleWin::ensureWeekDayShortLabels()
294
if (!m_weekDayShortLabels.isEmpty())
296
const LCTYPE types[7] = {
297
LOCALE_SABBREVDAYNAME7, // Sunday
298
LOCALE_SABBREVDAYNAME1, // Monday
299
LOCALE_SABBREVDAYNAME2,
300
LOCALE_SABBREVDAYNAME3,
301
LOCALE_SABBREVDAYNAME4,
302
LOCALE_SABBREVDAYNAME5,
303
LOCALE_SABBREVDAYNAME6
305
m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
306
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
307
m_weekDayShortLabels.append(getLocaleInfoString(types[i]));
308
if (m_weekDayShortLabels.last().isEmpty()) {
309
m_weekDayShortLabels.shrink(0);
310
m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayName));
311
for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) {
312
// weekdayName starts with Monday.
313
m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]);
320
#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
321
const Vector<String>& LocaleWin::monthLabels()
324
return m_monthLabels;
328
#if ENABLE(CALENDAR_PICKER)
329
const Vector<String>& LocaleWin::weekDayShortLabels()
331
ensureWeekDayShortLabels();
332
return m_weekDayShortLabels;
335
unsigned LocaleWin::firstDayOfWeek()
337
return m_firstDayOfWeek;
340
bool LocaleWin::isRTL()
342
WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]);
343
return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic;
347
#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
348
String LocaleWin::dateFormat()
350
if (m_dateFormat.isNull())
351
m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
355
String LocaleWin::dateFormat(const String& windowsFormat)
357
return convertWindowsDateTimeFormat(windowsFormat);
360
String LocaleWin::monthFormat()
362
if (m_monthFormat.isNull())
363
m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
364
return m_monthFormat;
367
String LocaleWin::timeFormat()
369
if (m_timeFormatWithSeconds.isNull())
370
m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
371
return m_timeFormatWithSeconds;
374
String LocaleWin::shortTimeFormat()
376
if (!m_timeFormatWithoutSeconds.isNull())
377
return m_timeFormatWithoutSeconds;
378
String format = getLocaleInfoString(LOCALE_SSHORTTIME);
379
// Vista or older Windows doesn't support LOCALE_SSHORTTIME.
380
if (format.isEmpty()) {
381
format = getLocaleInfoString(LOCALE_STIMEFORMAT);
382
StringBuilder builder;
383
builder.append(getLocaleInfoString(LOCALE_STIME));
384
builder.append("ss");
385
size_t pos = format.reverseFind(builder.toString());
387
format.remove(pos, builder.length());
389
m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
390
return m_timeFormatWithoutSeconds;
393
String LocaleWin::dateTimeFormatWithSeconds()
395
if (!m_dateTimeFormatWithSeconds.isNull())
396
return m_dateTimeFormatWithSeconds;
397
StringBuilder builder;
398
builder.append(dateFormat());
400
builder.append(timeFormat());
401
m_dateTimeFormatWithSeconds = builder.toString();
402
return m_dateTimeFormatWithSeconds;
405
String LocaleWin::dateTimeFormatWithoutSeconds()
407
if (!m_dateTimeFormatWithoutSeconds.isNull())
408
return m_dateTimeFormatWithoutSeconds;
409
StringBuilder builder;
410
builder.append(dateFormat());
412
builder.append(shortTimeFormat());
413
m_dateTimeFormatWithoutSeconds = builder.toString();
414
return m_dateTimeFormatWithoutSeconds;
417
const Vector<String>& LocaleWin::shortMonthLabels()
419
ensureShortMonthLabels();
420
return m_shortMonthLabels;
423
const Vector<String>& LocaleWin::standAloneMonthLabels()
425
// Windows doesn't provide a way to get stand-alone month labels.
426
return monthLabels();
429
const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
431
// Windows doesn't provide a way to get stand-alone month labels.
432
return shortMonthLabels();
435
const Vector<String>& LocaleWin::timeAMPMLabels()
437
if (m_timeAMPMLabels.isEmpty()) {
438
m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
439
m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
441
return m_timeAMPMLabels;
445
void LocaleWin::initializeLocaleData()
447
if (m_didInitializeNumberData)
450
Vector<String, DecimalSymbolsSize> symbols;
451
enum DigitSubstitution {
452
DigitSubstitutionContext = 0,
453
DigitSubstitution0to9 = 1,
454
DigitSubstitutionNative = 2,
456
DWORD digitSubstitution = DigitSubstitution0to9;
457
getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
458
if (digitSubstitution == DigitSubstitution0to9) {
470
String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
471
ASSERT(digits.length() >= 10);
472
for (unsigned i = 0; i < 10; ++i)
473
symbols.append(digits.substring(i, 1));
475
ASSERT(symbols.size() == DecimalSeparatorIndex);
476
symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
477
ASSERT(symbols.size() == GroupSeparatorIndex);
478
symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
479
ASSERT(symbols.size() == DecimalSymbolsSize);
481
String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
482
enum NegativeFormat {
483
NegativeFormatParenthesis = 0,
484
NegativeFormatSignPrefix = 1,
485
NegativeFormatSignSpacePrefix = 2,
486
NegativeFormatSignSuffix = 3,
487
NegativeFormatSpaceSignSuffix = 4,
489
DWORD negativeFormat = NegativeFormatSignPrefix;
490
getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
491
String negativePrefix = emptyString();
492
String negativeSuffix = emptyString();
493
switch (negativeFormat) {
494
case NegativeFormatParenthesis:
495
negativePrefix = "(";
496
negativeSuffix = ")";
498
case NegativeFormatSignSpacePrefix:
499
negativePrefix = negativeSign + " ";
501
case NegativeFormatSignSuffix:
502
negativeSuffix = negativeSign;
504
case NegativeFormatSpaceSignSuffix:
505
negativeSuffix = " " + negativeSign;
507
case NegativeFormatSignPrefix: // Fall through.
509
negativePrefix = negativeSign;
512
m_didInitializeNumberData = true;
513
setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);