Home | History | Annotate | Download | only in mac
      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 "core/platform/text/mac/LocaleMac.h"
     33 
     34 #import <Foundation/NSDateFormatter.h>
     35 #import <Foundation/NSLocale.h>
     36 #include "core/platform/Language.h"
     37 #include "core/platform/LocalizedStrings.h"
     38 #include "wtf/DateMath.h"
     39 #include "wtf/PassOwnPtr.h"
     40 #include "wtf/RetainPtr.h"
     41 #include "wtf/text/StringBuilder.h"
     42 
     43 using namespace std;
     44 
     45 namespace WebCore {
     46 
     47 static inline String languageFromLocale(const String& locale)
     48 {
     49     String normalizedLocale = locale;
     50     normalizedLocale.replace('-', '_');
     51     size_t separatorPosition = normalizedLocale.find('_');
     52     if (separatorPosition == notFound)
     53         return normalizedLocale;
     54     return normalizedLocale.left(separatorPosition);
     55 }
     56 
     57 static RetainPtr<NSLocale> determineLocale(const String& locale)
     58 {
     59     RetainPtr<NSLocale> currentLocale = [NSLocale currentLocale];
     60     String currentLocaleLanguage = languageFromLocale(String([currentLocale.get() localeIdentifier]));
     61     String localeLanguage = languageFromLocale(locale);
     62     if (equalIgnoringCase(currentLocaleLanguage, localeLanguage))
     63         return currentLocale;
     64     // It seems initWithLocaleIdentifier accepts dash-separated locale identifier.
     65      return RetainPtr<NSLocale>(AdoptNS, [[NSLocale alloc] initWithLocaleIdentifier:locale]);
     66 }
     67 
     68 PassOwnPtr<Locale> Locale::create(const AtomicString& locale)
     69 {
     70     return LocaleMac::create(determineLocale(locale.string()).get());
     71 }
     72 
     73 static RetainPtr<NSDateFormatter> createDateTimeFormatter(NSLocale* locale, NSCalendar* calendar, NSDateFormatterStyle dateStyle, NSDateFormatterStyle timeStyle)
     74 {
     75     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
     76     [formatter setLocale:locale];
     77     [formatter setDateStyle:dateStyle];
     78     [formatter setTimeStyle:timeStyle];
     79     [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
     80     [formatter setCalendar:calendar];
     81     return adoptNS(formatter);
     82 }
     83 
     84 LocaleMac::LocaleMac(NSLocale* locale)
     85     : m_locale(locale)
     86     , m_gregorianCalendar(AdoptNS, [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar])
     87     , m_didInitializeNumberData(false)
     88 {
     89     NSArray* availableLanguages = [NSLocale ISOLanguageCodes];
     90     // NSLocale returns a lower case NSLocaleLanguageCode so we don't have care about case.
     91     NSString* language = [m_locale.get() objectForKey:NSLocaleLanguageCode];
     92     if ([availableLanguages indexOfObject:language] == NSNotFound)
     93         m_locale.adoptNS([[NSLocale alloc] initWithLocaleIdentifier:defaultLanguage()]);
     94     [m_gregorianCalendar.get() setLocale:m_locale.get()];
     95 }
     96 
     97 LocaleMac::~LocaleMac()
     98 {
     99 }
    100 
    101 PassOwnPtr<LocaleMac> LocaleMac::create(const String& localeIdentifier)
    102 {
    103     RetainPtr<NSLocale> locale = [[NSLocale alloc] initWithLocaleIdentifier:localeIdentifier];
    104     return adoptPtr(new LocaleMac(locale.get()));
    105 }
    106 
    107 PassOwnPtr<LocaleMac> LocaleMac::create(NSLocale* locale)
    108 {
    109     return adoptPtr(new LocaleMac(locale));
    110 }
    111 
    112 RetainPtr<NSDateFormatter> LocaleMac::shortDateFormatter()
    113 {
    114     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterNoStyle);
    115 }
    116 
    117 const Vector<String>& LocaleMac::monthLabels()
    118 {
    119     if (!m_monthLabels.isEmpty())
    120         return m_monthLabels;
    121     m_monthLabels.reserveCapacity(12);
    122     NSArray *array = [shortDateFormatter().get() monthSymbols];
    123     if ([array count] == 12) {
    124         for (unsigned i = 0; i < 12; ++i)
    125             m_monthLabels.append(String([array objectAtIndex:i]));
    126         return m_monthLabels;
    127     }
    128     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthFullName); ++i)
    129         m_monthLabels.append(WTF::monthFullName[i]);
    130     return m_monthLabels;
    131 }
    132 
    133 #if ENABLE(CALENDAR_PICKER)
    134 const Vector<String>& LocaleMac::weekDayShortLabels()
    135 {
    136     if (!m_weekDayShortLabels.isEmpty())
    137         return m_weekDayShortLabels;
    138     m_weekDayShortLabels.reserveCapacity(7);
    139     NSArray *array = [shortDateFormatter().get() shortWeekdaySymbols];
    140     if ([array count] == 7) {
    141         for (unsigned i = 0; i < 7; ++i)
    142             m_weekDayShortLabels.append(String([array objectAtIndex:i]));
    143         return m_weekDayShortLabels;
    144     }
    145     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::weekdayName); ++i) {
    146         // weekdayName starts with Monday.
    147         m_weekDayShortLabels.append(WTF::weekdayName[(i + 6) % 7]);
    148     }
    149     return m_weekDayShortLabels;
    150 }
    151 
    152 unsigned LocaleMac::firstDayOfWeek()
    153 {
    154     // The document for NSCalendar - firstWeekday doesn't have an explanation of
    155     // firstWeekday value. We can guess it by the document of NSDateComponents -
    156     // weekDay, so it can be 1 through 7 and 1 is Sunday.
    157     return [m_gregorianCalendar.get() firstWeekday] - 1;
    158 }
    159 
    160 bool LocaleMac::isRTL()
    161 {
    162     return NSLocaleLanguageDirectionRightToLeft == [NSLocale characterDirectionForLanguage:[NSLocale canonicalLanguageIdentifierFromString:[m_locale.get() localeIdentifier]]];
    163 }
    164 #endif
    165 
    166 RetainPtr<NSDateFormatter> LocaleMac::timeFormatter()
    167 {
    168     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterMediumStyle);
    169 }
    170 
    171 RetainPtr<NSDateFormatter> LocaleMac::shortTimeFormatter()
    172 {
    173     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterNoStyle, NSDateFormatterShortStyle);
    174 }
    175 
    176 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithSeconds()
    177 {
    178     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterMediumStyle);
    179 }
    180 
    181 RetainPtr<NSDateFormatter> LocaleMac::dateTimeFormatterWithoutSeconds()
    182 {
    183     return createDateTimeFormatter(m_locale.get(), m_gregorianCalendar.get(), NSDateFormatterShortStyle, NSDateFormatterShortStyle);
    184 }
    185 
    186 String LocaleMac::dateFormat()
    187 {
    188     if (!m_dateFormat.isNull())
    189         return m_dateFormat;
    190     m_dateFormat = [shortDateFormatter().get() dateFormat];
    191     return m_dateFormat;
    192 }
    193 
    194 String LocaleMac::monthFormat()
    195 {
    196     if (!m_monthFormat.isNull())
    197         return m_monthFormat;
    198     // Gets a format for "MMMM" because Windows API always provides formats for
    199     // "MMMM" in some locales.
    200     m_monthFormat = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMMM" options:0 locale:m_locale.get()];
    201     return m_monthFormat;
    202 }
    203 
    204 String LocaleMac::shortMonthFormat()
    205 {
    206     if (!m_shortMonthFormat.isNull())
    207         return m_shortMonthFormat;
    208     m_shortMonthFormat = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMM" options:0 locale:m_locale.get()];
    209     return m_shortMonthFormat;
    210 }
    211 
    212 String LocaleMac::timeFormat()
    213 {
    214     if (!m_timeFormatWithSeconds.isNull())
    215         return m_timeFormatWithSeconds;
    216     m_timeFormatWithSeconds = [timeFormatter().get() dateFormat];
    217     return m_timeFormatWithSeconds;
    218 }
    219 
    220 String LocaleMac::shortTimeFormat()
    221 {
    222     if (!m_timeFormatWithoutSeconds.isNull())
    223         return m_timeFormatWithoutSeconds;
    224     m_timeFormatWithoutSeconds = [shortTimeFormatter().get() dateFormat];
    225     return m_timeFormatWithoutSeconds;
    226 }
    227 
    228 String LocaleMac::dateTimeFormatWithSeconds()
    229 {
    230     if (!m_dateTimeFormatWithSeconds.isNull())
    231         return m_dateTimeFormatWithSeconds;
    232     m_dateTimeFormatWithSeconds = [dateTimeFormatterWithSeconds().get() dateFormat];
    233     return m_dateTimeFormatWithSeconds;
    234 }
    235 
    236 String LocaleMac::dateTimeFormatWithoutSeconds()
    237 {
    238     if (!m_dateTimeFormatWithoutSeconds.isNull())
    239         return m_dateTimeFormatWithoutSeconds;
    240     m_dateTimeFormatWithoutSeconds = [dateTimeFormatterWithoutSeconds().get() dateFormat];
    241     return m_dateTimeFormatWithoutSeconds;
    242 }
    243 
    244 const Vector<String>& LocaleMac::shortMonthLabels()
    245 {
    246     if (!m_shortMonthLabels.isEmpty())
    247         return m_shortMonthLabels;
    248     m_shortMonthLabels.reserveCapacity(12);
    249     NSArray *array = [shortDateFormatter().get() shortMonthSymbols];
    250     if ([array count] == 12) {
    251         for (unsigned i = 0; i < 12; ++i)
    252             m_shortMonthLabels.append([array objectAtIndex:i]);
    253         return m_shortMonthLabels;
    254     }
    255     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::monthName); ++i)
    256         m_shortMonthLabels.append(WTF::monthName[i]);
    257     return m_shortMonthLabels;
    258 }
    259 
    260 const Vector<String>& LocaleMac::standAloneMonthLabels()
    261 {
    262     if (!m_standAloneMonthLabels.isEmpty())
    263         return m_standAloneMonthLabels;
    264     NSArray *array = [shortDateFormatter().get() standaloneMonthSymbols];
    265     if ([array count] == 12) {
    266         m_standAloneMonthLabels.reserveCapacity(12);
    267         for (unsigned i = 0; i < 12; ++i)
    268             m_standAloneMonthLabels.append([array objectAtIndex:i]);
    269         return m_standAloneMonthLabels;
    270     }
    271     m_standAloneMonthLabels = shortMonthLabels();
    272     return m_standAloneMonthLabels;
    273 }
    274 
    275 const Vector<String>& LocaleMac::shortStandAloneMonthLabels()
    276 {
    277     if (!m_shortStandAloneMonthLabels.isEmpty())
    278         return m_shortStandAloneMonthLabels;
    279     NSArray *array = [shortDateFormatter().get() shortStandaloneMonthSymbols];
    280     if ([array count] == 12) {
    281         m_shortStandAloneMonthLabels.reserveCapacity(12);
    282         for (unsigned i = 0; i < 12; ++i)
    283             m_shortStandAloneMonthLabels.append([array objectAtIndex:i]);
    284         return m_shortStandAloneMonthLabels;
    285     }
    286     m_shortStandAloneMonthLabels = shortMonthLabels();
    287     return m_shortStandAloneMonthLabels;
    288 }
    289 
    290 const Vector<String>& LocaleMac::timeAMPMLabels()
    291 {
    292     if (!m_timeAMPMLabels.isEmpty())
    293         return m_timeAMPMLabels;
    294     m_timeAMPMLabels.reserveCapacity(2);
    295     RetainPtr<NSDateFormatter> formatter = shortTimeFormatter();
    296     m_timeAMPMLabels.append([formatter.get() AMSymbol]);
    297     m_timeAMPMLabels.append([formatter.get() PMSymbol]);
    298     return m_timeAMPMLabels;
    299 }
    300 
    301 void LocaleMac::initializeLocaleData()
    302 {
    303     if (m_didInitializeNumberData)
    304         return;
    305     m_didInitializeNumberData = true;
    306 
    307     RetainPtr<NSNumberFormatter> formatter(AdoptNS, [[NSNumberFormatter alloc] init]);
    308     [formatter.get() setLocale:m_locale.get()];
    309     [formatter.get() setNumberStyle:NSNumberFormatterDecimalStyle];
    310     [formatter.get() setUsesGroupingSeparator:NO];
    311 
    312     RetainPtr<NSNumber> sampleNumber(AdoptNS, [[NSNumber alloc] initWithDouble:9876543210]);
    313     String nineToZero([formatter.get() stringFromNumber:sampleNumber.get()]);
    314     if (nineToZero.length() != 10)
    315         return;
    316     Vector<String, DecimalSymbolsSize> symbols;
    317     for (unsigned i = 0; i < 10; ++i)
    318         symbols.append(nineToZero.substring(9 - i, 1));
    319     ASSERT(symbols.size() == DecimalSeparatorIndex);
    320     symbols.append([formatter.get() decimalSeparator]);
    321     ASSERT(symbols.size() == GroupSeparatorIndex);
    322     symbols.append([formatter.get() groupingSeparator]);
    323     ASSERT(symbols.size() == DecimalSymbolsSize);
    324 
    325     String positivePrefix([formatter.get() positivePrefix]);
    326     String positiveSuffix([formatter.get() positiveSuffix]);
    327     String negativePrefix([formatter.get() negativePrefix]);
    328     String negativeSuffix([formatter.get() negativeSuffix]);
    329     setLocaleData(symbols, positivePrefix, positiveSuffix, negativePrefix, negativeSuffix);
    330 }
    331 
    332 }
    333