Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / text / 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 "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& 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
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 | (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
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), defaultsForLocale);
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_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
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& 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
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::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
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::RightToLeftArabic;
408 }
409
410 String LocaleWin::dateFormat()
411 {
412     if (m_dateFormat.isNull())
413         m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
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(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
433     return m_shortMonthFormat;
434 }
435
436 String LocaleWin::timeFormat()
437 {
438     if (m_timeFormatWithSeconds.isNull())
439         m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(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, negativeSuffix);
582 }
583
584 }