2 *******************************************************************************
3 * Copyright (C) 2008-2012, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl.javaspi;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Locale;
17 import java.util.Properties;
20 import com.ibm.icu.impl.ICUResourceBundle;
21 import com.ibm.icu.util.ULocale;
22 import com.ibm.icu.util.ULocale.Builder;
24 public class ICULocaleServiceProvider {
25 private static final String SPI_PROP_FILE = "com/ibm/icu/impl/javaspi/ICULocaleServiceProviderConfig.properties";
27 private static final String SUFFIX_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.icuVariantSuffix";
28 private static final String ENABLE_VARIANTS_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.enableIcuVariants";
29 private static final String ENABLE_ISO3_LANG_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.enableIso3Languages";
30 private static final String USE_DECIMALFORMAT_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.useDecimalFormat";
32 private static boolean configLoaded = false;
34 private static String suffix = "ICU4J";
35 private static boolean enableVariants = true;
36 private static boolean enableIso3Lang = true;
37 private static boolean useDecimalFormat = false;
39 private static final Locale[] SPECIAL_LOCALES = {
40 new Locale("ja", "JP", "JP"),
42 new Locale("no", "NO"),
43 new Locale("no", "NO", "NY"),
44 new Locale("sr", "CS"),
45 new Locale("th", "TH", "TH"),
48 private static Map<Locale, Locale> SPECIAL_LOCALES_MAP = null;
50 private static Locale[] LOCALES = null;
52 public static Locale[] getAvailableLocales() {
53 Locale[] all = getLocales();
54 return Arrays.copyOf(all, all.length);
57 public static ULocale toULocaleNoSpecialVariant(Locale locale) {
58 // If the given Locale has legacy ill-formed variant
59 // reserved by JDK, use the map to resolve the locale.
60 Locale spLoc = getSpecialLocalesMap().get(locale);
62 return ULocale.forLocale(spLoc);
65 // The locale may have script field on Java 7+.
66 // So we once convert it to ULocale, then strip the ICU suffix off
68 ULocale result = ULocale.forLocale(locale);
69 String variant = result.getVariant();
70 String suffix = getIcuSuffix();
71 String variantNoSuffix = null;
72 if (variant.equals(suffix)) {
74 } else if (variant.endsWith(suffix) && variant.charAt(variant.length() - suffix.length() - 1) == '_') {
75 variantNoSuffix = variant.substring(0, variant.length() - suffix.length() - 1);
77 if (variantNoSuffix == null) {
81 // Strip off ICU's special suffix - cannot use Builder because
82 // original locale may have ill-formed variant
83 StringBuilder id = new StringBuilder(result.getLanguage());
84 String script = result.getScript();
85 String country = result.getCountry();
86 if (script.length() > 0) {
90 if (country.length() > 0 || variantNoSuffix.length() > 0) {
94 if (variantNoSuffix.length() > 0) {
96 id.append(variantNoSuffix);
98 String orgID = result.getName();
99 int kwdIdx = orgID.indexOf('@');
101 id.append(orgID.substring(kwdIdx));
103 return new ULocale(id.toString());
106 public static boolean useDecimalFormat() {
108 return useDecimalFormat;
111 private static synchronized Map<Locale, Locale> getSpecialLocalesMap() {
112 if (SPECIAL_LOCALES_MAP != null) {
113 return SPECIAL_LOCALES_MAP;
116 Map<Locale, Locale> splocs = new HashMap<Locale, Locale>();
117 for (Locale spLoc : SPECIAL_LOCALES) {
118 String var = spLoc.getVariant();
119 if (var.length() > 0) {
120 splocs.put(new Locale(spLoc.getLanguage(), spLoc.getCountry(), var + "_" + getIcuSuffix()), spLoc);
123 SPECIAL_LOCALES_MAP = Collections.unmodifiableMap(splocs);
124 return SPECIAL_LOCALES_MAP;
127 private static synchronized Locale[] getLocales() {
128 if (LOCALES != null) {
132 Set<Locale> localeSet = new HashSet<Locale>();
133 ULocale[] icuLocales = ICUResourceBundle.getAvailableULocales();
135 for (ULocale uloc : icuLocales) {
136 String language = uloc.getLanguage();
137 if (language.length() >= 3 && !enableIso3Languages()) {
140 addULocale(uloc, localeSet);
142 if (uloc.getScript().length() > 0 && uloc.getCountry().length() > 0) {
143 // ICU's available locales do not contain language+country
144 // locales if script is available. Need to add them too.
145 Builder locBld = new Builder();
147 locBld.setLocale(uloc);
148 locBld.setScript(null);
149 ULocale ulocWithoutScript = locBld.build();
150 addULocale(ulocWithoutScript, localeSet);
151 } catch (Exception e) {
157 for (Locale l : SPECIAL_LOCALES) {
158 addLocale(l, localeSet);
161 LOCALES = localeSet.toArray(new Locale[0]);
165 private static void addLocale(Locale loc, Set<Locale> locales) {
168 if (enableIcuVariants()) {
170 String language = loc.getLanguage();
171 String country = loc.getCountry();
172 String variant = loc.getVariant();
174 StringBuilder var = new StringBuilder(variant);
175 if (var.length() != 0) {
178 var.append(getIcuSuffix());
179 locales.add(new Locale(language, country, var.toString()));
183 private static void addULocale(ULocale uloc, Set<Locale> locales) {
185 // ULocale#toLocale on Java 6 maps "nn" to "no_NO_NY"
186 if (uloc.getLanguage().equals("nn") && uloc.getScript().length() == 0) {
187 Locale locNN = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
188 addLocale(locNN, locales);
192 locales.add(uloc.toLocale());
194 if (enableIcuVariants()) {
196 StringBuilder var = new StringBuilder(uloc.getVariant());
197 if (var.length() != 0) {
200 var.append(getIcuSuffix());
202 Builder locBld = new Builder();
204 locBld.setLocale(uloc);
205 locBld.setVariant(var.toString());
206 ULocale ulocWithVar = locBld.build();
207 locales.add(ulocWithVar.toLocale());
208 } catch (Exception e) {
214 private static boolean enableIso3Languages() {
215 return enableIso3Lang;
218 private static boolean enableIcuVariants() {
220 return enableVariants;
223 private static String getIcuSuffix() {
228 private static synchronized void loadConfiguration() {
232 Properties spiConfigProps = new Properties();
234 InputStream is = ClassLoader.getSystemResourceAsStream(SPI_PROP_FILE);
235 spiConfigProps.load(is);
237 String val = (String)spiConfigProps.get(SUFFIX_KEY);
238 if (val != null && val.length() > 0) {
241 enableVariants = parseBooleanString((String)spiConfigProps.get(ENABLE_VARIANTS_KEY), enableVariants);
242 enableIso3Lang = parseBooleanString((String)spiConfigProps.get(ENABLE_ISO3_LANG_KEY), enableIso3Lang);
243 useDecimalFormat = parseBooleanString((String)spiConfigProps.get(USE_DECIMALFORMAT_KEY), useDecimalFormat);
244 } catch (IOException ioe) {
245 // Any IO errors, ignore
250 private static boolean parseBooleanString(String str, boolean defaultVal) {
254 if (str.equalsIgnoreCase("true")) {
256 } else if (str.equalsIgnoreCase("false")) {