/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package libcore.icu; import java.util.LinkedHashSet; import java.util.Locale; /*-[ #import "java/lang/UnsupportedOperationException.h" ]-*/ /** * Makes ICU data accessible by pulling it from the Foundation API. */ public final class ICU { /** * Cache for ISO language names. */ private static String[] isoLanguages; /** * Cache for ISO country names. */ private static String[] isoCountries; /** * Returns an array of ISO language names (two-letter codes), fetched either * from ICU's database or from our memory cache. * * @return The array. */ public static String[] getISOLanguages() { if (isoLanguages == null) { isoLanguages = getISOLanguagesNative(); } return isoLanguages.clone(); } /** * Returns an array of ISO country names (two-letter codes), fetched either * from ICU's database or from our memory cache. * * @return The array. */ public static String[] getISOCountries() { if (isoCountries == null) { isoCountries = getISOCountriesNative(); } return isoCountries.clone(); } /** * Returns the appropriate {@code Locale} given a {@code String} of the form returned * by {@code toString}. This is very lenient, and doesn't care what's between the underscores: * this method can parse strings that {@code Locale.toString} won't produce. * Used to remove duplication. */ public static Locale localeFromString(String localeName) { int first = localeName.indexOf('_'); int second = localeName.indexOf('_', first + 1); if (first == -1) { // Language only ("ja"). return new Locale(localeName); } else if (second == -1) { // Language and country ("ja_JP"). return new Locale(localeName.substring(0, first), localeName.substring(first + 1)); } else { // Language and country and variant ("ja_JP_TRADITIONAL"). return new Locale(localeName.substring(0, first), localeName.substring(first + 1, second), localeName.substring(second + 1)); } } public static Locale[] localesFromStrings(String[] localeNames) { // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera. // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about // both so that we never need to convert back when talking to it. LinkedHashSet<Locale> set = new LinkedHashSet<Locale>(); for (String localeName : localeNames) { set.add(localeFromString(localeName)); } return set.toArray(new Locale[set.size()]); } private static Locale[] availableLocalesCache; public static Locale[] getAvailableLocales() { if (availableLocalesCache == null) { availableLocalesCache = localesFromStrings(getAvailableLocalesNative()); } return availableLocalesCache.clone(); } public static Locale[] getAvailableBreakIteratorLocales() { return localesFromStrings(getAvailableBreakIteratorLocalesNative()); } public static Locale[] getAvailableCalendarLocales() { return localesFromStrings(getAvailableCalendarLocalesNative()); } public static Locale[] getAvailableCollatorLocales() { return localesFromStrings(getAvailableCollatorLocalesNative()); } public static Locale[] getAvailableDateFormatLocales() { return localesFromStrings(getAvailableDateFormatLocalesNative()); } public static Locale[] getAvailableDateFormatSymbolsLocales() { return getAvailableDateFormatLocales(); } public static Locale[] getAvailableDecimalFormatSymbolsLocales() { return getAvailableNumberFormatLocales(); } public static Locale[] getAvailableNumberFormatLocales() { return localesFromStrings(getAvailableNumberFormatLocalesNative()); } public static native String[] getAvailableCurrencyCodes() /*-[ NSArray *currencyCodes = [NSLocale ISOCurrencyCodes]; return [IOSObjectArray arrayWithNSArray:currencyCodes type:NSString_class_()]; ]-*/; // --- Native methods accessing iOS data. private static native String[] getISOLanguagesNative() /*-[ NSArray *languages = [NSLocale ISOLanguageCodes]; NSUInteger count = [languages count]; NSMutableData* data = [NSMutableData dataWithLength: count * sizeof(id)]; NSRange range = NSMakeRange(0, count); [languages getObjects:(__unsafe_unretained id *) data.mutableBytes range:range]; IOSObjectArray * result = [IOSObjectArray arrayWithObjects:(__unsafe_unretained id *) data.mutableBytes count:(jint)count type:NSString_class_()]; return result; ]-*/; private static native String[] getISOCountriesNative() /*-[ NSArray *countries = [NSLocale ISOCountryCodes]; NSUInteger count = [countries count]; NSMutableData* data = [NSMutableData dataWithLength: count * sizeof(id)]; NSRange range = NSMakeRange(0, count); [countries getObjects:(__unsafe_unretained id *) data.mutableBytes range:range]; IOSObjectArray * result = [IOSObjectArray arrayWithObjects:(__unsafe_unretained id *) data.mutableBytes count:(jint)count type:NSString_class_()]; return result; ]-*/; private static native String[] getAvailableBreakIteratorLocalesNative() /*-[ // Foundation framework doesn't support break iterators. return [IOSObjectArray arrayWithLength:0 type:NSString_class_()]; ]-*/; private static native String[] getAvailableCollatorLocalesNative() /*-[ // Foundation framework doesn't support collators. return [IOSObjectArray arrayWithLength:0 type:NSString_class_()]; ]-*/; private static native String[] getAvailableLocalesNative() /*-[ NSArray *localeIds = [NSLocale availableLocaleIdentifiers]; return [IOSObjectArray arrayWithNSArray:localeIds type:NSString_class_()]; ]-*/; private static native String[] getAvailableDateFormatLocalesNative() /*-[ NSMutableArray *localesWithDateFormats = [NSMutableArray array]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; for (NSString *localeId in [NSLocale availableLocaleIdentifiers]) { NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; [formatter setLocale:locale]; NSString *dateFormat = [formatter dateFormat]; if (dateFormat) { [localesWithDateFormats addObject:localeId]; } #if !__has_feature(objc_arc) [locale release]; #endif } return [IOSObjectArray arrayWithNSArray:localesWithDateFormats type:NSString_class_()]; ]-*/; private static native String[] getAvailableCalendarLocalesNative() /*-[ NSMutableArray *localesWithCalendarFormats = [NSMutableArray array]; for (NSString *localeId in [NSLocale availableLocaleIdentifiers]) { NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; if ([locale objectForKey:NSLocaleCalendar]) { [localesWithCalendarFormats addObject:localeId]; } #if !__has_feature(objc_arc) [locale release]; #endif } return [IOSObjectArray arrayWithNSArray:localesWithCalendarFormats type:NSString_class_()]; ]-*/; private static native String[] getAvailableNumberFormatLocalesNative() /*-[ NSMutableArray *localesWithNumberFormats = [NSMutableArray array]; NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; for (NSString *localeId in [NSLocale availableLocaleIdentifiers]) { NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; [formatter setLocale:locale]; NSString *numberFormat = [formatter positiveFormat]; if (numberFormat) { [localesWithNumberFormats addObject:localeId]; } #if !__has_feature(objc_arc) [locale release]; #endif } return [IOSObjectArray arrayWithNSArray:localesWithNumberFormats type:NSString_class_()]; ]-*/; public static String getDisplayCountry(Locale targetLocale, Locale locale) { return getDisplayCountryNative(targetLocale.toLanguageTag(), locale.toLanguageTag()); } private static native String getDisplayCountryNative(String countryCode, String localeId) /*-[ NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; NSString *country = [locale objectForKey:NSLocaleCountryCode]; #if !__has_feature(objc_arc) [locale release]; #endif return (country) ? country : countryCode; ]-*/; public static String getDisplayLanguage(Locale targetLocale, Locale locale) { return getDisplayLanguageNative(targetLocale.toLanguageTag(), locale.toLanguageTag()); } private static native String getDisplayLanguageNative(String languageCode, String localeId) /*-[ NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; NSString *language = [locale objectForKey:NSLocaleLanguageCode]; #if !__has_feature(objc_arc) [locale release]; #endif return (language) ? language : languageCode; ]-*/; public static String getDisplayVariant(Locale targetLocale, Locale locale) { return getDisplayVariantNative(targetLocale.toLanguageTag(), locale.toLanguageTag()); } private static native String getDisplayVariantNative(String variantCode, String localeId) /*-[ NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeId]; NSString *variant = [locale objectForKey:NSLocaleVariantCode]; #if !__has_feature(objc_arc) [locale release]; #endif return (variant) ? variant : variantCode; ]-*/; public static String getDisplayScript(Locale targetLocale, Locale locale) { return getDisplayScriptNative(targetLocale.toLanguageTag(), locale.toLanguageTag()); } private static native String getDisplayScriptNative(String targetLanguageTag, String languageTag) /*-[ @throw AUTORELEASE([[JavaLangUnsupportedOperationException alloc] initWithNSString:@"Display script not available on iOS"]); return nil; ]-*/; public static native String getISO3Country(String localeId) /*-[ @throw AUTORELEASE([[JavaLangUnsupportedOperationException alloc] initWithNSString:@"ISO3 codes not available on iOS"]); return nil; ]-*/; public static native String getISO3Language(String localeId) /*-[ @throw AUTORELEASE([[JavaLangUnsupportedOperationException alloc] initWithNSString:@"ISO3 codes not available on iOS"]); return nil; ]-*/; public static native String getCurrencyDisplayName(Locale locale, String currencyCode) /*-[ NSString *localeId = [locale toLanguageTag]; NSLocale *nativeLocale = AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:localeId]); return [nativeLocale displayNameForKey:NSLocaleCurrencyCode value:currencyCode]; ]-*/; public static native String getCurrencyCode(Locale locale) /*-[ NSLocale *nativeLocale = [NSLocale localeWithLocaleIdentifier:[locale toLanguageTag]]; NSNumberFormatter *formatter = AUTORELEASE([[NSNumberFormatter alloc] init]); [formatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [formatter setLocale:nativeLocale]; return [formatter currencyCode]; ]-*/; public static native String getCurrencySymbol(String localeId) /*-[ NSLocale *nativeLocale = AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:localeId]); NSNumberFormatter *formatter = AUTORELEASE([[NSNumberFormatter alloc] init]); [formatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [formatter setLocale:nativeLocale]; return [formatter currencySymbol]; ]-*/; public static native String getCurrencySymbol(Locale locale, String currencyCode) /*-[ NSString *localeId = [locale toLanguageTag]; NSLocale *nativeLocale = AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:localeId]); return [nativeLocale displayNameForKey:NSLocaleCurrencySymbol value:currencyCode]; ]-*/; public static native int getCurrencyFractionDigits(String currencyCode) /*-[ NSNumberFormatter *formatter = AUTORELEASE([[NSNumberFormatter alloc] init]); [formatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [formatter setCurrencyCode:currencyCode]; return (int) [formatter maximumFractionDigits]; ]-*/; /** * Takes a BCP-47 language tag (Locale.toLanguageTag()). e.g. en-US, not en_US */ public static native void setDefaultLocale(String languageTag) /*-[ [[NSUserDefaults standardUserDefaults] setObject:languageTag forKey:@"LanguageCode"]; [[NSUserDefaults standardUserDefaults] synchronize]; ]-*/; }