Unreviewed, rolling out r133712.
[WebKit-https.git] / Source / WebCore / platform / text / win / LocaleWin.cpp
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 "LocaleWin.h"
33
34 #include "DateComponents.h"
35 #include "DateTimeFormat.h"
36 #include "Language.h"
37 #include "LocalizedStrings.h"
38 #include <limits>
39 #include <windows.h>
40 #include <wtf/CurrentTime.h>
41 #include <wtf/DateMath.h>
42 #include <wtf/OwnPtr.h>
43 #include <wtf/PassOwnPtr.h>
44 #include <wtf/text/StringBuilder.h>
45
46 using namespace std;
47
48 namespace WebCore {
49
50 typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD);
51
52 static String extractLanguageCode(const String& locale)
53 {
54     size_t dashPosition = locale.find('-');
55     if (dashPosition == notFound)
56         return locale;
57     return locale.left(dashPosition);
58 }
59
60 static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, String& locale)
61 {
62     String localeLanguageCode = extractLanguageCode(locale);
63     if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode))
64         return userDefaultLCID;
65     return localeNameToLCID(locale.charactersWithNullTermination(), 0);
66 }
67
68 static LCID LCIDFromLocale(const AtomicString& locale)
69 {
70     // LocaleNameToLCID() is available since Windows Vista.
71     LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID"));
72     if (!localeNameToLCID)
73         return LOCALE_USER_DEFAULT;
74
75     // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
76     const size_t languageCodeBufferSize = 9;
77     WCHAR lowercaseLanguageCode[languageCodeBufferSize];
78     ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lowercaseLanguageCode, languageCodeBufferSize);
79     String userDefaultLanguageCode = String(lowercaseLanguageCode);
80
81     LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, String(locale));
82     if (!lcid)
83         lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage());
84     return lcid;
85 }
86
87 PassOwnPtr<Locale> Locale::create(const AtomicString& locale)
88 {
89     return LocaleWin::create(LCIDFromLocale(locale));
90 }
91
92 inline LocaleWin::LocaleWin(LCID lcid)
93     : m_lcid(lcid)
94     , m_didInitializeNumberData(false)
95 {
96 #if ENABLE(CALENDAR_PICKER)
97     DWORD value = 0;
98     getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, value);
99     // 0:Monday, ..., 6:Sunday.
100     // We need 1 for Monday, 0 for Sunday.
101     m_firstDayOfWeek = (value + 1) % 7;
102 #endif
103 }
104
105 PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid)
106 {
107     return adoptPtr(new LocaleWin(lcid));
108 }
109
110 LocaleWin::~LocaleWin()
111 {
112 }
113
114 String LocaleWin::getLocaleInfoString(LCTYPE type)
115 {
116     int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type, 0, 0);
117     if (bufferSizeWithNUL <= 0)
118         return String();
119     Vector<UChar> buffer(bufferSizeWithNUL);
120     ::GetLocaleInfo(m_lcid, type, buffer.data(), bufferSizeWithNUL);
121     buffer.shrink(bufferSizeWithNUL - 1);
122     return String::adopt(buffer);
123 }
124
125 void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
126 {
127     ::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
128 }
129
130 void LocaleWin::ensureShortMonthLabels()
131 {
132     if (!m_shortMonthLabels.isEmpty())
133         return;
134     const LCTYPE types[12] = {
135         LOCALE_SABBREVMONTHNAME1,
136         LOCALE_SABBREVMONTHNAME2,
137         LOCALE_SABBREVMONTHNAME3,
138         LOCALE_SABBREVMONTHNAME4,
139         LOCALE_SABBREVMONTHNAME5,
140         LOCALE_SABBREVMONTHNAME6,
141         LOCALE_SABBREVMONTHNAME7,
142         LOCALE_SABBREVMONTHNAME8,
143         LOCALE_SABBREVMONTHNAME9,
144         LOCALE_SABBREVMONTHNAME10,
145         LOCALE_SABBREVMONTHNAME11,
146         LOCALE_SABBREVMONTHNAME12,
147     };
148     m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
149     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
150         m_shortMonthLabels.append(getLocaleInfoString(types[i]));
151         if (m_shortMonthLabels.last().isEmpty()) {
152             m_shortMonthLabels.shrink(0);
153             m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
154             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
155                 m_shortMonthLabels.append(WTF::monthName[m]);
156             return;
157         }
158     }
159 }
160
161 // -------------------------------- Tokenized date format
162
163 struct DateFormatToken {
164     enum Type {
165         Literal,
166         Day1,
167         Day2,
168         Month1,
169         Month2,
170         Month3,
171         Month4,
172         Year1,
173         Year2,
174         Year4,
175     };
176     Type type;
177     String data; // This is valid only if type==Literal.
178
179     DateFormatToken(Type type)
180         : type(type)
181     { }
182
183     DateFormatToken(const String& data)
184         : type(Literal)
185         , data(data)
186     { }
187
188     DateFormatToken(const DateFormatToken& token)
189         : type(token.type)
190         , data(token.data)
191     { }
192 };
193
194 static inline bool isEraSymbol(UChar letter) { return letter == 'g'; }
195 static inline bool isYearSymbol(UChar letter) { return letter == 'y'; }
196 static inline bool isMonthSymbol(UChar letter) { return letter == 'M'; }
197 static inline bool isDaySymbol(UChar letter) { return letter == 'd'; }
198
199 static unsigned countContinuousLetters(const String& format, unsigned index)
200 {
201     unsigned count = 1;
202     UChar reference = format[index];
203     while (index + 1 < format.length()) {
204         if (format[++index] != reference)
205             break;
206         ++count;
207     }
208     return count;
209 }
210
211 static void commitLiteralToken(StringBuilder& literalBuffer, Vector<DateFormatToken>& tokens)
212 {
213     if (literalBuffer.length() <= 0)
214         return;
215     tokens.append(DateFormatToken(literalBuffer.toString()));
216     literalBuffer.clear();
217 }
218
219 // See http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
220 static Vector<DateFormatToken> parseDateFormat(const String format)
221 {
222     Vector<DateFormatToken> tokens;
223     StringBuilder literalBuffer;
224     bool inQuote = false;
225     bool lastQuoteCanBeLiteral = false;
226     for (unsigned i = 0; i < format.length(); ++i) {
227         UChar ch = format[i];
228         if (inQuote) {
229             if (ch == '\'') {
230                 inQuote = false;
231                 ASSERT(i);
232                 if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
233                     literalBuffer.append('\'');
234                     lastQuoteCanBeLiteral = false;
235                 } else
236                     lastQuoteCanBeLiteral = true;
237             } else
238                 literalBuffer.append(ch);
239             continue;
240         }
241
242         if (ch == '\'') {
243             inQuote = true;
244             if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
245                 literalBuffer.append(ch);
246                 lastQuoteCanBeLiteral = false;
247             } else
248                 lastQuoteCanBeLiteral = true;
249         } else if (isYearSymbol(ch)) {
250             commitLiteralToken(literalBuffer, tokens);
251             unsigned count = countContinuousLetters(format, i);
252             i += count - 1;
253             if (count == 1)
254                 tokens.append(DateFormatToken(DateFormatToken::Year1));
255             else if (count == 2)
256                 tokens.append(DateFormatToken(DateFormatToken::Year2));
257             else
258                 tokens.append(DateFormatToken(DateFormatToken::Year4));
259         } else if (isMonthSymbol(ch)) {
260             commitLiteralToken(literalBuffer, tokens);
261             unsigned count = countContinuousLetters(format, i);
262             i += count - 1;
263             if (count == 1)
264                 tokens.append(DateFormatToken(DateFormatToken::Month1));
265             else if (count == 2)
266                 tokens.append(DateFormatToken(DateFormatToken::Month2));
267             else if (count == 3)
268                 tokens.append(DateFormatToken(DateFormatToken::Month3));
269             else
270                 tokens.append(DateFormatToken(DateFormatToken::Month4));
271         } else if (isDaySymbol(ch)) {
272             commitLiteralToken(literalBuffer, tokens);
273             unsigned count = countContinuousLetters(format, i);
274             i += count - 1;
275             if (count == 1)
276                 tokens.append(DateFormatToken(DateFormatToken::Day1));
277             else
278                 tokens.append(DateFormatToken(DateFormatToken::Day2));
279         } else if (isEraSymbol(ch)) {
280             // Just ignore era.
281             // HTML5 date supports only A.D.
282         } else
283             literalBuffer.append(ch);
284     }
285     commitLiteralToken(literalBuffer, tokens);
286     return tokens;
287 }
288
289 void LocaleWin::ensureShortDateTokens()
290 {
291     if (!m_shortDateTokens.isEmpty())
292         return;
293     m_shortDateTokens = parseDateFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
294 }
295
296 void LocaleWin::ensureMonthLabels()
297 {
298     if (!m_monthLabels.isEmpty())
299         return;
300     const LCTYPE types[12] = {
301         LOCALE_SMONTHNAME1,
302         LOCALE_SMONTHNAME2,
303         LOCALE_SMONTHNAME3,
304         LOCALE_SMONTHNAME4,
305         LOCALE_SMONTHNAME5,
306         LOCALE_SMONTHNAME6,
307         LOCALE_SMONTHNAME7,
308         LOCALE_SMONTHNAME8,
309         LOCALE_SMONTHNAME9,
310         LOCALE_SMONTHNAME10,
311         LOCALE_SMONTHNAME11,
312         LOCALE_SMONTHNAME12,
313     };
314     m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
315     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
316         m_monthLabels.append(getLocaleInfoString(types[i]));
317         if (m_monthLabels.last().isEmpty()) {
318             m_monthLabels.shrink(0);
319             m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
320             for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
321                 m_monthLabels.append(WTF::monthFullName[m]);
322             return;
323         }
324     }
325 }
326
327 void LocaleWin::ensureWeekDayShortLabels()
328 {
329     if (!m_weekDayShortLabels.isEmpty())
330         return;
331     const LCTYPE types[7] = {
332         LOCALE_SABBREVDAYNAME7, // Sunday
333         LOCALE_SABBREVDAYNAME1, // Monday
334         LOCALE_SABBREVDAYNAME2,
335         LOCALE_SABBREVDAYNAME3,
336         LOCALE_SABBREVDAYNAME4,
337         LOCALE_SABBREVDAYNAME5,
338         LOCALE_SABBREVDAYNAME6
339     };
340     m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
341     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
342         m_weekDayShortLabels.append(getLocaleInfoString(types[i]));
343         if (m_weekDayShortLabels.last().isEmpty()) {
344             m_weekDayShortLabels.shrink(0);
345             m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayName));
346             for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) {
347                 // weekdayName starts with Monday.
348                 m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]);
349             }
350             return;
351         }
352     }
353 }
354
355 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
356 const Vector<String>& LocaleWin::monthLabels()
357 {
358     ensureMonthLabels();
359     return m_monthLabels;
360 }
361 #endif
362
363 #if ENABLE(CALENDAR_PICKER)
364 const Vector<String>& LocaleWin::weekDayShortLabels()
365 {
366     ensureWeekDayShortLabels();
367     return m_weekDayShortLabels;
368 }
369
370 unsigned LocaleWin::firstDayOfWeek()
371 {
372     return m_firstDayOfWeek;
373 }
374
375 bool LocaleWin::isRTL()
376 {
377     WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]);
378     return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic;
379 }
380 #endif
381
382 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
383 static String convertWindowsDateFormatToLDML(const Vector<DateFormatToken>& tokens)
384 {
385     StringBuilder buffer;
386     for (unsigned i = 0; i < tokens.size(); ++i) {
387         switch (tokens[i].type) {
388         case DateFormatToken::Literal:
389             DateTimeFormat::quoteAndAppendLiteral(tokens[i].data, buffer);
390             break;
391
392         case DateFormatToken::Day2:
393             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeDayOfMonth));
394             // Fallthrough.
395         case DateFormatToken::Day1:
396             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeDayOfMonth));
397             break;
398
399         case DateFormatToken::Month4:
400             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeMonth));
401             // Fallthrough.
402         case DateFormatToken::Month3:
403             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeMonth));
404             // Fallthrough.
405         case DateFormatToken::Month2:
406             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeMonth));
407             // Fallthrough.
408         case DateFormatToken::Month1:
409             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeMonth));
410             break;
411
412         case DateFormatToken::Year4:
413             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeYear));
414             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeYear));
415             // Fallthrough.
416         case DateFormatToken::Year2:
417             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeYear));
418             // Fallthrough.
419         case DateFormatToken::Year1:
420             buffer.append(static_cast<char>(DateTimeFormat::FieldTypeYear));
421             break;
422         }
423     }
424     return buffer.toString();
425 }
426
427 static DateTimeFormat::FieldType mapCharacterToDateTimeFieldType(UChar ch)
428 {
429     switch (ch) {
430     case 'h':
431         return DateTimeFormat::FieldTypeHour12;
432
433     case 'H':
434         return DateTimeFormat::FieldTypeHour23;
435
436     case 'm':
437         return DateTimeFormat::FieldTypeMinute;
438
439     case 's':
440         return DateTimeFormat::FieldTypeSecond;
441
442     case 't':
443         return DateTimeFormat::FieldTypePeriod;
444
445     default:
446         return DateTimeFormat::FieldTypeLiteral;
447     }
448 }
449
450 // This class used for converting Windows time pattern format[1] into LDML[2]
451 // time format string.
452 // [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd318148(v=vs.85).aspx
453 // [2] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
454 static String convertWindowsTimeFormatToLDML(const String& windowsTimeFormat)
455 {
456     StringBuilder builder;
457     int counter = 0;
458     DateTimeFormat::FieldType lastFieldType = DateTimeFormat::FieldTypeLiteral;
459     for (unsigned index = 0; index < windowsTimeFormat.length(); ++index) {
460         UChar const ch = windowsTimeFormat[index];
461         DateTimeFormat::FieldType fieldType = mapCharacterToDateTimeFieldType(ch);
462         if (fieldType == DateTimeFormat::FieldTypeLiteral)
463             builder.append(ch);
464         else if (fieldType == lastFieldType) {
465             ++counter;
466             if (counter == 2 && lastFieldType != DateTimeFormat::FieldTypePeriod)
467                 builder.append(static_cast<UChar>(lastFieldType));
468         } else {
469             if (lastFieldType != DateTimeFormat::FieldTypeLiteral)
470                 builder.append(static_cast<UChar>(lastFieldType));
471             builder.append(static_cast<UChar>(fieldType));
472             counter = 1;
473         }
474         lastFieldType = fieldType;
475     }
476     return builder.toString();
477 }
478
479 String LocaleWin::dateFormat()
480 {
481     if (!m_dateFormat.isNull())
482         return m_dateFormat;
483     ensureShortDateTokens();
484     m_dateFormat = convertWindowsDateFormatToLDML(m_shortDateTokens);
485     return m_dateFormat;
486 }
487
488 String LocaleWin::dateFormat(const String& windowsFormat)
489 {
490     return convertWindowsDateFormatToLDML(parseDateFormat(windowsFormat));
491 }
492
493 String LocaleWin::monthFormat()
494 {
495     if (!m_monthFormat.isNull())
496         return m_monthFormat;
497     m_monthFormat = convertWindowsDateFormatToLDML(parseDateFormat(getLocaleInfoString(LOCALE_SYEARMONTH)));
498     return m_monthFormat;
499 }
500
501 String LocaleWin::timeFormat()
502 {
503     if (m_timeFormatWithSeconds.isNull())
504         m_timeFormatWithSeconds = convertWindowsTimeFormatToLDML(getLocaleInfoString(LOCALE_STIMEFORMAT));
505     return m_timeFormatWithSeconds;
506 }
507
508 String LocaleWin::shortTimeFormat()
509 {
510     if (!m_timeFormatWithoutSeconds.isNull())
511         return m_timeFormatWithoutSeconds;
512     String format = getLocaleInfoString(LOCALE_SSHORTTIME);
513     // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
514     if (format.isEmpty()) {
515         format = timeFormat();
516         StringBuilder builder;
517         builder.append(getLocaleInfoString(LOCALE_STIME));
518         builder.append("ss");
519         size_t pos = format.reverseFind(builder.toString());
520         if (pos != notFound)
521             format.remove(pos, builder.length());
522     }
523     m_timeFormatWithoutSeconds = convertWindowsTimeFormatToLDML(format);
524     return m_timeFormatWithoutSeconds;
525 }
526
527 const Vector<String>& LocaleWin::shortMonthLabels()
528 {
529     ensureShortMonthLabels();
530     return m_shortMonthLabels;
531 }
532
533 const Vector<String>& LocaleWin::standAloneMonthLabels()
534 {
535     // Windows doesn't provide a way to get stand-alone month labels.
536     return monthLabels();
537 }
538
539 const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
540 {
541     // Windows doesn't provide a way to get stand-alone month labels.
542     return shortMonthLabels();
543 }
544
545 const Vector<String>& LocaleWin::timeAMPMLabels()
546 {
547     if (m_timeAMPMLabels.isEmpty()) {
548         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
549         m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
550     }
551     return m_timeAMPMLabels;
552 }
553 #endif
554
555 void LocaleWin::initializeLocaleData()
556 {
557     if (m_didInitializeNumberData)
558         return;
559
560     Vector<String, DecimalSymbolsSize> symbols;
561     enum DigitSubstitution {
562         DigitSubstitutionContext = 0,
563         DigitSubstitution0to9 = 1,
564         DigitSubstitutionNative = 2,
565     };
566     DWORD digitSubstitution = DigitSubstitution0to9;
567     getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
568     if (digitSubstitution == DigitSubstitution0to9) {
569         symbols.append("0");
570         symbols.append("1");
571         symbols.append("2");
572         symbols.append("3");
573         symbols.append("4");
574         symbols.append("5");
575         symbols.append("6");
576         symbols.append("7");
577         symbols.append("8");
578         symbols.append("9");
579     } else {
580         String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
581         ASSERT(digits.length() >= 10);
582         for (unsigned i = 0; i < 10; ++i)
583             symbols.append(digits.substring(i, 1));
584     }
585     ASSERT(symbols.size() == DecimalSeparatorIndex);
586     symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
587     ASSERT(symbols.size() == GroupSeparatorIndex);
588     symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
589     ASSERT(symbols.size() == DecimalSymbolsSize);
590
591     String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
592     enum NegativeFormat {
593         NegativeFormatParenthesis = 0,
594         NegativeFormatSignPrefix = 1,
595         NegativeFormatSignSpacePrefix = 2,
596         NegativeFormatSignSuffix = 3,
597         NegativeFormatSpaceSignSuffix = 4,
598     };
599     DWORD negativeFormat = NegativeFormatSignPrefix;
600     getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
601     String negativePrefix = emptyString();
602     String negativeSuffix = emptyString();
603     switch (negativeFormat) {
604     case NegativeFormatParenthesis:
605         negativePrefix = "(";
606         negativeSuffix = ")";
607         break;
608     case NegativeFormatSignSpacePrefix:
609         negativePrefix = negativeSign + " ";
610         break;
611     case NegativeFormatSignSuffix:
612         negativeSuffix = negativeSign;
613         break;
614     case NegativeFormatSpaceSignSuffix:
615         negativeSuffix = " " + negativeSign;
616         break;
617     case NegativeFormatSignPrefix: // Fall through.
618     default:
619         negativePrefix = negativeSign;
620         break;
621     }
622     m_didInitializeNumberData = true;
623     setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);
624 }
625
626 }