Home | History | Annotate | Download | only in text
      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/LocaleMac.h"
     33 
     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"
     41 
     42 namespace blink {
     43 
     44 static inline String languageFromLocale(const String& locale)
     45 {
     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);
     52 }
     53 
     54 static RetainPtr<NSLocale> determineLocale(const String& locale)
     55 {
     56     RetainPtr<NSLocale> currentLocale = [NSLocale currentLocale];
     57     String currentLocaleLanguage = languageFromLocale(String([currentLocale.get() localeIdentifier]));
     58     String localeLanguage = languageFromLocale(locale);
     59     if (equalIgnoringCase(currentLocaleLanguage, localeLanguage))
     60         return currentLocale;
     61     // It seems initWithLocaleIdentifier accepts dash-separated locale identifier.
     62      return RetainPtr<NSLocale>(AdoptNS, [[NSLocale alloc] initWithLocaleIdentifier:locale]);
     63 }
     64 
     65 PassOwnPtr<Locale> Locale::create(const String& locale)
     66 {
     67     return LocaleMac::create(determineLocale(locale).get());
     68 }
     69 
     70 static RetainPtr<NSDateFormatter> createDateTimeFormatter(NSLocale* locale, NSCalendar* calendar, NSDateFormatterStyle dateStyle, NSDateFormatterStyle timeStyle)
     71 {
     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);
     79 }
     80 
     81 LocaleMac::LocaleMac(NSLocale* locale)
     82     : m_locale(locale)
     83     , m_gregorianCalendar(AdoptNS, [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar])
     84     , m_didInitializeNumberData(false)
     85 {
     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()];
     92 }
     93 
     94 LocaleMac::~LocaleMac()
     95 {
     96 }
     97 
     98 PassOwnPtr<LocaleMac> LocaleMac::create(const String& localeIdentifier)
     99 {
    100     RetainPtr<NSLocale> locale = [[NSLocale alloc] initWithLocaleIdentifier:localeIdentifier];
    101     return adoptPtr(new LocaleMac(locale.get()));
    102 }
    103 
    104 PassOwnPtr<LocaleMac> LocaleMac::create(NSLocale* locale)
    105 {
    106     return adoptPtr(new LocaleMac(locale));
    107 }
    108 
    109 RetainPtr<NSDateFormatter> LocaleMac::shortDateFormatter()
    110 {
    111     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterNoStyle);
    112 }
    113 
    114 const Vector<String>& LocaleMac::monthLabels()
    115 {
    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;
    124     }
    125     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthFullName); ++i)
    126         m_monthLabels.append(WTF::monthFullName[i]);
    127     return m_monthLabels;
    128 }
    129 
    130 const Vector<String>& LocaleMac::weekDayShortLabels()
    131 {
    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;
    140     }
    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]);
    144     }
    145     return m_weekDayShortLabels;
    146 }
    147 
    148 unsigned LocaleMac::firstDayOfWeek()
    149 {
    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;
    154 }
    155 
    156 bool LocaleMac::isRTL()
    157 {
    158     return NSLocaleLanguageDirectionRightToLeft == [NSLocale characterDirectionForLanguage:[NSLocale canonicalLanguageIdentifierFromString:[m_locale.get() localeIdentifier]]];
    159 }
    160 
    161 RetainPtr<NSDateFormatter> LocaleMac::timeFormatter()
    162 {
    163     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterMediumStyle);
    164 }
    165 
    166 RetainPtr<NSDateFormatter> LocaleMac::shortTimeFormatter()
    167 {
    168     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterShortStyle);
    169 }
    170 
    171 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithSeconds()
    172 {
    173     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterMediumStyle);
    174 }
    175 
    176 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithoutSeconds()
    177 {
    178     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterShortStyle);
    179 }
    180 
    181 String LocaleMac::dateFormat()
    182 {
    183     if (!m_dateFormat.isNull())
    184         return m_dateFormat;
    185     m_dateFormat = [shortDateFormatter().get() dateFormat];
    186     return m_dateFormat;
    187 }
    188 
    189 String LocaleMac::monthFormat()
    190 {
    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;
    197 }
    198 
    199 String LocaleMac::shortMonthFormat()
    200 {
    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;
    205 }
    206 
    207 String LocaleMac::timeFormat()
    208 {
    209     if (!m_timeFormatWithSeconds.isNull())
    210         return m_timeFormatWithSeconds;
    211     m_timeFormatWithSeconds = [timeFormatter().get() dateFormat];
    212     return m_timeFormatWithSeconds;
    213 }
    214 
    215 String LocaleMac::shortTimeFormat()
    216 {
    217     if (!m_timeFormatWithoutSeconds.isNull())
    218         return m_timeFormatWithoutSeconds;
    219     m_timeFormatWithoutSeconds = [shortTimeFormatter().get() dateFormat];
    220     return m_timeFormatWithoutSeconds;
    221 }
    222 
    223 String LocaleMac::dateTimeFormatWithSeconds()
    224 {
    225     if (!m_dateTimeFormatWithSeconds.isNull())
    226         return m_dateTimeFormatWithSeconds;
    227     m_dateTimeFormatWithSeconds = [dateTimeFormatterWithSeconds().get() dateFormat];
    228     return m_dateTimeFormatWithSeconds;
    229 }
    230 
    231 String LocaleMac::dateTimeFormatWithoutSeconds()
    232 {
    233     if (!m_dateTimeFormatWithoutSeconds.isNull())
    234         return m_dateTimeFormatWithoutSeconds;
    235     m_dateTimeFormatWithoutSeconds = [dateTimeFormatterWithoutSeconds().get() dateFormat];
    236     return m_dateTimeFormatWithoutSeconds;
    237 }
    238 
    239 const Vector<String>& LocaleMac::shortMonthLabels()
    240 {
    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;
    249     }
    250     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthName); ++i)
    251         m_shortMonthLabels.append(WTF::monthName[i]);
    252     return m_shortMonthLabels;
    253 }
    254 
    255 const Vector<String>& LocaleMac::standAloneMonthLabels()
    256 {
    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;
    265     }
    266     m_standAloneMonthLabels = shortMonthLabels();
    267     return m_standAloneMonthLabels;
    268 }
    269 
    270 const Vector<String>& LocaleMac::shortStandAloneMonthLabels()
    271 {
    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;
    280     }
    281     m_shortStandAloneMonthLabels = shortMonthLabels();
    282     return m_shortStandAloneMonthLabels;
    283 }
    284 
    285 const Vector<String>& LocaleMac::timeAMPMLabels()
    286 {
    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;
    294 }
    295 
    296 void LocaleMac::initializeLocaleData()
    297 {
    298     if (m_didInitializeNumberData)
    299         return;
    300     m_didInitializeNumberData = true;
    301 
    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];
    306 
    307     RetainPtr<NSNumber> sampleNumber(AdoptNS, [[NSNumber alloc] initWithDouble:9876543210]);
    308     String nineToZero([formatter.get() stringFromNumber:sampleNumber.get()]);
    309     if (nineToZero.length() != 10)
    310         return;
    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);
    319 
    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);
    325 }
    326 
    327 }
    328