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