| 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 |