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