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 48namespace blink { 49 50typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD); 51typedef HashMap<String, LCID> NameToLCIDMap; 52 53static 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 61static 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 69static 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. 104static 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 121static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, 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 129static 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 | (defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), lowercaseLanguageCode, languageCodeBufferSize); 140 String userDefaultLanguageCode = String(lowercaseLanguageCode); 141 142 LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, locale); 143 if (!lcid) 144 lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage()); 145 return lcid; 146} 147 148PassOwnPtr<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), defaultsForLocale); 153} 154 155inline 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_NOUSEROVERRIDE : 0), value); 162 // 0:Monday, ..., 6:Sunday. 163 // We need 1 for Monday, 0 for Sunday. 164 m_firstDayOfWeek = (value + 1) % 7; 165} 166 167PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid, bool defaultsForLocale) 168{ 169 return adoptPtr(new LocaleWin(lcid, defaultsForLocale)); 170} 171 172LocaleWin::~LocaleWin() 173{ 174} 175 176String 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 187void 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 192void 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 225static 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 237static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted) 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 262static 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 328void 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 359void 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::weekdayName)); 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 387const Vector<String>& LocaleWin::monthLabels() 388{ 389 ensureMonthLabels(); 390 return m_monthLabels; 391} 392 393const Vector<String>& LocaleWin::weekDayShortLabels() 394{ 395 ensureWeekDayShortLabels(); 396 return m_weekDayShortLabels; 397} 398 399unsigned LocaleWin::firstDayOfWeek() 400{ 401 return m_firstDayOfWeek; 402} 403 404bool LocaleWin::isRTL() 405{ 406 WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]); 407 return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic; 408} 409 410String LocaleWin::dateFormat() 411{ 412 if (m_dateFormat.isNull()) 413 m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE)); 414 return m_dateFormat; 415} 416 417String LocaleWin::dateFormat(const String& windowsFormat) 418{ 419 return convertWindowsDateTimeFormat(windowsFormat); 420} 421 422String LocaleWin::monthFormat() 423{ 424 if (m_monthFormat.isNull()) 425 m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)); 426 return m_monthFormat; 427} 428 429String LocaleWin::shortMonthFormat() 430{ 431 if (m_shortMonthFormat.isNull()) 432 m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM"); 433 return m_shortMonthFormat; 434} 435 436String LocaleWin::timeFormat() 437{ 438 if (m_timeFormatWithSeconds.isNull()) 439 m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT)); 440 return m_timeFormatWithSeconds; 441} 442 443String 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 462String 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 474String 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 486const Vector<String>& LocaleWin::shortMonthLabels() 487{ 488 ensureShortMonthLabels(); 489 return m_shortMonthLabels; 490} 491 492const Vector<String>& LocaleWin::standAloneMonthLabels() 493{ 494 // Windows doesn't provide a way to get stand-alone month labels. 495 return monthLabels(); 496} 497 498const Vector<String>& LocaleWin::shortStandAloneMonthLabels() 499{ 500 // Windows doesn't provide a way to get stand-alone month labels. 501 return shortMonthLabels(); 502} 503 504const 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 513void 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, negativeSuffix); 582} 583 584} 585