2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "platform/text/LocaleMac.h"
34 #import <Foundation/NSDateFormatter.h>
35 #import <Foundation/NSLocale.h>
36 #include "platform/Language.h"
37 #include "wtf/DateMath.h"
38 #include "wtf/PassOwnPtr.h"
39 #include "wtf/RetainPtr.h"
40 #include "wtf/text/StringBuilder.h"
44 static inline String languageFromLocale(const String& locale)
46 String normalizedLocale = locale;
47 normalizedLocale.replace('-', '_');
48 size_t separatorPosition = normalizedLocale.find('_');
49 if (separatorPosition == kNotFound)
50 return normalizedLocale;
51 return normalizedLocale.left(separatorPosition);
54 static RetainPtr<NSLocale> determineLocale(const String& locale)
56 RetainPtr<NSLocale> currentLocale = [NSLocale currentLocale];
57 String currentLocaleLanguage = languageFromLocale(String([currentLocale.get() localeIdentifier]));
58 String localeLanguage = languageFromLocale(locale);
59 if (equalIgnoringCase(currentLocaleLanguage, localeLanguage))
61 // It seems initWithLocaleIdentifier accepts dash-separated locale identifier.
62 return RetainPtr<NSLocale>(AdoptNS, [[NSLocale alloc] initWithLocaleIdentifier:locale]);
65 PassOwnPtr<Locale> Locale::create(const String& locale)
67 return LocaleMac::create(determineLocale(locale).get());
70 static RetainPtr<NSDateFormatter> createDateTimeFormatter(NSLocale* locale, NSCalendar* calendar, NSDateFormatterStyle dateStyle, NSDateFormatterStyle timeStyle)
72 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
73 [formatter setLocale:locale];
74 [formatter setDateStyle:dateStyle];
75 [formatter setTimeStyle:timeStyle];
76 [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
77 [formatter setCalendar:calendar];
78 return adoptNS(formatter);
81 LocaleMac::LocaleMac(NSLocale* locale)
83 , m_gregorianCalendar(AdoptNS, [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar])
84 , m_didInitializeNumberData(false)
86 NSArray* availableLanguages = [NSLocale ISOLanguageCodes];
87 // NSLocale returns a lower case NSLocaleLanguageCode so we don't have care about case.
88 NSString* language = [m_locale.get() objectForKey:NSLocaleLanguageCode];
89 if ([availableLanguages indexOfObject:language] == NSNotFound)
90 m_locale.adoptNS([[NSLocale alloc] initWithLocaleIdentifier:defaultLanguage()]);
91 [m_gregorianCalendar.get() setLocale:m_locale.get()];
94 LocaleMac::~LocaleMac()
98 PassOwnPtr<LocaleMac> LocaleMac::create(const String& localeIdentifier)
100 RetainPtr<NSLocale> locale = [[NSLocale alloc] initWithLocaleIdentifier:localeIdentifier];
101 return adoptPtr(new LocaleMac(locale.get()));
104 PassOwnPtr<LocaleMac> LocaleMac::create(NSLocale* locale)
106 return adoptPtr(new LocaleMac(locale));
109 RetainPtr<NSDateFormatter> LocaleMac::shortDateFormatter()
111 return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterNoStyle);
114 const Vector<String>& LocaleMac::monthLabels()
116 if (!m_monthLabels.isEmpty())
117 return m_monthLabels;
118 m_monthLabels.reserveCapacity(12);
119 NSArray *array = [shortDateFormatter().get() monthSymbols];
120 if ([array count] == 12) {
121 for (unsigned i = 0; i < 12; ++i)
122 m_monthLabels.append(String([array objectAtIndex:i]));
123 return m_monthLabels;
125 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthFullName); ++i)
126 m_monthLabels.append(WTF::monthFullName[i]);
127 return m_monthLabels;
130 const Vector<String>& LocaleMac::weekDayShortLabels()
132 if (!m_weekDayShortLabels.isEmpty())
133 return m_weekDayShortLabels;
134 m_weekDayShortLabels.reserveCapacity(7);
135 NSArray *array = [shortDateFormatter().get() shortWeekdaySymbols];
136 if ([array count] == 7) {
137 for (unsigned i = 0; i < 7; ++i)
138 m_weekDayShortLabels.append(String([array objectAtIndex:i]));
139 return m_weekDayShortLabels;
141 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::weekdayName); ++i) {
142 // weekdayName starts with Monday.
143 m_weekDayShortLabels.append(WTF::weekdayName[(i + 6) % 7]);
145 return m_weekDayShortLabels;
148 unsigned LocaleMac::firstDayOfWeek()
150 // The document for NSCalendar - firstWeekday doesn't have an explanation of
151 // firstWeekday value. We can guess it by the document of NSDateComponents -
152 // weekDay, so it can be 1 through 7 and 1 is Sunday.
153 return [m_gregorianCalendar.get() firstWeekday] - 1;
156 bool LocaleMac::isRTL()
158 return NSLocaleLanguageDirectionRightToLeft == [NSLocale characterDirectionForLanguage:[NSLocale canonicalLanguageIdentifierFromString:[m_locale.get() localeIdentifier]]];
161 RetainPtr<NSDateFormatter> LocaleMac::timeFormatter()
163 return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterMediumStyle);
166 RetainPtr<NSDateFormatter> LocaleMac::shortTimeFormatter()
168 return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterShortStyle);
171 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithSeconds()
173 return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterMediumStyle);
176 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithoutSeconds()
178 return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterShortStyle);
181 String LocaleMac::dateFormat()
183 if (!m_dateFormat.isNull())
185 m_dateFormat = [shortDateFormatter().get() dateFormat];
189 String LocaleMac::monthFormat()
191 if (!m_monthFormat.isNull())
192 return m_monthFormat;
193 // Gets a format for "MMMM" because Windows API always provides formats for
194 // "MMMM" in some locales.
195 m_monthFormat = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMMM" options:0 locale:m_locale.get()];
196 return m_monthFormat;
199 String LocaleMac::shortMonthFormat()
201 if (!m_shortMonthFormat.isNull())
202 return m_shortMonthFormat;
203 m_shortMonthFormat = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMM" options:0 locale:m_locale.get()];
204 return m_shortMonthFormat;
207 String LocaleMac::timeFormat()
209 if (!m_timeFormatWithSeconds.isNull())
210 return m_timeFormatWithSeconds;
211 m_timeFormatWithSeconds = [timeFormatter().get() dateFormat];
212 return m_timeFormatWithSeconds;
215 String LocaleMac::shortTimeFormat()
217 if (!m_timeFormatWithoutSeconds.isNull())
218 return m_timeFormatWithoutSeconds;
219 m_timeFormatWithoutSeconds = [shortTimeFormatter().get() dateFormat];
220 return m_timeFormatWithoutSeconds;
223 String LocaleMac::dateTimeFormatWithSeconds()
225 if (!m_dateTimeFormatWithSeconds.isNull())
226 return m_dateTimeFormatWithSeconds;
227 m_dateTimeFormatWithSeconds = [dateTimeFormatterWithSeconds().get() dateFormat];
228 return m_dateTimeFormatWithSeconds;
231 String LocaleMac::dateTimeFormatWithoutSeconds()
233 if (!m_dateTimeFormatWithoutSeconds.isNull())
234 return m_dateTimeFormatWithoutSeconds;
235 m_dateTimeFormatWithoutSeconds = [dateTimeFormatterWithoutSeconds().get() dateFormat];
236 return m_dateTimeFormatWithoutSeconds;
239 const Vector<String>& LocaleMac::shortMonthLabels()
241 if (!m_shortMonthLabels.isEmpty())
242 return m_shortMonthLabels;
243 m_shortMonthLabels.reserveCapacity(12);
244 NSArray *array = [shortDateFormatter().get() shortMonthSymbols];
245 if ([array count] == 12) {
246 for (unsigned i = 0; i < 12; ++i)
247 m_shortMonthLabels.append([array objectAtIndex:i]);
248 return m_shortMonthLabels;
250 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthName); ++i)
251 m_shortMonthLabels.append(WTF::monthName[i]);
252 return m_shortMonthLabels;
255 const Vector<String>& LocaleMac::standAloneMonthLabels()
257 if (!m_standAloneMonthLabels.isEmpty())
258 return m_standAloneMonthLabels;
259 NSArray *array = [shortDateFormatter().get() standaloneMonthSymbols];
260 if ([array count] == 12) {
261 m_standAloneMonthLabels.reserveCapacity(12);
262 for (unsigned i = 0; i < 12; ++i)
263 m_standAloneMonthLabels.append([array objectAtIndex:i]);
264 return m_standAloneMonthLabels;
266 m_standAloneMonthLabels = shortMonthLabels();
267 return m_standAloneMonthLabels;
270 const Vector<String>& LocaleMac::shortStandAloneMonthLabels()
272 if (!m_shortStandAloneMonthLabels.isEmpty())
273 return m_shortStandAloneMonthLabels;
274 NSArray *array = [shortDateFormatter().get() shortStandaloneMonthSymbols];
275 if ([array count] == 12) {
276 m_shortStandAloneMonthLabels.reserveCapacity(12);
277 for (unsigned i = 0; i < 12; ++i)
278 m_shortStandAloneMonthLabels.append([array objectAtIndex:i]);
279 return m_shortStandAloneMonthLabels;
281 m_shortStandAloneMonthLabels = shortMonthLabels();
282 return m_shortStandAloneMonthLabels;
285 const Vector<String>& LocaleMac::timeAMPMLabels()
287 if (!m_timeAMPMLabels.isEmpty())
288 return m_timeAMPMLabels;
289 m_timeAMPMLabels.reserveCapacity(2);
290 RetainPtr<NSDateFormatter> formatter = shortTimeFormatter();
291 m_timeAMPMLabels.append([formatter.get() AMSymbol]);
292 m_timeAMPMLabels.append([formatter.get() PMSymbol]);
293 return m_timeAMPMLabels;
296 void LocaleMac::initializeLocaleData()
298 if (m_didInitializeNumberData)
300 m_didInitializeNumberData = true;
302 RetainPtr<NSNumberFormatter> formatter(AdoptNS, [[NSNumberFormatter alloc] init]);
303 [formatter.get() setLocale:m_locale.get()];
304 [formatter.get() setNumberStyle:NSNumberFormatterDecimalStyle];
305 [formatter.get() setUsesGroupingSeparator:NO];
307 RetainPtr<NSNumber> sampleNumber(AdoptNS, [[NSNumber alloc] initWithDouble:9876543210]);
308 String nineToZero([formatter.get() stringFromNumber:sampleNumber.get()]);
309 if (nineToZero.length() != 10)
311 Vector<String, DecimalSymbolsSize> symbols;
312 for (unsigned i = 0; i < 10; ++i)
313 symbols.append(nineToZero.substring(9 - i, 1));
314 ASSERT(symbols.size() == DecimalSeparatorIndex);
315 symbols.append([formatter.get() decimalSeparator]);
316 ASSERT(symbols.size() == GroupSeparatorIndex);
317 symbols.append([formatter.get() groupingSeparator]);
318 ASSERT(symbols.size() == DecimalSymbolsSize);
320 String positivePrefix([formatter.get() positivePrefix]);
321 String positiveSuffix([formatter.get() positiveSuffix]);
322 String negativePrefix([formatter.get() negativePrefix]);
323 String negativeSuffix([formatter.get() negativeSuffix]);
324 setLocaleData(symbols, positivePrefix, positiveSuffix, negativePrefix, negativeSuffix);