Home | History | Annotate | Download | only in win
      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 "core/platform/DateComponents.h"
     37 #include "core/platform/Language.h"
     38 #include "core/platform/LocalizedStrings.h"
     39 #include "core/platform/text/DateTimeFormat.h"
     40 #include "wtf/CurrentTime.h"
     41 #include "wtf/DateMath.h"
     42 #include "wtf/HashMap.h"
     43 #include "wtf/OwnPtr.h"
     44 #include "wtf/PassOwnPtr.h"
     45 #include "wtf/text/StringBuffer.h"
     46 #include "wtf/text/StringBuilder.h"
     47 #include "wtf/text/StringHash.h"
     48 
     49 using namespace std;
     50 
     51 namespace WebCore {
     52 
     53 typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD);
     54 typedef HashMap<String, LCID> NameToLCIDMap;
     55 
     56 static String extractLanguageCode(const String& locale)
     57 {
     58     size_t dashPosition = locale.find('-');
     59     if (dashPosition == notFound)
     60         return locale;
     61     return locale.left(dashPosition);
     62 }
     63 
     64 static String removeLastComponent(const String& name)
     65 {
     66     size_t lastSeparator = name.reverseFind('-');
     67     if (lastSeparator == notFound)
     68         return emptyString();
     69     return name.left(lastSeparator);
     70 }
     71 
     72 static void ensureNameToLCIDMap(NameToLCIDMap& map)
     73 {
     74     if (!map.isEmpty())
     75         return;
     76     // http://www.microsoft.com/resources/msdn/goglobal/default.mspx
     77     // We add only locales used in layout tests for now.
     78     map.add("ar", 0x0001);
     79     map.add("ar-eg", 0x0C01);
     80     map.add("de", 0x0007);
     81     map.add("de-de", 0x0407);
     82     map.add("el", 0x0008);
     83     map.add("el-gr", 0x0408);
     84     map.add("en", 0x0009);
     85     map.add("en-gb", 0x0809);
     86     map.add("en-us", 0x0409);
     87     map.add("fr", 0x000C);
     88     map.add("fr-fr", 0x040C);
     89     map.add("he", 0x000D);
     90     map.add("he-il", 0x040D);
     91     map.add("hi", 0x0039);
     92     map.add("hi-in", 0x0439);
     93     map.add("ja", 0x0011);
     94     map.add("ja-jp", 0x0411);
     95     map.add("ko", 0x0012);
     96     map.add("ko-kr", 0x0412);
     97     map.add("ru", 0x0019);
     98     map.add("ru-ru", 0x0419);
     99     map.add("zh-cn", 0x0804);
    100     map.add("zh-tw", 0x0404);
    101 }
    102 
    103 // Fallback implementation of LocaleNameToLCID API. This is used for
    104 // testing on Windows XP.
    105 // FIXME: Remove this, ensureNameToLCIDMap, and removeLastComponent when we drop
    106 // Windows XP support.
    107 static LCID WINAPI convertLocaleNameToLCID(LPCWSTR name, DWORD)
    108 {
    109     if (!name || !name[0])
    110         return LOCALE_USER_DEFAULT;
    111     DEFINE_STATIC_LOCAL(NameToLCIDMap, map, ());
    112     ensureNameToLCIDMap(map);
    113     String localeName = String(name).replace('_', '-');
    114     localeName.makeLower();
    115     do {
    116         NameToLCIDMap::const_iterator iterator = map.find(localeName);
    117         if (iterator != map.end())
    118             return iterator->value;
    119         localeName = removeLastComponent(localeName);
    120     } while (!localeName.isEmpty());
    121     return LOCALE_USER_DEFAULT;
    122 }
    123 
    124 static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, String& locale)
    125 {
    126     String localeLanguageCode = extractLanguageCode(locale);
    127     if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode))
    128         return userDefaultLCID;
    129     return localeNameToLCID(locale.charactersWithNullTermination().data(), 0);
    130 }
    131 
    132 static LCID LCIDFromLocale(const AtomicString& locale)
    133 {
    134     // LocaleNameToLCID() is available since Windows Vista.
    135     LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID"));
    136     if (!localeNameToLCID)
    137         localeNameToLCID = convertLocaleNameToLCID;
    138 
    139     // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
    140     const size_t languageCodeBufferSize = 9;
    141     WCHAR lowercaseLanguageCode[languageCodeBufferSize];
    142     ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lowercaseLanguageCode, languageCodeBufferSize);
    143     String userDefaultLanguageCode = String(lowercaseLanguageCode);
    144 
    145     LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, String(locale));
    146     if (!lcid)
    147         lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage());
    148     return lcid;
    149 }
    150 
    151 PassOwnPtr<Locale> Locale::create(const AtomicString& locale)
    152 {
    153     return LocaleWin::create(LCIDFromLocale(locale));
    154 }
    155 
    156 inline LocaleWin::LocaleWin(LCID lcid)
    157     : m_lcid(lcid)
    158     , m_didInitializeNumberData(false)
    159 {
    160 #if ENABLE(CALENDAR_PICKER)
    161     DWORD value = 0;
    162     getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, value);
    163     // 0:Monday, ..., 6:Sunday.
    164     // We need 1 for Monday, 0 for Sunday.
    165     m_firstDayOfWeek = (value + 1) % 7;
    166 #endif
    167 }
    168 
    169 PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid)
    170 {
    171     return adoptPtr(new LocaleWin(lcid));
    172 }
    173 
    174 LocaleWin::~LocaleWin()
    175 {
    176 }
    177 
    178 String LocaleWin::getLocaleInfoString(LCTYPE type)
    179 {
    180     int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type, 0, 0);
    181     if (bufferSizeWithNUL <= 0)
    182         return String();
    183     StringBuffer<UChar> buffer(bufferSizeWithNUL);
    184     ::GetLocaleInfo(m_lcid, type, 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             } else
    282                 literalBuffer.append(ch);
    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         } else if (isASCIIAlpha(ch)) {
    294             commitLiteralToken(literalBuffer, converted);
    295             unsigned symbolStart = i;
    296             unsigned count = countContinuousLetters(format, i);
    297             i += count - 1;
    298             if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y')
    299                 converted.append(format, symbolStart, count);
    300             else if (ch == 'd') {
    301                 if (count <= 2)
    302                     converted.append(format, symbolStart, count);
    303                 else if (count == 3)
    304                     converted.append("EEE");
    305                 else
    306                     converted.append("EEEE");
    307             } else if (ch == 'g') {
    308                 if (count == 1)
    309                     converted.append('G');
    310                 else {
    311                     // gg means imperial era in Windows.
    312                     // Just ignore it.
    313                 }
    314             } else if (ch == 't')
    315                 converted.append('a');
    316             else
    317                 literalBuffer.append(format, symbolStart, count);
    318         } else
    319             literalBuffer.append(ch);
    320     }
    321     commitLiteralToken(literalBuffer, converted);
    322     return converted.toString();
    323 }
    324 
    325 void LocaleWin::ensureMonthLabels()
    326 {
    327     if (!m_monthLabels.isEmpty())
    328         return;
    329     const LCTYPE types[12] = {
    330         LOCALE_SMONTHNAME1,
    331         LOCALE_SMONTHNAME2,
    332         LOCALE_SMONTHNAME3,
    333         LOCALE_SMONTHNAME4,
    334         LOCALE_SMONTHNAME5,
    335         LOCALE_SMONTHNAME6,
    336         LOCALE_SMONTHNAME7,
    337         LOCALE_SMONTHNAME8,
    338         LOCALE_SMONTHNAME9,
    339         LOCALE_SMONTHNAME10,
    340         LOCALE_SMONTHNAME11,
    341         LOCALE_SMONTHNAME12,
    342     };
    343     m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
    344     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
    345         m_monthLabels.append(getLocaleInfoString(types[i]));
    346         if (m_monthLabels.last().isEmpty()) {
    347             m_monthLabels.shrink(0);
    348             m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
    349             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
    350                 m_monthLabels.append(WTF::monthFullName[m]);
    351             return;
    352         }
    353     }
    354 }
    355 
    356 void LocaleWin::ensureWeekDayShortLabels()
    357 {
    358     if (!m_weekDayShortLabels.isEmpty())
    359         return;
    360     const LCTYPE types[7] = {
    361         LOCALE_SABBREVDAYNAME7, // Sunday
    362         LOCALE_SABBREVDAYNAME1, // Monday
    363         LOCALE_SABBREVDAYNAME2,
    364         LOCALE_SABBREVDAYNAME3,
    365         LOCALE_SABBREVDAYNAME4,
    366         LOCALE_SABBREVDAYNAME5,
    367         LOCALE_SABBREVDAYNAME6
    368     };
    369     m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
    370     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
    371         m_weekDayShortLabels.append(getLocaleInfoString(types[i]));
    372         if (m_weekDayShortLabels.last().isEmpty()) {
    373             m_weekDayShortLabels.shrink(0);
    374             m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayName));
    375             for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) {
    376                 // weekdayName starts with Monday.
    377                 m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]);
    378             }
    379             return;
    380         }
    381     }
    382 }
    383 
    384 const Vector<String>& LocaleWin::monthLabels()
    385 {
    386     ensureMonthLabels();
    387     return m_monthLabels;
    388 }
    389 
    390 #if ENABLE(CALENDAR_PICKER)
    391 const Vector<String>& LocaleWin::weekDayShortLabels()
    392 {
    393     ensureWeekDayShortLabels();
    394     return m_weekDayShortLabels;
    395 }
    396 
    397 unsigned LocaleWin::firstDayOfWeek()
    398 {
    399     return m_firstDayOfWeek;
    400 }
    401 
    402 bool LocaleWin::isRTL()
    403 {
    404     WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]);
    405     return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic;
    406 }
    407 #endif
    408 
    409 String LocaleWin::dateFormat()
    410 {
    411     if (m_dateFormat.isNull())
    412         m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
    413     return m_dateFormat;
    414 }
    415 
    416 String LocaleWin::dateFormat(const String& windowsFormat)
    417 {
    418     return convertWindowsDateTimeFormat(windowsFormat);
    419 }
    420 
    421 String LocaleWin::monthFormat()
    422 {
    423     if (m_monthFormat.isNull())
    424         m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
    425     return m_monthFormat;
    426 }
    427 
    428 String LocaleWin::shortMonthFormat()
    429 {
    430     if (m_shortMonthFormat.isNull())
    431         m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
    432     return m_shortMonthFormat;
    433 }
    434 
    435 String LocaleWin::timeFormat()
    436 {
    437     if (m_timeFormatWithSeconds.isNull())
    438         m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
    439     return m_timeFormatWithSeconds;
    440 }
    441 
    442 String LocaleWin::shortTimeFormat()
    443 {
    444     if (!m_timeFormatWithoutSeconds.isNull())
    445         return m_timeFormatWithoutSeconds;
    446     String format = getLocaleInfoString(LOCALE_SSHORTTIME);
    447     // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
    448     if (format.isEmpty()) {
    449         format = getLocaleInfoString(LOCALE_STIMEFORMAT);
    450         StringBuilder builder;
    451         builder.append(getLocaleInfoString(LOCALE_STIME));
    452         builder.append("ss");
    453         size_t pos = format.reverseFind(builder.toString());
    454         if (pos != notFound)
    455             format.remove(pos, builder.length());
    456     }
    457     m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
    458     return m_timeFormatWithoutSeconds;
    459 }
    460 
    461 String LocaleWin::dateTimeFormatWithSeconds()
    462 {
    463     if (!m_dateTimeFormatWithSeconds.isNull())
    464         return m_dateTimeFormatWithSeconds;
    465     StringBuilder builder;
    466     builder.append(dateFormat());
    467     builder.append(' ');
    468     builder.append(timeFormat());
    469     m_dateTimeFormatWithSeconds = builder.toString();
    470     return m_dateTimeFormatWithSeconds;
    471 }
    472 
    473 String LocaleWin::dateTimeFormatWithoutSeconds()
    474 {
    475     if (!m_dateTimeFormatWithoutSeconds.isNull())
    476         return m_dateTimeFormatWithoutSeconds;
    477     StringBuilder builder;
    478     builder.append(dateFormat());
    479     builder.append(' ');
    480     builder.append(shortTimeFormat());
    481     m_dateTimeFormatWithoutSeconds = builder.toString();
    482     return m_dateTimeFormatWithoutSeconds;
    483 }
    484 
    485 const Vector<String>& LocaleWin::shortMonthLabels()
    486 {
    487     ensureShortMonthLabels();
    488     return m_shortMonthLabels;
    489 }
    490 
    491 const Vector<String>& LocaleWin::standAloneMonthLabels()
    492 {
    493     // Windows doesn't provide a way to get stand-alone month labels.
    494     return monthLabels();
    495 }
    496 
    497 const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
    498 {
    499     // Windows doesn't provide a way to get stand-alone month labels.
    500     return shortMonthLabels();
    501 }
    502 
    503 const Vector<String>& LocaleWin::timeAMPMLabels()
    504 {
    505     if (m_timeAMPMLabels.isEmpty()) {
    506         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
    507         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
    508     }
    509     return m_timeAMPMLabels;
    510 }
    511 
    512 void LocaleWin::initializeLocaleData()
    513 {
    514     if (m_didInitializeNumberData)
    515         return;
    516 
    517     Vector<String, DecimalSymbolsSize> symbols;
    518     enum DigitSubstitution {
    519         DigitSubstitutionContext = 0,
    520         DigitSubstitution0to9 = 1,
    521         DigitSubstitutionNative = 2,
    522     };
    523     DWORD digitSubstitution = DigitSubstitution0to9;
    524     getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
    525     if (digitSubstitution == DigitSubstitution0to9) {
    526         symbols.append("0");
    527         symbols.append("1");
    528         symbols.append("2");
    529         symbols.append("3");
    530         symbols.append("4");
    531         symbols.append("5");
    532         symbols.append("6");
    533         symbols.append("7");
    534         symbols.append("8");
    535         symbols.append("9");
    536     } else {
    537         String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
    538         ASSERT(digits.length() >= 10);
    539         for (unsigned i = 0; i < 10; ++i)
    540             symbols.append(digits.substring(i, 1));
    541     }
    542     ASSERT(symbols.size() == DecimalSeparatorIndex);
    543     symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
    544     ASSERT(symbols.size() == GroupSeparatorIndex);
    545     symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
    546     ASSERT(symbols.size() == DecimalSymbolsSize);
    547 
    548     String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
    549     enum NegativeFormat {
    550         NegativeFormatParenthesis = 0,
    551         NegativeFormatSignPrefix = 1,
    552         NegativeFormatSignSpacePrefix = 2,
    553         NegativeFormatSignSuffix = 3,
    554         NegativeFormatSpaceSignSuffix = 4,
    555     };
    556     DWORD negativeFormat = NegativeFormatSignPrefix;
    557     getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
    558     String negativePrefix = emptyString();
    559     String negativeSuffix = emptyString();
    560     switch (negativeFormat) {
    561     case NegativeFormatParenthesis:
    562         negativePrefix = "(";
    563         negativeSuffix = ")";
    564         break;
    565     case NegativeFormatSignSpacePrefix:
    566         negativePrefix = negativeSign + " ";
    567         break;
    568     case NegativeFormatSignSuffix:
    569         negativeSuffix = negativeSign;
    570         break;
    571     case NegativeFormatSpaceSignSuffix:
    572         negativeSuffix = " " + negativeSign;
    573         break;
    574     case NegativeFormatSignPrefix: // Fall through.
    575     default:
    576         negativePrefix = negativeSign;
    577         break;
    578     }
    579     m_didInitializeNumberData = true;
    580     setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);
    581 }
    582 
    583 }
    584