OLD | NEW |
| (Empty) |
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 "platform/text/LocaleWin.h" | |
33 | |
34 #include <limits> | |
35 #include "platform/DateComponents.h" | |
36 #include "platform/Language.h" | |
37 #include "platform/LayoutTestSupport.h" | |
38 #include "platform/text/DateTimeFormat.h" | |
39 #include "wtf/CurrentTime.h" | |
40 #include "wtf/DateMath.h" | |
41 #include "wtf/HashMap.h" | |
42 #include "wtf/OwnPtr.h" | |
43 #include "wtf/PassOwnPtr.h" | |
44 #include "wtf/text/StringBuffer.h" | |
45 #include "wtf/text/StringBuilder.h" | |
46 #include "wtf/text/StringHash.h" | |
47 | |
48 namespace blink { | |
49 | |
50 typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD); | |
51 typedef HashMap<String, LCID> NameToLCIDMap; | |
52 | |
53 static String extractLanguageCode(const String& locale) | |
54 { | |
55 size_t dashPosition = locale.find('-'); | |
56 if (dashPosition == kNotFound) | |
57 return locale; | |
58 return locale.left(dashPosition); | |
59 } | |
60 | |
61 static String removeLastComponent(const String& name) | |
62 { | |
63 size_t lastSeparator = name.reverseFind('-'); | |
64 if (lastSeparator == kNotFound) | |
65 return emptyString(); | |
66 return name.left(lastSeparator); | |
67 } | |
68 | |
69 static void ensureNameToLCIDMap(NameToLCIDMap& map) | |
70 { | |
71 if (!map.isEmpty()) | |
72 return; | |
73 // http://www.microsoft.com/resources/msdn/goglobal/default.mspx | |
74 // We add only locales used in layout tests for now. | |
75 map.add("ar", 0x0001); | |
76 map.add("ar-eg", 0x0C01); | |
77 map.add("de", 0x0007); | |
78 map.add("de-de", 0x0407); | |
79 map.add("el", 0x0008); | |
80 map.add("el-gr", 0x0408); | |
81 map.add("en", 0x0009); | |
82 map.add("en-gb", 0x0809); | |
83 map.add("en-us", 0x0409); | |
84 map.add("fr", 0x000C); | |
85 map.add("fr-fr", 0x040C); | |
86 map.add("he", 0x000D); | |
87 map.add("he-il", 0x040D); | |
88 map.add("hi", 0x0039); | |
89 map.add("hi-in", 0x0439); | |
90 map.add("ja", 0x0011); | |
91 map.add("ja-jp", 0x0411); | |
92 map.add("ko", 0x0012); | |
93 map.add("ko-kr", 0x0412); | |
94 map.add("ru", 0x0019); | |
95 map.add("ru-ru", 0x0419); | |
96 map.add("zh-cn", 0x0804); | |
97 map.add("zh-tw", 0x0404); | |
98 } | |
99 | |
100 // Fallback implementation of LocaleNameToLCID API. This is used for | |
101 // testing on Windows XP. | |
102 // FIXME: Remove this, ensureNameToLCIDMap, and removeLastComponent when we drop | |
103 // Windows XP support. | |
104 static LCID WINAPI convertLocaleNameToLCID(LPCWSTR name, DWORD) | |
105 { | |
106 if (!name || !name[0]) | |
107 return LOCALE_USER_DEFAULT; | |
108 DEFINE_STATIC_LOCAL(NameToLCIDMap, map, ()); | |
109 ensureNameToLCIDMap(map); | |
110 String localeName = String(name).replace('_', '-'); | |
111 localeName = localeName.lower(); | |
112 do { | |
113 NameToLCIDMap::const_iterator iterator = map.find(localeName); | |
114 if (iterator != map.end()) | |
115 return iterator->value; | |
116 localeName = removeLastComponent(localeName); | |
117 } while (!localeName.isEmpty()); | |
118 return LOCALE_USER_DEFAULT; | |
119 } | |
120 | |
121 static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefau
ltLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, const String& locale) | |
122 { | |
123 String localeLanguageCode = extractLanguageCode(locale); | |
124 if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode)) | |
125 return userDefaultLCID; | |
126 return localeNameToLCID(locale.charactersWithNullTermination().data(), 0); | |
127 } | |
128 | |
129 static LCID LCIDFromLocale(const String& locale, bool defaultsForLocale) | |
130 { | |
131 // LocaleNameToLCID() is available since Windows Vista. | |
132 LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>
(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID")); | |
133 if (!localeNameToLCID) | |
134 localeNameToLCID = convertLocaleNameToLCID; | |
135 | |
136 // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME. | |
137 const size_t languageCodeBufferSize = 9; | |
138 WCHAR lowercaseLanguageCode[languageCodeBufferSize]; | |
139 ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME | (defaultsForLo
cale ? LOCALE_NOUSEROVERRIDE : 0), lowercaseLanguageCode, languageCodeBufferSize
); | |
140 String userDefaultLanguageCode = String(lowercaseLanguageCode); | |
141 | |
142 LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageC
ode, localeNameToLCID, locale); | |
143 if (!lcid) | |
144 lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCo
de, localeNameToLCID, defaultLanguage()); | |
145 return lcid; | |
146 } | |
147 | |
148 PassOwnPtr<Locale> Locale::create(const String& locale) | |
149 { | |
150 // Whether the default settings for the locale should be used, ignoring user
overrides. | |
151 bool defaultsForLocale = LayoutTestSupport::isRunningLayoutTest(); | |
152 return LocaleWin::create(LCIDFromLocale(locale, defaultsForLocale), defaults
ForLocale); | |
153 } | |
154 | |
155 inline LocaleWin::LocaleWin(LCID lcid, bool defaultsForLocale) | |
156 : m_lcid(lcid) | |
157 , m_didInitializeNumberData(false) | |
158 , m_defaultsForLocale(defaultsForLocale) | |
159 { | |
160 DWORD value = 0; | |
161 getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK | (defaultsForLocale ? LOCALE_NOUSEROVE
RRIDE : 0), value); | |
162 // 0:Monday, ..., 6:Sunday. | |
163 // We need 1 for Monday, 0 for Sunday. | |
164 m_firstDayOfWeek = (value + 1) % 7; | |
165 } | |
166 | |
167 PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid, bool defaultsForLocale) | |
168 { | |
169 return adoptPtr(new LocaleWin(lcid, defaultsForLocale)); | |
170 } | |
171 | |
172 LocaleWin::~LocaleWin() | |
173 { | |
174 } | |
175 | |
176 String LocaleWin::getLocaleInfoString(LCTYPE type) | |
177 { | |
178 int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale
? LOCALE_NOUSEROVERRIDE : 0), 0, 0); | |
179 if (bufferSizeWithNUL <= 0) | |
180 return String(); | |
181 StringBuffer<UChar> buffer(bufferSizeWithNUL); | |
182 ::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale ? LOCALE_NOUSEROVERRIDE
: 0), buffer.characters(), bufferSizeWithNUL); | |
183 buffer.shrink(bufferSizeWithNUL - 1); | |
184 return String::adopt(buffer); | |
185 } | |
186 | |
187 void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result) | |
188 { | |
189 ::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR
>(&result), sizeof(DWORD) / sizeof(TCHAR)); | |
190 } | |
191 | |
192 void LocaleWin::ensureShortMonthLabels() | |
193 { | |
194 if (!m_shortMonthLabels.isEmpty()) | |
195 return; | |
196 const LCTYPE types[12] = { | |
197 LOCALE_SABBREVMONTHNAME1, | |
198 LOCALE_SABBREVMONTHNAME2, | |
199 LOCALE_SABBREVMONTHNAME3, | |
200 LOCALE_SABBREVMONTHNAME4, | |
201 LOCALE_SABBREVMONTHNAME5, | |
202 LOCALE_SABBREVMONTHNAME6, | |
203 LOCALE_SABBREVMONTHNAME7, | |
204 LOCALE_SABBREVMONTHNAME8, | |
205 LOCALE_SABBREVMONTHNAME9, | |
206 LOCALE_SABBREVMONTHNAME10, | |
207 LOCALE_SABBREVMONTHNAME11, | |
208 LOCALE_SABBREVMONTHNAME12, | |
209 }; | |
210 m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types)); | |
211 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) { | |
212 m_shortMonthLabels.append(getLocaleInfoString(types[i])); | |
213 if (m_shortMonthLabels.last().isEmpty()) { | |
214 m_shortMonthLabels.shrink(0); | |
215 m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName))
; | |
216 for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m) | |
217 m_shortMonthLabels.append(WTF::monthName[m]); | |
218 return; | |
219 } | |
220 } | |
221 } | |
222 | |
223 // -------------------------------- Tokenized date format | |
224 | |
225 static unsigned countContinuousLetters(const String& format, unsigned index) | |
226 { | |
227 unsigned count = 1; | |
228 UChar reference = format[index]; | |
229 while (index + 1 < format.length()) { | |
230 if (format[++index] != reference) | |
231 break; | |
232 ++count; | |
233 } | |
234 return count; | |
235 } | |
236 | |
237 static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& conv
erted) | |
238 { | |
239 if (literalBuffer.length() <= 0) | |
240 return; | |
241 DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted); | |
242 literalBuffer.clear(); | |
243 } | |
244 | |
245 // This function converts Windows date/time pattern format [1][2] into LDML date | |
246 // format pattern [3]. | |
247 // | |
248 // i.e. | |
249 // We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of | |
250 // Windows and LDML. | |
251 // We need to convert the following patterns: | |
252 // t -> a | |
253 // tt -> a | |
254 // ddd -> EEE | |
255 // dddd -> EEEE | |
256 // g -> G | |
257 // gg -> ignore | |
258 // | |
259 // [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx | |
260 // [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx | |
261 // [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns | |
262 static String convertWindowsDateTimeFormat(const String& format) | |
263 { | |
264 StringBuilder converted; | |
265 StringBuilder literalBuffer; | |
266 bool inQuote = false; | |
267 bool lastQuoteCanBeLiteral = false; | |
268 for (unsigned i = 0; i < format.length(); ++i) { | |
269 UChar ch = format[i]; | |
270 if (inQuote) { | |
271 if (ch == '\'') { | |
272 inQuote = false; | |
273 ASSERT(i); | |
274 if (lastQuoteCanBeLiteral && format[i - 1] == '\'') { | |
275 literalBuffer.append('\''); | |
276 lastQuoteCanBeLiteral = false; | |
277 } else { | |
278 lastQuoteCanBeLiteral = true; | |
279 } | |
280 } else { | |
281 literalBuffer.append(ch); | |
282 } | |
283 continue; | |
284 } | |
285 | |
286 if (ch == '\'') { | |
287 inQuote = true; | |
288 if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') { | |
289 literalBuffer.append(ch); | |
290 lastQuoteCanBeLiteral = false; | |
291 } else { | |
292 lastQuoteCanBeLiteral = true; | |
293 } | |
294 } else if (isASCIIAlpha(ch)) { | |
295 commitLiteralToken(literalBuffer, converted); | |
296 unsigned symbolStart = i; | |
297 unsigned count = countContinuousLetters(format, i); | |
298 i += count - 1; | |
299 if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' ||
ch == 'y') { | |
300 converted.append(format, symbolStart, count); | |
301 } else if (ch == 'd') { | |
302 if (count <= 2) | |
303 converted.append(format, symbolStart, count); | |
304 else if (count == 3) | |
305 converted.appendLiteral("EEE"); | |
306 else | |
307 converted.appendLiteral("EEEE"); | |
308 } else if (ch == 'g') { | |
309 if (count == 1) { | |
310 converted.append('G'); | |
311 } else { | |
312 // gg means imperial era in Windows. | |
313 // Just ignore it. | |
314 } | |
315 } else if (ch == 't') { | |
316 converted.append('a'); | |
317 } else { | |
318 literalBuffer.append(format, symbolStart, count); | |
319 } | |
320 } else { | |
321 literalBuffer.append(ch); | |
322 } | |
323 } | |
324 commitLiteralToken(literalBuffer, converted); | |
325 return converted.toString(); | |
326 } | |
327 | |
328 void LocaleWin::ensureMonthLabels() | |
329 { | |
330 if (!m_monthLabels.isEmpty()) | |
331 return; | |
332 const LCTYPE types[12] = { | |
333 LOCALE_SMONTHNAME1, | |
334 LOCALE_SMONTHNAME2, | |
335 LOCALE_SMONTHNAME3, | |
336 LOCALE_SMONTHNAME4, | |
337 LOCALE_SMONTHNAME5, | |
338 LOCALE_SMONTHNAME6, | |
339 LOCALE_SMONTHNAME7, | |
340 LOCALE_SMONTHNAME8, | |
341 LOCALE_SMONTHNAME9, | |
342 LOCALE_SMONTHNAME10, | |
343 LOCALE_SMONTHNAME11, | |
344 LOCALE_SMONTHNAME12, | |
345 }; | |
346 m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types)); | |
347 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) { | |
348 m_monthLabels.append(getLocaleInfoString(types[i])); | |
349 if (m_monthLabels.last().isEmpty()) { | |
350 m_monthLabels.shrink(0); | |
351 m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName)); | |
352 for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m) | |
353 m_monthLabels.append(WTF::monthFullName[m]); | |
354 return; | |
355 } | |
356 } | |
357 } | |
358 | |
359 void LocaleWin::ensureWeekDayShortLabels() | |
360 { | |
361 if (!m_weekDayShortLabels.isEmpty()) | |
362 return; | |
363 const LCTYPE types[7] = { | |
364 LOCALE_SABBREVDAYNAME7, // Sunday | |
365 LOCALE_SABBREVDAYNAME1, // Monday | |
366 LOCALE_SABBREVDAYNAME2, | |
367 LOCALE_SABBREVDAYNAME3, | |
368 LOCALE_SABBREVDAYNAME4, | |
369 LOCALE_SABBREVDAYNAME5, | |
370 LOCALE_SABBREVDAYNAME6 | |
371 }; | |
372 m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types)); | |
373 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) { | |
374 m_weekDayShortLabels.append(getLocaleInfoString(types[i])); | |
375 if (m_weekDayShortLabels.last().isEmpty()) { | |
376 m_weekDayShortLabels.shrink(0); | |
377 m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayNa
me)); | |
378 for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) { | |
379 // weekdayName starts with Monday. | |
380 m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]); | |
381 } | |
382 return; | |
383 } | |
384 } | |
385 } | |
386 | |
387 const Vector<String>& LocaleWin::monthLabels() | |
388 { | |
389 ensureMonthLabels(); | |
390 return m_monthLabels; | |
391 } | |
392 | |
393 const Vector<String>& LocaleWin::weekDayShortLabels() | |
394 { | |
395 ensureWeekDayShortLabels(); | |
396 return m_weekDayShortLabels; | |
397 } | |
398 | |
399 unsigned LocaleWin::firstDayOfWeek() | |
400 { | |
401 return m_firstDayOfWeek; | |
402 } | |
403 | |
404 bool LocaleWin::isRTL() | |
405 { | |
406 WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]); | |
407 return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftA
rabic; | |
408 } | |
409 | |
410 String LocaleWin::dateFormat() | |
411 { | |
412 if (m_dateFormat.isNull()) | |
413 m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_S
SHORTDATE)); | |
414 return m_dateFormat; | |
415 } | |
416 | |
417 String LocaleWin::dateFormat(const String& windowsFormat) | |
418 { | |
419 return convertWindowsDateTimeFormat(windowsFormat); | |
420 } | |
421 | |
422 String LocaleWin::monthFormat() | |
423 { | |
424 if (m_monthFormat.isNull()) | |
425 m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_
SYEARMONTH)); | |
426 return m_monthFormat; | |
427 } | |
428 | |
429 String LocaleWin::shortMonthFormat() | |
430 { | |
431 if (m_shortMonthFormat.isNull()) | |
432 m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LO
CALE_SYEARMONTH)).replace("MMMM", "MMM"); | |
433 return m_shortMonthFormat; | |
434 } | |
435 | |
436 String LocaleWin::timeFormat() | |
437 { | |
438 if (m_timeFormatWithSeconds.isNull()) | |
439 m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoStri
ng(LOCALE_STIMEFORMAT)); | |
440 return m_timeFormatWithSeconds; | |
441 } | |
442 | |
443 String LocaleWin::shortTimeFormat() | |
444 { | |
445 if (!m_timeFormatWithoutSeconds.isNull()) | |
446 return m_timeFormatWithoutSeconds; | |
447 String format = getLocaleInfoString(LOCALE_SSHORTTIME); | |
448 // Vista or older Windows doesn't support LOCALE_SSHORTTIME. | |
449 if (format.isEmpty()) { | |
450 format = getLocaleInfoString(LOCALE_STIMEFORMAT); | |
451 StringBuilder builder; | |
452 builder.append(getLocaleInfoString(LOCALE_STIME)); | |
453 builder.appendLiteral("ss"); | |
454 size_t pos = format.reverseFind(builder.toString()); | |
455 if (pos != kNotFound) | |
456 format.remove(pos, builder.length()); | |
457 } | |
458 m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format); | |
459 return m_timeFormatWithoutSeconds; | |
460 } | |
461 | |
462 String LocaleWin::dateTimeFormatWithSeconds() | |
463 { | |
464 if (!m_dateTimeFormatWithSeconds.isNull()) | |
465 return m_dateTimeFormatWithSeconds; | |
466 StringBuilder builder; | |
467 builder.append(dateFormat()); | |
468 builder.append(' '); | |
469 builder.append(timeFormat()); | |
470 m_dateTimeFormatWithSeconds = builder.toString(); | |
471 return m_dateTimeFormatWithSeconds; | |
472 } | |
473 | |
474 String LocaleWin::dateTimeFormatWithoutSeconds() | |
475 { | |
476 if (!m_dateTimeFormatWithoutSeconds.isNull()) | |
477 return m_dateTimeFormatWithoutSeconds; | |
478 StringBuilder builder; | |
479 builder.append(dateFormat()); | |
480 builder.append(' '); | |
481 builder.append(shortTimeFormat()); | |
482 m_dateTimeFormatWithoutSeconds = builder.toString(); | |
483 return m_dateTimeFormatWithoutSeconds; | |
484 } | |
485 | |
486 const Vector<String>& LocaleWin::shortMonthLabels() | |
487 { | |
488 ensureShortMonthLabels(); | |
489 return m_shortMonthLabels; | |
490 } | |
491 | |
492 const Vector<String>& LocaleWin::standAloneMonthLabels() | |
493 { | |
494 // Windows doesn't provide a way to get stand-alone month labels. | |
495 return monthLabels(); | |
496 } | |
497 | |
498 const Vector<String>& LocaleWin::shortStandAloneMonthLabels() | |
499 { | |
500 // Windows doesn't provide a way to get stand-alone month labels. | |
501 return shortMonthLabels(); | |
502 } | |
503 | |
504 const Vector<String>& LocaleWin::timeAMPMLabels() | |
505 { | |
506 if (m_timeAMPMLabels.isEmpty()) { | |
507 m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159)); | |
508 m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359)); | |
509 } | |
510 return m_timeAMPMLabels; | |
511 } | |
512 | |
513 void LocaleWin::initializeLocaleData() | |
514 { | |
515 if (m_didInitializeNumberData) | |
516 return; | |
517 | |
518 Vector<String, DecimalSymbolsSize> symbols; | |
519 enum DigitSubstitution { | |
520 DigitSubstitutionContext = 0, | |
521 DigitSubstitution0to9 = 1, | |
522 DigitSubstitutionNative = 2, | |
523 }; | |
524 DWORD digitSubstitution = DigitSubstitution0to9; | |
525 getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution); | |
526 if (digitSubstitution == DigitSubstitution0to9) { | |
527 symbols.append("0"); | |
528 symbols.append("1"); | |
529 symbols.append("2"); | |
530 symbols.append("3"); | |
531 symbols.append("4"); | |
532 symbols.append("5"); | |
533 symbols.append("6"); | |
534 symbols.append("7"); | |
535 symbols.append("8"); | |
536 symbols.append("9"); | |
537 } else { | |
538 String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS); | |
539 ASSERT(digits.length() >= 10); | |
540 for (unsigned i = 0; i < 10; ++i) | |
541 symbols.append(digits.substring(i, 1)); | |
542 } | |
543 ASSERT(symbols.size() == DecimalSeparatorIndex); | |
544 symbols.append(getLocaleInfoString(LOCALE_SDECIMAL)); | |
545 ASSERT(symbols.size() == GroupSeparatorIndex); | |
546 symbols.append(getLocaleInfoString(LOCALE_STHOUSAND)); | |
547 ASSERT(symbols.size() == DecimalSymbolsSize); | |
548 | |
549 String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN); | |
550 enum NegativeFormat { | |
551 NegativeFormatParenthesis = 0, | |
552 NegativeFormatSignPrefix = 1, | |
553 NegativeFormatSignSpacePrefix = 2, | |
554 NegativeFormatSignSuffix = 3, | |
555 NegativeFormatSpaceSignSuffix = 4, | |
556 }; | |
557 DWORD negativeFormat = NegativeFormatSignPrefix; | |
558 getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat); | |
559 String negativePrefix = emptyString(); | |
560 String negativeSuffix = emptyString(); | |
561 switch (negativeFormat) { | |
562 case NegativeFormatParenthesis: | |
563 negativePrefix = "("; | |
564 negativeSuffix = ")"; | |
565 break; | |
566 case NegativeFormatSignSpacePrefix: | |
567 negativePrefix = negativeSign + " "; | |
568 break; | |
569 case NegativeFormatSignSuffix: | |
570 negativeSuffix = negativeSign; | |
571 break; | |
572 case NegativeFormatSpaceSignSuffix: | |
573 negativeSuffix = " " + negativeSign; | |
574 break; | |
575 case NegativeFormatSignPrefix: // Fall through. | |
576 default: | |
577 negativePrefix = negativeSign; | |
578 break; | |
579 } | |
580 m_didInitializeNumberData = true; | |
581 setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativ
eSuffix); | |
582 } | |
583 | |
584 } | |
OLD | NEW |