c658d02094f3af74efd2151a28ef84ba28d1994f
[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 using namespace std;
49
50 namespace blink {
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 = LayoutTestSupport::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 }