2 ******************************************************************************
3 * Copyright (C) 2003-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
8 package com.ibm.icu.util;
10 import java.io.Serializable;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.security.AccessControlException;
14 import java.security.AccessController;
15 import java.security.PrivilegedAction;
16 import java.text.ParseException;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Locale;
21 import java.util.Map.Entry;
22 import java.util.MissingResourceException;
24 import java.util.TreeMap;
25 import java.util.TreeSet;
27 import com.ibm.icu.impl.ICUCache;
28 import com.ibm.icu.impl.ICUResourceBundle;
29 import com.ibm.icu.impl.ICUResourceTableAccess;
30 import com.ibm.icu.impl.LocaleIDParser;
31 import com.ibm.icu.impl.LocaleIDs;
32 import com.ibm.icu.impl.LocaleUtility;
33 import com.ibm.icu.impl.SimpleCache;
34 import com.ibm.icu.impl.locale.AsciiUtil;
35 import com.ibm.icu.impl.locale.BaseLocale;
36 import com.ibm.icu.impl.locale.Extension;
37 import com.ibm.icu.impl.locale.InternalLocaleBuilder;
38 import com.ibm.icu.impl.locale.LanguageTag;
39 import com.ibm.icu.impl.locale.LocaleExtensions;
40 import com.ibm.icu.impl.locale.LocaleSyntaxException;
41 import com.ibm.icu.impl.locale.ParseStatus;
42 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
43 import com.ibm.icu.text.LocaleDisplayNames;
44 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
47 * {@icuenhanced java.util.Locale}.{@icu _usage_}
49 * A class analogous to {@link java.util.Locale} that provides additional
50 * support for ICU protocol. In ICU 3.0 this class is enhanced to support
51 * RFC 3066 language identifiers.
53 * <p>Many classes and services in ICU follow a factory idiom, in
54 * which a factory method or object responds to a client request with
55 * an object. The request includes a locale (the <i>requested</i>
56 * locale), and the returned object is constructed using data for that
57 * locale. The system may lack data for the requested locale, in
58 * which case the locale fallback mechanism will be invoked until a
59 * populated locale is found (the <i>valid</i> locale). Furthermore,
60 * even when a populated locale is found (the <i>valid</i> locale),
61 * further fallback may be required to reach a locale containing the
62 * specific data required by the service (the <i>actual</i> locale).
64 * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
65 * Normalization 'cleans up' ICU locale ids as follows:
67 * <li>language, script, country, variant, and keywords are properly cased<br>
68 * (lower, title, upper, upper, and lower case respectively)</li>
69 * <li>hyphens used as separators are converted to underscores</li>
70 * <li>three-letter language and country ids are converted to two-letter
71 * equivalents where available</li>
72 * <li>surrounding spaces are removed from keywords and values</li>
73 * <li>if there are multiple keywords, they are put in sorted order</li>
75 * Canonicalization additionally performs the following:
77 * <li>POSIX ids are converted to ICU format IDs</li>
78 * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
79 * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
81 * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
83 * All ULocale constructors automatically normalize the locale id. To handle
84 * POSIX ids, <code>canonicalize</code> can be called to convert the id
85 * to canonical form, or the <code>canonicalInstance</code> factory method
88 * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
89 * #ACTUAL_LOCALE} intended for use in methods named
90 * <tt>getLocale()</tt>. These methods exist in several ICU classes,
91 * including {@link com.ibm.icu.util.Calendar}, {@link
92 * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
93 * {@link com.ibm.icu.text.BreakIterator},
94 * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>,
95 * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
96 * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
97 * any. Once an object of one of these classes has been created,
98 * <tt>getLocale()</tt> may be called on it to determine the valid and
99 * actual locale arrived at during the object's construction.
101 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
102 * locale is not, in most cases.
104 * @see java.util.Locale
107 * @author Ram Viswanadha
110 public final class ULocale implements Serializable {
111 // using serialver from jdk1.4.2_05
112 private static final long serialVersionUID = 3715177670352309217L;
115 * Useful constant for language.
118 public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
121 * Useful constant for language.
124 public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
127 * Useful constant for language.
130 public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
133 * Useful constant for language.
136 public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
139 * Useful constant for language.
142 public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
145 * Useful constant for language.
148 public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
151 * Useful constant for language.
154 public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
157 * Useful constant for language.
160 public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans", Locale.CHINESE);
163 * Useful constant for language.
166 public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant", Locale.CHINESE);
169 * Useful constant for country/region.
172 public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
175 * Useful constant for country/region.
178 public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
181 * Useful constant for country/region.
184 public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
187 * Useful constant for country/region.
190 public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
193 * Useful constant for country/region.
196 public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
199 * Useful constant for country/region.
202 public static final ULocale CHINA = new ULocale("zh_Hans_CN", Locale.CHINA);
205 * Useful constant for country/region.
208 public static final ULocale PRC = CHINA;
211 * Useful constant for country/region.
214 public static final ULocale TAIWAN = new ULocale("zh_Hant_TW", Locale.TAIWAN);
217 * Useful constant for country/region.
220 public static final ULocale UK = new ULocale("en_GB", Locale.UK);
223 * Useful constant for country/region.
226 public static final ULocale US = new ULocale("en_US", Locale.US);
229 * Useful constant for country/region.
232 public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
235 * Useful constant for country/region.
238 public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
243 private static final String EMPTY_STRING = "";
245 // Used in both ULocale and LocaleIDParser, so moved up here.
246 private static final char UNDERSCORE = '_';
248 // default empty locale
249 private static final Locale EMPTY_LOCALE = new Locale("", "");
251 // special keyword key for Unicode locale attributes
252 private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
258 public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
261 * Enum for locale categories. These locale categories are used to get/set the default locale for
262 * the specific functionality represented by the category.
265 public enum Category {
267 * Category used to represent the default locale for displaying user interfaces.
272 * Category used to represent the default locale for formatting date, number and/or currency.
278 private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>();
283 private transient volatile Locale locale;
286 * The raw localeID that we were passed in.
288 private String localeID;
291 * Cache the locale data container fields.
292 * In future, we want to use them as the primary locale identifier storage.
294 private transient volatile BaseLocale baseLocale;
295 private transient volatile LocaleExtensions extensions;
298 private static String[][] CANONICALIZE_MAP;
299 private static String[][] variantsToKeywords;
301 private static void initCANONICALIZE_MAP() {
302 if (CANONICALIZE_MAP == null) {
304 * This table lists pairs of locale ids for canonicalization. The
305 * The 1st item is the normalized id. The 2nd item is the
306 * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
308 String[][] tempCANONICALIZE_MAP = {
309 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */
310 { "C", "en_US_POSIX", null, null }, /* POSIX name */
311 { "art_LOJBAN", "jbo", null, null }, /* registered name */
312 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */
313 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */
314 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
315 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */
316 { "de_1901", "de__1901", null, null }, /* registered name */
317 { "de_1906", "de__1906", null, null }, /* registered name */
318 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
319 { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
320 { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
321 { "de_LU_PREEURO", "de_LU", "currency", "EUR" },
322 { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
323 { "en_BOONT", "en__BOONT", null, null }, /* registered name */
324 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */
325 { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
326 { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
327 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
328 { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
329 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
330 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
331 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
332 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
333 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
334 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
335 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
336 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
337 { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
338 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
339 // { "nb_NO_NY", "nn_NO", null, null },
340 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
341 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
342 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
343 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */
344 { "sr_SP_CYRL", "sr_Cyrl_RS", null, null }, /* .NET name */
345 { "sr_SP_LATN", "sr_Latn_RS", null, null }, /* .NET name */
346 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
347 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
348 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
349 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */
350 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */
351 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */
352 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */
353 { "zh_GAN", "zh__GAN", null, null }, /* registered name */
354 { "zh_GUOYU", "zh", null, null }, /* registered name */
355 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */
356 { "zh_MIN", "zh__MIN", null, null }, /* registered name */
357 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */
358 { "zh_WUU", "zh__WUU", null, null }, /* registered name */
359 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */
360 { "zh_YUE", "zh__YUE", null, null } /* registered name */
363 synchronized (ULocale.class) {
364 if (CANONICALIZE_MAP == null) {
365 CANONICALIZE_MAP = tempCANONICALIZE_MAP;
369 if (variantsToKeywords == null) {
371 * This table lists pairs of locale ids for canonicalization. The
372 * The first item is the normalized variant id.
374 String[][] tempVariantsToKeywords = {
375 { "EURO", "currency", "EUR" },
376 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
377 { "STROKE", "collation", "stroke" } /* Solaris variant */
380 synchronized (ULocale.class) {
381 if (variantsToKeywords == null) {
382 variantsToKeywords = tempVariantsToKeywords;
389 * Private constructor used by static initializers.
391 private ULocale(String localeID, Locale locale) {
392 this.localeID = localeID;
393 this.locale = locale;
397 * Construct a ULocale object from a {@link java.util.Locale}.
398 * @param loc a JDK locale
400 private ULocale(Locale loc) {
401 this.localeID = getName(forLocale(loc).toString());
406 * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
407 * The ULocale is canonicalized.
408 * @param loc a JDK locale
411 public static ULocale forLocale(Locale loc) {
415 ULocale result = CACHE.get(loc);
416 if (result == null) {
417 result = JDKLocaleHelper.toULocale(loc);
418 CACHE.put(loc, result);
424 * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
425 * of optional language, script, country, and variant fields in that order,
426 * separated by underscores, followed by an optional keyword list. The
427 * script, if present, is four characters long-- this distinguishes it
428 * from a country code, which is two characters long. Other fields
429 * are distinguished by position as indicated by the underscores. The
430 * start of the keyword list is indicated by '@', and consists of two
431 * or more keyword/value pairs separated by semicolons(';').
433 * <p>This constructor does not canonicalize the localeID. So, for
434 * example, "zh__pinyin" remains unchanged instead of converting
435 * to "zh@collation=pinyin". By default ICU only recognizes the
436 * latter as specifying pinyin collation. Use {@link #createCanonical}
437 * or {@link #canonicalize} if you need to canonicalize the localeID.
439 * @param localeID string representation of the locale, e.g:
440 * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
443 public ULocale(String localeID) {
444 this.localeID = getName(localeID);
448 * Convenience overload of ULocale(String, String, String) for
449 * compatibility with java.util.Locale.
450 * @see #ULocale(String, String, String)
453 public ULocale(String a, String b) {
458 * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
459 * c. These fields are concatenated using underscores to form a localeID of the form
460 * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
463 * <p>Java locale strings consisting of language, country, and
464 * variant will be handled by this form, since the country code
465 * (being shorter than four letters long) will not be interpreted
466 * as a script code. If a script code is present, the final
467 * argument ('c') will be interpreted as the country code. It is
468 * recommended that this constructor only be used to ease porting,
469 * and that clients instead use the single-argument constructor
470 * when constructing a ULocale from a localeID.
471 * @param a first component of the locale id
472 * @param b second component of the locale id
473 * @param c third component of the locale id
474 * @see #ULocale(String)
477 public ULocale(String a, String b, String c) {
478 localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
482 * {@icu} Creates a ULocale from the id by first canonicalizing the id.
483 * @param nonCanonicalID the locale id to canonicalize
484 * @return the locale created from the canonical version of the ID.
487 public static ULocale createCanonical(String nonCanonicalID) {
488 return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
491 private static String lscvToID(String lang, String script, String country, String variant) {
492 StringBuilder buf = new StringBuilder();
494 if (lang != null && lang.length() > 0) {
497 if (script != null && script.length() > 0) {
498 buf.append(UNDERSCORE);
501 if (country != null && country.length() > 0) {
502 buf.append(UNDERSCORE);
505 if (variant != null && variant.length() > 0) {
506 if (country == null || country.length() == 0) {
507 buf.append(UNDERSCORE);
509 buf.append(UNDERSCORE);
512 return buf.toString();
516 * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
517 * @return a JDK locale that either exactly represents this object
518 * or is the closest approximation.
521 public Locale toLocale() {
522 if (locale == null) {
523 locale = JDKLocaleHelper.toLocale(this);
528 private static ICUCache<String, String> nameCache = new SimpleCache<String, String>();
531 * Keep our own default ULocale.
533 private static Locale defaultLocale = Locale.getDefault();
534 private static ULocale defaultULocale;
536 private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
537 private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
540 defaultULocale = forLocale(defaultLocale);
542 // For Java 6 or older JRE, ICU initializes the default script from
543 // "user.script" system property. The system property was added
544 // in Java 7. On JRE 7, Locale.getDefault() should reflect the
545 // property value to the Locale's default. So ICU just relies on
546 // Locale.getDefault().
548 // Note: The "user.script" property is only used by initialization.
550 if (JDKLocaleHelper.isJava7orNewer()) {
551 for (Category cat: Category.values()) {
552 int idx = cat.ordinal();
553 defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
554 defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
557 // Make sure the current default Locale is original.
558 // If not, it means that someone updated the default Locale.
559 // In this case, user.XXX properties are already out of date
560 // and we should not use user.script.
561 if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) {
562 // Use "user.script" if available
563 String userScript = JDKLocaleHelper.getSystemProperty("user.script");
564 if (userScript != null && LanguageTag.isScript(userScript)) {
565 // Note: Builder or forLanguageTag cannot be used here
566 // when one of Locale fields is not well-formed.
567 BaseLocale base = defaultULocale.base();
568 BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript,
569 base.getRegion(), base.getVariant());
570 defaultULocale = getInstance(newBase, defaultULocale.extensions());
574 // Java 6 or older does not have separated category locales,
575 // use the non-category default for all
576 for (Category cat: Category.values()) {
577 int idx = cat.ordinal();
578 defaultCategoryLocales[idx] = defaultLocale;
579 defaultCategoryULocales[idx] = defaultULocale;
585 * Returns the current default ULocale.
587 * The default ULocale is synchronized to the default Java Locale. This method checks
588 * the current default Java Locale and returns an equivalent ULocale.
590 * <b>Note:</b> Before Java 7, the JDK Locale was not able to represent a locale's script.
591 * Therefore, the script field in the default ULocale is always empty unless
592 * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)}
593 * on Java 6 or older systems.
595 * <b>Note for ICU 49 or later:</b> Some JRE implementations allow users to override the default
596 * JDK Locale using system properties - <code>user.language</code>, <code>user.country</code>
597 * and <code>user.variant</code>. In addition to these system properties, some Java 7
598 * implementations support <code>user.script</code> for overriding the default Locale's script.
599 * ICU 49 and later versions use the <code>user.script</code> system property on Java 6
600 * or older systems supporting other <code>user.*</code> system properties to initialize
601 * the default ULocale. The <code>user.script</code> override for default ULocale is not
602 * used on Java 7, or if the current Java default Locale is changed after start up.
604 * @return the default ULocale.
607 public static ULocale getDefault() {
608 synchronized (ULocale.class) {
609 if (defaultULocale == null) {
610 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
611 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
612 // stored in a resource bundle. However, UResourceBundle currently requires
613 // non-null default ULocale. For now, this implementation returns ULocale.ROOT
614 // to avoid the problem.
616 // TODO: Consider moving BCP47 mapping data out of resource bundle later.
620 Locale currentDefault = Locale.getDefault();
621 if (!defaultLocale.equals(currentDefault)) {
622 defaultLocale = currentDefault;
623 defaultULocale = forLocale(currentDefault);
625 if (!JDKLocaleHelper.isJava7orNewer()) {
626 // Detected Java default Locale change.
627 // We need to update category defaults to match the
628 // Java 7's behavior on Java 6 or older environment.
629 for (Category cat : Category.values()) {
630 int idx = cat.ordinal();
631 defaultCategoryLocales[idx] = currentDefault;
632 defaultCategoryULocales[idx] = forLocale(currentDefault);
636 return defaultULocale;
641 * {@icu} Sets the default ULocale. This also sets the default Locale.
642 * If the caller does not have write permission to the
643 * user.language property, a security exception will be thrown,
644 * and the default ULocale will remain unchanged.
646 * By setting the default ULocale with this method, all of the default categoy locales
647 * are also set to the specified default ULocale.
648 * @param newLocale the new default locale
649 * @throws SecurityException if a security manager exists and its
650 * <code>checkPermission</code> method doesn't allow the operation.
651 * @throws NullPointerException if <code>newLocale</code> is null
652 * @see SecurityManager#checkPermission(java.security.Permission)
653 * @see java.util.PropertyPermission
654 * @see ULocale#setDefault(Category, ULocale)
657 public static synchronized void setDefault(ULocale newLocale){
658 defaultLocale = newLocale.toLocale();
659 Locale.setDefault(defaultLocale);
660 defaultULocale = newLocale;
661 // This method also updates all category default locales
662 for (Category cat : Category.values()) {
663 setDefault(cat, newLocale);
668 * Returns the current default ULocale for the specified category.
670 * @param category the category
671 * @return the default ULocale for the specified category.
674 public static ULocale getDefault(Category category) {
675 synchronized (ULocale.class) {
676 int idx = category.ordinal();
677 if (defaultCategoryULocales[idx] == null) {
678 // Just in case this method is called during ULocale class
679 // initialization. Unlike getDefault(), we do not have
680 // cyclic dependency for category default.
683 if (JDKLocaleHelper.isJava7orNewer()) {
684 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
685 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
686 defaultCategoryLocales[idx] = currentCategoryDefault;
687 defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
690 // java.util.Locale.setDefault(Locale) in Java 7 updates
691 // category locale defaults. On Java 6 or older environment,
692 // ICU4J checks if the default locale has changed and update
693 // category ULocales here if necessary.
695 // Note: When java.util.Locale.setDefault(Locale) is called
696 // with a Locale same with the previous one, Java 7 still
697 // updates category locale defaults. On Java 6 or older env,
698 // there is no good way to detect the event, ICU4J simply
699 // check if the default Java Locale has changed since last
702 Locale currentDefault = Locale.getDefault();
703 if (!defaultLocale.equals(currentDefault)) {
704 defaultLocale = currentDefault;
705 defaultULocale = forLocale(currentDefault);
707 for (Category cat : Category.values()) {
708 int tmpIdx = cat.ordinal();
709 defaultCategoryLocales[tmpIdx] = currentDefault;
710 defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
714 // No synchronization with JDK Locale, because category default
715 // is not supported in Java 6 or older versions
717 return defaultCategoryULocales[idx];
722 * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
723 * This also sets the default <code>Locale</code> for the specified <code>Category</code>
724 * of the JVM. If the caller does not have write permission to the
725 * user.language property, a security exception will be thrown,
726 * and the default ULocale for the specified Category will remain unchanged.
728 * @param category the specified category to set the default locale
729 * @param newLocale the new default locale
730 * @see SecurityManager#checkPermission(java.security.Permission)
731 * @see java.util.PropertyPermission
734 public static synchronized void setDefault(Category category, ULocale newLocale) {
735 Locale newJavaDefault = newLocale.toLocale();
736 int idx = category.ordinal();
737 defaultCategoryULocales[idx] = newLocale;
738 defaultCategoryLocales[idx] = newJavaDefault;
739 JDKLocaleHelper.setDefault(category, newJavaDefault);
743 * This is for compatibility with Locale-- in actuality, since ULocale is
744 * immutable, there is no reason to clone it, so this API returns 'this'.
747 public Object clone() {
752 * Returns the hashCode.
755 public int hashCode() {
756 return localeID.hashCode();
760 * Returns true if the other object is another ULocale with the
762 * Note that since names are not canonicalized, two ULocales that
763 * function identically might not compare equal.
765 * @return true if this Locale is equal to the specified object.
768 public boolean equals(Object obj) {
772 if (obj instanceof ULocale) {
773 return localeID.equals(((ULocale)obj).localeID);
779 * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
780 * not <code>Locale</code>. Returns a list of all installed locales.
783 public static ULocale[] getAvailableLocales() {
784 return ICUResourceBundle.getAvailableULocales();
788 * Returns a list of all 2-letter country codes defined in ISO 3166.
789 * Can be used to create Locales.
792 public static String[] getISOCountries() {
793 return LocaleIDs.getISOCountries();
797 * Returns a list of all 2-letter language codes defined in ISO 639.
798 * Can be used to create Locales.
799 * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
800 * The list this function returns includes both the new and the old codes for the
801 * languages whose codes have changed.]
804 public static String[] getISOLanguages() {
805 return LocaleIDs.getISOLanguages();
809 * Returns the language code for this locale, which will either be the empty string
810 * or a lowercase ISO 639 code.
811 * @see #getDisplayLanguage()
812 * @see #getDisplayLanguage(ULocale)
815 public String getLanguage() {
816 return getLanguage(localeID);
820 * Returns the language code for the locale ID,
821 * which will either be the empty string
822 * or a lowercase ISO 639 code.
823 * @see #getDisplayLanguage()
824 * @see #getDisplayLanguage(ULocale)
827 public static String getLanguage(String localeID) {
828 return new LocaleIDParser(localeID).getLanguage();
832 * {@icu} Returns the script code for this locale, which might be the empty string.
833 * @see #getDisplayScript()
834 * @see #getDisplayScript(ULocale)
837 public String getScript() {
838 return getScript(localeID);
842 * {@icu} Returns the script code for the specified locale, which might be the empty
844 * @see #getDisplayScript()
845 * @see #getDisplayScript(ULocale)
848 public static String getScript(String localeID) {
849 return new LocaleIDParser(localeID).getScript();
853 * Returns the country/region code for this locale, which will either be the empty string
854 * or an uppercase ISO 3166 2-letter code.
855 * @see #getDisplayCountry()
856 * @see #getDisplayCountry(ULocale)
859 public String getCountry() {
860 return getCountry(localeID);
864 * Returns the country/region code for this locale, which will either be the empty string
865 * or an uppercase ISO 3166 2-letter code.
866 * @param localeID The locale identification string.
867 * @see #getDisplayCountry()
868 * @see #getDisplayCountry(ULocale)
871 public static String getCountry(String localeID) {
872 return new LocaleIDParser(localeID).getCountry();
876 * Returns the variant code for this locale, which might be the empty string.
877 * @see #getDisplayVariant()
878 * @see #getDisplayVariant(ULocale)
881 public String getVariant() {
882 return getVariant(localeID);
886 * Returns the variant code for the specified locale, which might be the empty string.
887 * @see #getDisplayVariant()
888 * @see #getDisplayVariant(ULocale)
891 public static String getVariant(String localeID) {
892 return new LocaleIDParser(localeID).getVariant();
896 * {@icu} Returns the fallback locale for the specified locale, which might be the
900 public static String getFallback(String localeID) {
901 return getFallbackString(getName(localeID));
905 * {@icu} Returns the fallback locale for this locale. If this locale is root,
909 public ULocale getFallback() {
910 if (localeID.length() == 0 || localeID.charAt(0) == '@') {
913 return new ULocale(getFallbackString(localeID), (Locale)null);
917 * Returns the given (canonical) locale id minus the last part before the tags.
919 private static String getFallbackString(String fallback) {
920 int extStart = fallback.indexOf('@');
921 if (extStart == -1) {
922 extStart = fallback.length();
924 int last = fallback.lastIndexOf('_', extStart);
928 // truncate empty segment
930 if (fallback.charAt(last - 1) != '_') {
936 return fallback.substring(0, last) + fallback.substring(extStart);
940 * {@icu} Returns the (normalized) base name for this locale,
941 * like {@link #getName()}, but without keywords.
943 * @return the base name as a String.
946 public String getBaseName() {
947 return getBaseName(localeID);
951 * {@icu} Returns the (normalized) base name for the specified locale,
952 * like {@link #getName(String)}, but without keywords.
954 * @param localeID the locale ID as a string
955 * @return the base name as a String.
958 public static String getBaseName(String localeID){
959 if (localeID.indexOf('@') == -1) {
962 return new LocaleIDParser(localeID).getBaseName();
966 * {@icu} Returns the (normalized) full name for this locale.
968 * @return String the full name of the localeID
971 public String getName() {
972 return localeID; // always normalized
976 * Gets the shortest length subtag's size.
979 * @return The size of the shortest length subtag
981 private static int getShortestSubtagLength(String localeID) {
982 int localeIDLength = localeID.length();
983 int length = localeIDLength;
984 boolean reset = true;
987 for (int i = 0; i < localeIDLength; i++) {
988 if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
995 if (tmpLength != 0 && tmpLength < length) {
1006 * {@icu} Returns the (normalized) full name for the specified locale.
1008 * @param localeID the localeID as a string
1009 * @return String the full name of the localeID
1012 public static String getName(String localeID){
1014 // Convert BCP47 id if necessary
1015 if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
1016 tmpLocaleID = forLanguageTag(localeID).getName();
1017 if (tmpLocaleID.length() == 0) {
1018 tmpLocaleID = localeID;
1021 tmpLocaleID = localeID;
1023 String name = nameCache.get(tmpLocaleID);
1025 name = new LocaleIDParser(tmpLocaleID).getName();
1026 nameCache.put(tmpLocaleID, name);
1032 * Returns a string representation of this object.
1035 public String toString() {
1040 * {@icu} Returns an iterator over keywords for this locale. If there
1041 * are no keywords, returns null.
1042 * @return iterator over keywords, or null if there are no keywords.
1045 public Iterator<String> getKeywords() {
1046 return getKeywords(localeID);
1050 * {@icu} Returns an iterator over keywords for the specified locale. If there
1051 * are no keywords, returns null.
1052 * @return an iterator over the keywords in the specified locale, or null
1053 * if there are no keywords.
1056 public static Iterator<String> getKeywords(String localeID){
1057 return new LocaleIDParser(localeID).getKeywords();
1061 * {@icu} Returns the value for a keyword in this locale. If the keyword is not
1062 * defined, returns null.
1063 * @param keywordName name of the keyword whose value is desired. Case insensitive.
1064 * @return the value of the keyword, or null.
1067 public String getKeywordValue(String keywordName){
1068 return getKeywordValue(localeID, keywordName);
1072 * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
1073 * not defined, returns null. The locale name does not need to be normalized.
1074 * @param keywordName name of the keyword whose value is desired. Case insensitive.
1075 * @return String the value of the keyword as a string
1078 public static String getKeywordValue(String localeID, String keywordName) {
1079 return new LocaleIDParser(localeID).getKeywordValue(keywordName);
1083 * {@icu} Returns the canonical name for the specified locale ID. This is used to
1084 * convert POSIX and other grandfathered IDs to standard ICU form.
1085 * @param localeID the locale id
1086 * @return the canonicalized id
1089 public static String canonicalize(String localeID){
1090 LocaleIDParser parser = new LocaleIDParser(localeID, true);
1091 String baseName = parser.getBaseName();
1092 boolean foundVariant = false;
1094 // formerly, we always set to en_US_POSIX if the basename was empty, but
1095 // now we require that the entire id be empty, so that "@foo=bar"
1096 // will pass through unchanged.
1097 // {dlf} I'd rather keep "" unchanged.
1098 if (localeID.equals("")) {
1100 // return "en_US_POSIX";
1103 // we have an ID in the form xx_Yyyy_ZZ_KKKKK
1105 initCANONICALIZE_MAP();
1107 /* convert the variants to appropriate ID */
1108 for (int i = 0; i < variantsToKeywords.length; i++) {
1109 String[] vals = variantsToKeywords[i];
1110 int idx = baseName.lastIndexOf("_" + vals[0]);
1112 foundVariant = true;
1114 baseName = baseName.substring(0, idx);
1115 if (baseName.endsWith("_")) {
1116 baseName = baseName.substring(0, --idx);
1118 parser.setBaseName(baseName);
1119 parser.defaultKeywordValue(vals[1], vals[2]);
1124 /* See if this is an already known locale */
1125 for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
1126 if (CANONICALIZE_MAP[i][0].equals(baseName)) {
1127 foundVariant = true;
1129 String[] vals = CANONICALIZE_MAP[i];
1130 parser.setBaseName(vals[1]);
1131 if (vals[2] != null) {
1132 parser.defaultKeywordValue(vals[2], vals[3]);
1138 /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
1139 if (!foundVariant) {
1140 if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
1141 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
1145 return parser.getName();
1149 * Given a keyword and a value, return a new locale with an updated
1150 * keyword and value. If the keyword is null, this removes all keywords from the locale id.
1151 * Otherwise, if the value is null, this removes the value for this keyword from the
1152 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
1153 * The keyword and value must not be empty.
1155 * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed.
1157 * @param keyword the keyword to add/remove, or null to remove all keywords.
1158 * @param value the value to add/set, or null to remove this particular keyword.
1159 * @return the updated locale
1162 public ULocale setKeywordValue(String keyword, String value) {
1163 return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
1167 * Given a locale id, a keyword, and a value, return a new locale id with an updated
1168 * keyword and value. If the keyword is null, this removes all keywords from the locale id.
1169 * Otherwise, if the value is null, this removes the value for this keyword from the
1170 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
1171 * The keyword and value must not be empty.
1173 * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed.
1175 * @param localeID the locale id to modify
1176 * @param keyword the keyword to add/remove, or null to remove all keywords.
1177 * @param value the value to add/set, or null to remove this particular keyword.
1178 * @return the updated locale id
1181 public static String setKeywordValue(String localeID, String keyword, String value) {
1182 LocaleIDParser parser = new LocaleIDParser(localeID);
1183 parser.setKeywordValue(keyword, value);
1184 return parser.getName();
1188 * Given a locale id, a keyword, and a value, return a new locale id with an updated
1189 * keyword and value, if the keyword does not already have a value. The keyword and
1190 * value must not be null or empty.
1191 * @param localeID the locale id to modify
1192 * @param keyword the keyword to add, if not already present
1193 * @param value the value to add, if not already present
1194 * @return the updated locale id
1196 /* private static String defaultKeywordValue(String localeID, String keyword, String value) {
1197 LocaleIDParser parser = new LocaleIDParser(localeID);
1198 parser.defaultKeywordValue(keyword, value);
1199 return parser.getName();
1203 * Returns a three-letter abbreviation for this locale's language. If the locale
1204 * doesn't specify a language, returns the empty string. Otherwise, returns
1205 * a lowercase ISO 639-2/T language code.
1206 * The ISO 639-2 language codes can be found on-line at
1207 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
1208 * @exception MissingResourceException Throws MissingResourceException if the
1209 * three-letter language abbreviation is not available for this locale.
1212 public String getISO3Language(){
1213 return getISO3Language(localeID);
1217 * Returns a three-letter abbreviation for this locale's language. If the locale
1218 * doesn't specify a language, returns the empty string. Otherwise, returns
1219 * a lowercase ISO 639-2/T language code.
1220 * The ISO 639-2 language codes can be found on-line at
1221 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
1222 * @exception MissingResourceException Throws MissingResourceException if the
1223 * three-letter language abbreviation is not available for this locale.
1226 public static String getISO3Language(String localeID) {
1227 return LocaleIDs.getISO3Language(getLanguage(localeID));
1231 * Returns a three-letter abbreviation for this locale's country/region. If the locale
1232 * doesn't specify a country, returns the empty string. Otherwise, returns
1233 * an uppercase ISO 3166 3-letter country code.
1234 * @exception MissingResourceException Throws MissingResourceException if the
1235 * three-letter country abbreviation is not available for this locale.
1238 public String getISO3Country() {
1239 return getISO3Country(localeID);
1243 * Returns a three-letter abbreviation for this locale's country/region. If the locale
1244 * doesn't specify a country, returns the empty string. Otherwise, returns
1245 * an uppercase ISO 3166 3-letter country code.
1246 * @exception MissingResourceException Throws MissingResourceException if the
1247 * three-letter country abbreviation is not available for this locale.
1250 public static String getISO3Country(String localeID) {
1251 return LocaleIDs.getISO3Country(getCountry(localeID));
1257 * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
1258 * @return the localized language name.
1259 * @see Category#DISPLAY
1262 public String getDisplayLanguage() {
1263 return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
1267 * {@icu} Returns this locale's language localized for display in the provided locale.
1268 * @param displayLocale the locale in which to display the name.
1269 * @return the localized language name.
1272 public String getDisplayLanguage(ULocale displayLocale) {
1273 return getDisplayLanguageInternal(this, displayLocale, false);
1277 * Returns a locale's language localized for display in the provided locale.
1278 * This is a cover for the ICU4C API.
1279 * @param localeID the id of the locale whose language will be displayed
1280 * @param displayLocaleID the id of the locale in which to display the name.
1281 * @return the localized language name.
1284 public static String getDisplayLanguage(String localeID, String displayLocaleID) {
1285 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
1290 * Returns a locale's language localized for display in the provided locale.
1291 * This is a cover for the ICU4C API.
1292 * @param localeID the id of the locale whose language will be displayed.
1293 * @param displayLocale the locale in which to display the name.
1294 * @return the localized language name.
1297 public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
1298 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
1301 * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
1302 * If a dialect name is present in the data, then it is returned.
1303 * @return the localized language name.
1304 * @see Category#DISPLAY
1307 public String getDisplayLanguageWithDialect() {
1308 return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
1312 * {@icu} Returns this locale's language localized for display in the provided locale.
1313 * If a dialect name is present in the data, then it is returned.
1314 * @param displayLocale the locale in which to display the name.
1315 * @return the localized language name.
1318 public String getDisplayLanguageWithDialect(ULocale displayLocale) {
1319 return getDisplayLanguageInternal(this, displayLocale, true);
1323 * {@icu} Returns a locale's language localized for display in the provided locale.
1324 * If a dialect name is present in the data, then it is returned.
1325 * This is a cover for the ICU4C API.
1326 * @param localeID the id of the locale whose language will be displayed
1327 * @param displayLocaleID the id of the locale in which to display the name.
1328 * @return the localized language name.
1331 public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
1332 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
1337 * {@icu} Returns a locale's language localized for display in the provided locale.
1338 * If a dialect name is present in the data, then it is returned.
1339 * This is a cover for the ICU4C API.
1340 * @param localeID the id of the locale whose language will be displayed.
1341 * @param displayLocale the locale in which to display the name.
1342 * @return the localized language name.
1345 public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
1346 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
1349 private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
1350 boolean useDialect) {
1351 String lang = useDialect ? locale.getBaseName() : locale.getLanguage();
1352 return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang);
1356 * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
1357 * @return the localized script name.
1358 * @see Category#DISPLAY
1361 public String getDisplayScript() {
1362 return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
1366 * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
1367 * @return the localized script name.
1368 * @see Category#DISPLAY
1370 * @deprecated This API is ICU internal only.
1372 public String getDisplayScriptInContext() {
1373 return getDisplayScriptInContextInternal(this, getDefault(Category.DISPLAY));
1377 * {@icu} Returns this locale's script localized for display in the provided locale.
1378 * @param displayLocale the locale in which to display the name.
1379 * @return the localized script name.
1382 public String getDisplayScript(ULocale displayLocale) {
1383 return getDisplayScriptInternal(this, displayLocale);
1387 * {@icu} Returns this locale's script localized for display in the provided locale.
1388 * @param displayLocale the locale in which to display the name.
1389 * @return the localized script name.
1391 * @deprecated This API is ICU internal only.
1393 public String getDisplayScriptInContext(ULocale displayLocale) {
1394 return getDisplayScriptInContextInternal(this, displayLocale);
1398 * {@icu} Returns a locale's script localized for display in the provided locale.
1399 * This is a cover for the ICU4C API.
1400 * @param localeID the id of the locale whose script will be displayed
1401 * @param displayLocaleID the id of the locale in which to display the name.
1402 * @return the localized script name.
1405 public static String getDisplayScript(String localeID, String displayLocaleID) {
1406 return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1409 * {@icu} Returns a locale's script localized for display in the provided locale.
1410 * This is a cover for the ICU4C API.
1411 * @param localeID the id of the locale whose script will be displayed
1412 * @param displayLocaleID the id of the locale in which to display the name.
1413 * @return the localized script name.
1415 * @deprecated This API is ICU internal only.
1417 public static String getDisplayScriptInContext(String localeID, String displayLocaleID) {
1418 return getDisplayScriptInContextInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1422 * {@icu} Returns a locale's script localized for display in the provided locale.
1423 * @param localeID the id of the locale whose script will be displayed.
1424 * @param displayLocale the locale in which to display the name.
1425 * @return the localized script name.
1428 public static String getDisplayScript(String localeID, ULocale displayLocale) {
1429 return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
1432 * {@icu} Returns a locale's script localized for display in the provided locale.
1433 * @param localeID the id of the locale whose script will be displayed.
1434 * @param displayLocale the locale in which to display the name.
1435 * @return the localized script name.
1437 * @deprecated This API is ICU internal only.
1439 public static String getDisplayScriptInContext(String localeID, ULocale displayLocale) {
1440 return getDisplayScriptInContextInternal(new ULocale(localeID), displayLocale);
1443 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
1444 private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
1445 return LocaleDisplayNames.getInstance(displayLocale)
1446 .scriptDisplayName(locale.getScript());
1449 private static String getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale) {
1450 return LocaleDisplayNames.getInstance(displayLocale)
1451 .scriptDisplayNameInContext(locale.getScript());
1455 * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
1456 * @return the localized country name.
1457 * @see Category#DISPLAY
1460 public String getDisplayCountry() {
1461 return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
1465 * Returns this locale's country localized for display in the provided locale.
1466 * @param displayLocale the locale in which to display the name.
1467 * @return the localized country name.
1470 public String getDisplayCountry(ULocale displayLocale){
1471 return getDisplayCountryInternal(this, displayLocale);
1475 * Returns a locale's country localized for display in the provided locale.
1476 * This is a cover for the ICU4C API.
1477 * @param localeID the id of the locale whose country will be displayed
1478 * @param displayLocaleID the id of the locale in which to display the name.
1479 * @return the localized country name.
1482 public static String getDisplayCountry(String localeID, String displayLocaleID) {
1483 return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1487 * Returns a locale's country localized for display in the provided locale.
1488 * This is a cover for the ICU4C API.
1489 * @param localeID the id of the locale whose country will be displayed.
1490 * @param displayLocale the locale in which to display the name.
1491 * @return the localized country name.
1494 public static String getDisplayCountry(String localeID, ULocale displayLocale) {
1495 return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
1498 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
1499 private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
1500 return LocaleDisplayNames.getInstance(displayLocale)
1501 .regionDisplayName(locale.getCountry());
1505 * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
1506 * @return the localized variant name.
1507 * @see Category#DISPLAY
1510 public String getDisplayVariant() {
1511 return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
1515 * Returns this locale's variant localized for display in the provided locale.
1516 * @param displayLocale the locale in which to display the name.
1517 * @return the localized variant name.
1520 public String getDisplayVariant(ULocale displayLocale) {
1521 return getDisplayVariantInternal(this, displayLocale);
1525 * Returns a locale's variant localized for display in the provided locale.
1526 * This is a cover for the ICU4C API.
1527 * @param localeID the id of the locale whose variant will be displayed
1528 * @param displayLocaleID the id of the locale in which to display the name.
1529 * @return the localized variant name.
1532 public static String getDisplayVariant(String localeID, String displayLocaleID){
1533 return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1537 * Returns a locale's variant localized for display in the provided locale.
1538 * This is a cover for the ICU4C API.
1539 * @param localeID the id of the locale whose variant will be displayed.
1540 * @param displayLocale the locale in which to display the name.
1541 * @return the localized variant name.
1544 public static String getDisplayVariant(String localeID, ULocale displayLocale) {
1545 return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
1548 private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
1549 return LocaleDisplayNames.getInstance(displayLocale)
1550 .variantDisplayName(locale.getVariant());
1554 * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
1555 * @param keyword the keyword to be displayed.
1556 * @return the localized keyword name.
1557 * @see #getKeywords()
1558 * @see Category#DISPLAY
1561 public static String getDisplayKeyword(String keyword) {
1562 return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
1566 * {@icu} Returns a keyword localized for display in the specified locale.
1567 * @param keyword the keyword to be displayed.
1568 * @param displayLocaleID the id of the locale in which to display the keyword.
1569 * @return the localized keyword name.
1570 * @see #getKeywords(String)
1573 public static String getDisplayKeyword(String keyword, String displayLocaleID) {
1574 return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
1578 * {@icu} Returns a keyword localized for display in the specified locale.
1579 * @param keyword the keyword to be displayed.
1580 * @param displayLocale the locale in which to display the keyword.
1581 * @return the localized keyword name.
1582 * @see #getKeywords(String)
1585 public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
1586 return getDisplayKeywordInternal(keyword, displayLocale);
1589 private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
1590 return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword);
1594 * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
1595 * @param keyword the keyword whose value is to be displayed.
1596 * @return the localized value name.
1597 * @see Category#DISPLAY
1600 public String getDisplayKeywordValue(String keyword) {
1601 return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
1605 * {@icu} Returns a keyword value localized for display in the specified locale.
1606 * @param keyword the keyword whose value is to be displayed.
1607 * @param displayLocale the locale in which to display the value.
1608 * @return the localized value name.
1611 public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
1612 return getDisplayKeywordValueInternal(this, keyword, displayLocale);
1616 * {@icu} Returns a keyword value localized for display in the specified locale.
1617 * This is a cover for the ICU4C API.
1618 * @param localeID the id of the locale whose keyword value is to be displayed.
1619 * @param keyword the keyword whose value is to be displayed.
1620 * @param displayLocaleID the id of the locale in which to display the value.
1621 * @return the localized value name.
1624 public static String getDisplayKeywordValue(String localeID, String keyword,
1625 String displayLocaleID) {
1626 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
1627 new ULocale(displayLocaleID));
1631 * {@icu} Returns a keyword value localized for display in the specified locale.
1632 * This is a cover for the ICU4C API.
1633 * @param localeID the id of the locale whose keyword value is to be displayed.
1634 * @param keyword the keyword whose value is to be displayed.
1635 * @param displayLocale the id of the locale in which to display the value.
1636 * @return the localized value name.
1639 public static String getDisplayKeywordValue(String localeID, String keyword,
1640 ULocale displayLocale) {
1641 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
1644 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
1645 private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
1646 ULocale displayLocale) {
1647 keyword = AsciiUtil.toLowerString(keyword.trim());
1648 String value = locale.getKeywordValue(keyword);
1649 return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value);
1653 * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
1654 * @return the localized locale name.
1655 * @see Category#DISPLAY
1658 public String getDisplayName() {
1659 return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
1663 * Returns this locale name localized for display in the provided locale.
1664 * @param displayLocale the locale in which to display the locale name.
1665 * @return the localized locale name.
1668 public String getDisplayName(ULocale displayLocale) {
1669 return getDisplayNameInternal(this, displayLocale);
1673 * Returns the locale ID localized for display in the provided locale.
1674 * This is a cover for the ICU4C API.
1675 * @param localeID the locale whose name is to be displayed.
1676 * @param displayLocaleID the id of the locale in which to display the locale name.
1677 * @return the localized locale name.
1680 public static String getDisplayName(String localeID, String displayLocaleID) {
1681 return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1685 * Returns the locale ID localized for display in the provided locale.
1686 * This is a cover for the ICU4C API.
1687 * @param localeID the locale whose name is to be displayed.
1688 * @param displayLocale the locale in which to display the locale name.
1689 * @return the localized locale name.
1692 public static String getDisplayName(String localeID, ULocale displayLocale) {
1693 return getDisplayNameInternal(new ULocale(localeID), displayLocale);
1696 private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
1697 return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale);
1701 * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
1702 * If a dialect name is present in the locale data, then it is returned.
1703 * @return the localized locale name.
1704 * @see Category#DISPLAY
1707 public String getDisplayNameWithDialect() {
1708 return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
1712 * {@icu} Returns this locale name localized for display in the provided locale.
1713 * If a dialect name is present in the locale data, then it is returned.
1714 * @param displayLocale the locale in which to display the locale name.
1715 * @return the localized locale name.
1718 public String getDisplayNameWithDialect(ULocale displayLocale) {
1719 return getDisplayNameWithDialectInternal(this, displayLocale);
1723 * {@icu} Returns the locale ID localized for display in the provided locale.
1724 * If a dialect name is present in the locale data, then it is returned.
1725 * This is a cover for the ICU4C API.
1726 * @param localeID the locale whose name is to be displayed.
1727 * @param displayLocaleID the id of the locale in which to display the locale name.
1728 * @return the localized locale name.
1731 public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
1732 return getDisplayNameWithDialectInternal(new ULocale(localeID),
1733 new ULocale(displayLocaleID));
1737 * {@icu} Returns the locale ID localized for display in the provided locale.
1738 * If a dialect name is present in the locale data, then it is returned.
1739 * This is a cover for the ICU4C API.
1740 * @param localeID the locale whose name is to be displayed.
1741 * @param displayLocale the locale in which to display the locale name.
1742 * @return the localized locale name.
1745 public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
1746 return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
1749 private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
1750 return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES)
1751 .localeDisplayName(locale);
1755 * {@icu} Returns this locale's layout orientation for characters. The possible
1756 * values are "left-to-right", "right-to-left", "top-to-bottom" or
1758 * @return The locale's layout orientation for characters.
1761 public String getCharacterOrientation() {
1762 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
1763 "layout", "characters");
1767 * {@icu} Returns this locale's layout orientation for lines. The possible
1768 * values are "left-to-right", "right-to-left", "top-to-bottom" or
1770 * @return The locale's layout orientation for lines.
1773 public String getLineOrientation() {
1774 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
1779 * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
1780 * resource containing the data. This is always at or above the
1781 * valid locale. If the valid locale does not contain the
1782 * specific data being requested, then the actual locale will be
1783 * above the valid locale. If the object was not constructed from
1784 * locale data, then the valid locale is <i>null</i>.
1786 * @draft ICU 2.8 (retain)
1787 * @provisional This API might change or be removed in a future release.
1789 public static Type ACTUAL_LOCALE = new Type();
1792 * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
1793 * locale for which any data exists. This is always at or above
1794 * the requested locale, and at or below the actual locale. If
1795 * the requested locale does not correspond to any resource data,
1796 * then the valid locale will be above the requested locale. If
1797 * the object was not constructed from locale data, then the
1798 * actual locale is <i>null</i>.
1800 * <p>Note: The valid locale will be returned correctly in ICU
1801 * 3.0 or later. In ICU 2.8, it is not returned correctly.
1802 * @draft ICU 2.8 (retain)
1803 * @provisional This API might change or be removed in a future release.
1805 public static Type VALID_LOCALE = new Type();
1808 * Opaque selector enum for <tt>getLocale()</tt>.
1809 * @see com.ibm.icu.util.ULocale
1810 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1811 * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1812 * @draft ICU 2.8 (retainAll)
1813 * @provisional This API might change or be removed in a future release.
1815 public static final class Type {
1820 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
1821 * locale for the user. NullPointerException is thrown if acceptLanguageList or
1822 * availableLocales is null. If fallback is non-null, it will contain true if a
1823 * fallback locale (one not in the acceptLanguageList) was returned. The value on
1824 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
1825 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
1826 * availableLocales matched). No ULocale array element should be null; behavior is
1827 * undefined if this is the case.
1828 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
1829 * @param availableLocales list of available locales. One of these will be returned.
1830 * @param fallback if non-null, a 1-element array containing a boolean to be set with
1831 * the fallback status
1832 * @return one of the locales from the availableLocales list, or null if none match
1835 public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
1836 boolean[] fallback) {
1837 if (acceptLanguageList == null) {
1838 throw new NullPointerException();
1840 ULocale acceptList[] = null;
1842 acceptList = parseAcceptLanguage(acceptLanguageList, true);
1843 } catch (ParseException pe) {
1846 if (acceptList == null) {
1849 return acceptLanguage(acceptList, availableLocales, fallback);
1853 * {@icu} Based on a list of acceptable locales, determine an available locale for the
1854 * user. NullPointerException is thrown if acceptLanguageList or availableLocales is
1855 * null. If fallback is non-null, it will contain true if a fallback locale (one not
1856 * in the acceptLanguageList) was returned. The value on entry is ignored. ULocale
1857 * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
1858 * locale was used as a fallback (because nothing else in availableLocales matched).
1859 * No ULocale array element should be null; behavior is undefined if this is the case.
1860 * @param acceptLanguageList list of acceptable locales
1861 * @param availableLocales list of available locales. One of these will be returned.
1862 * @param fallback if non-null, a 1-element array containing a boolean to be set with
1863 * the fallback status
1864 * @return one of the locales from the availableLocales list, or null if none match
1868 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
1869 availableLocales, boolean[] fallback) {
1872 if(fallback != null) {
1875 for(i=0;i<acceptLanguageList.length;i++) {
1876 ULocale aLocale = acceptLanguageList[i];
1877 boolean[] setFallback = fallback;
1879 for(j=0;j<availableLocales.length;j++) {
1880 if(availableLocales[j].equals(aLocale)) {
1881 if(setFallback != null) {
1882 setFallback[0]=false; // first time with this locale - not a fallback.
1884 return availableLocales[j];
1886 // compare to scriptless alias, so locales such as
1887 // zh_TW, zh_CN are considered as available locales - see #7190
1888 if (aLocale.getScript().length() == 0
1889 && availableLocales[j].getScript().length() > 0
1890 && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
1891 && availableLocales[j].getCountry().equals(aLocale.getCountry())
1892 && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
1893 ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
1894 if (minAvail.getScript().length() == 0) {
1895 if(setFallback != null) {
1896 setFallback[0] = false; // not a fallback.
1902 Locale loc = aLocale.toLocale();
1903 Locale parent = LocaleUtility.fallback(loc);
1904 if(parent != null) {
1905 aLocale = new ULocale(parent);
1909 setFallback = null; // Do not set fallback in later iterations
1910 } while (aLocale != null);
1916 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
1917 * locale for the user. NullPointerException is thrown if acceptLanguageList or
1918 * availableLocales is null. If fallback is non-null, it will contain true if a
1919 * fallback locale (one not in the acceptLanguageList) was returned. The value on
1920 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
1921 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
1922 * availableLocales matched). No ULocale array element should be null; behavior is
1923 * undefined if this is the case. This function will choose a locale from the
1924 * ULocale.getAvailableLocales() list as available.
1925 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
1926 * @param fallback if non-null, a 1-element array containing a boolean to be set with
1927 * the fallback status
1928 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
1932 public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
1933 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
1938 * {@icu} Based on an ordered array of acceptable locales, determine an available
1939 * locale for the user. NullPointerException is thrown if acceptLanguageList or
1940 * availableLocales is null. If fallback is non-null, it will contain true if a
1941 * fallback locale (one not in the acceptLanguageList) was returned. The value on
1942 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
1943 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
1944 * availableLocales matched). No ULocale array element should be null; behavior is
1945 * undefined if this is the case. This function will choose a locale from the
1946 * ULocale.getAvailableLocales() list as available.
1947 * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
1948 * @param fallback if non-null, a 1-element array containing a boolean to be set with
1949 * the fallback status
1950 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
1953 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
1954 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
1959 * Package local method used for parsing Accept-Language string
1961 static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
1962 throws ParseException {
1963 class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
1965 private double serial;
1966 public ULocaleAcceptLanguageQ(double theq, int theserial) {
1970 public int compareTo(ULocaleAcceptLanguageQ other) {
1971 if (q > other.q) { // reverse - to sort in descending order
1973 } else if (q < other.q) {
1976 if (serial < other.serial) {
1978 } else if (serial > other.serial) {
1981 return 0; // same object
1986 // parse out the acceptLanguage into an array
1987 TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
1988 new TreeMap<ULocaleAcceptLanguageQ, ULocale>();
1989 StringBuilder languageRangeBuf = new StringBuilder();
1990 StringBuilder qvalBuf = new StringBuilder();
1992 acceptLanguage += ","; // append comma to simplify the parsing code
1994 boolean subTag = false;
1996 for (n = 0; n < acceptLanguage.length(); n++) {
1997 boolean gotLanguageQ = false;
1998 char c = acceptLanguage.charAt(n);
2000 case 0: // before language-range start
2001 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
2002 // in language-range
2003 languageRangeBuf.append(c);
2006 } else if (c == '*') {
2007 languageRangeBuf.append(c);
2009 } else if (c != ' ' && c != '\t') {
2010 // invalid character
2014 case 1: // in language-range
2015 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
2016 languageRangeBuf.append(c);
2017 } else if (c == '-') {
2019 languageRangeBuf.append(c);
2020 } else if (c == '_') {
2023 languageRangeBuf.append(c);
2027 } else if ('0' <= c && c <= '9') {
2029 languageRangeBuf.append(c);
2031 // DIGIT is allowed only in language sub tag
2034 } else if (c == ',') {
2036 gotLanguageQ = true;
2037 } else if (c == ' ' || c == '\t') {
2038 // language-range end
2040 } else if (c == ';') {
2044 // invalid character for language-range
2048 case 2: // saw wild card range
2051 gotLanguageQ = true;
2052 } else if (c == ' ' || c == '\t') {
2053 // language-range end
2055 } else if (c == ';') {
2063 case 3: // language-range end
2066 gotLanguageQ = true;
2067 } else if (c == ';') {
2070 } else if (c != ' ' && c != '\t') {
2079 } else if (c != ' ' && c != '\t') {
2084 case 5: // before equal
2088 } else if (c != ' ' && c != '\t') {
2093 case 6: // before q value
2095 // q value start with 0
2099 } else if (c == '1') {
2100 // q value start with 1
2103 } else if (c == '.') {
2110 } else if (c != ' ' && c != '\t') {
2115 case 7: // q value start
2117 // before q value fraction part
2120 } else if (c == ',') {
2122 gotLanguageQ = true;
2123 } else if (c == ' ' || c == '\t') {
2131 case 8: // before q value fraction part
2132 if ('0' <= c || c <= '9') {
2133 if (q1 && c != '0' && !isLenient) {
2134 // if q value starts with 1, the fraction part must be 0
2137 // in q value fraction part
2146 case 9: // in q value fraction part
2147 if ('0' <= c && c <= '9') {
2148 if (q1 && c != '0') {
2149 // if q value starts with 1, the fraction part must be 0
2154 } else if (c == ',') {
2156 gotLanguageQ = true;
2157 } else if (c == ' ' || c == '\t') {
2165 case 10: // after q value
2168 gotLanguageQ = true;
2169 } else if (c != ' ' && c != '\t') {
2177 throw new ParseException("Invalid Accept-Language", n);
2181 if (qvalBuf.length() != 0) {
2183 q = Double.parseDouble(qvalBuf.toString());
2184 } catch (NumberFormatException nfe) {
2185 // Already validated, so it should never happen
2192 if (languageRangeBuf.charAt(0) != '*') {
2193 int serial = map.size();
2194 ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
2195 // sort in reverse order.. 1.0, 0.9, 0.8 .. etc
2196 map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
2199 // reset buffer and parse state
2200 languageRangeBuf.setLength(0);
2201 qvalBuf.setLength(0);
2206 // Well, the parser should handle all cases. So just in case.
2207 throw new ParseException("Invalid AcceptlLanguage", n);
2211 ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
2215 private static final String UNDEFINED_LANGUAGE = "und";
2216 private static final String UNDEFINED_SCRIPT = "Zzzz";
2217 private static final String UNDEFINED_REGION = "ZZ";
2220 * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
2221 * described in the following CLDR technical report:
2223 * http://www.unicode.org/reports/tr35/#Likely_Subtags
2225 * If the provided ULocale instance is already in the maximal form, or there is no
2226 * data available available for maximization, it will be returned. For example,
2227 * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
2228 * Otherwise, a new ULocale instance with the maximal form is returned.
2232 * "en" maximizes to "en_Latn_US"
2234 * "de" maximizes to "de_Latn_US"
2236 * "sr" maximizes to "sr_Cyrl_RS"
2238 * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
2240 * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
2242 * @param loc The ULocale to maximize
2243 * @return The maximized ULocale instance.
2246 public static ULocale addLikelySubtags(ULocale loc) {
2247 String[] tags = new String[3];
2248 String trailing = null;
2250 int trailingIndex = parseTagString(
2254 if (trailingIndex < loc.localeID.length()) {
2255 trailing = loc.localeID.substring(trailingIndex);
2258 String newLocaleID =
2259 createLikelySubtagsString(
2265 return newLocaleID == null ? loc : new ULocale(newLocaleID);
2269 * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
2270 * in the following CLDR technical report:<blockquote>
2272 * <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
2273 *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
2275 * If the provided ULocale instance is already in the minimal form, or there
2276 * is no data available for minimization, it will be returned. Since the
2277 * minimization algorithm relies on proper maximization, see the comments
2278 * for addLikelySubtags for reasons why there might not be any data.
2282 * "en_Latn_US" minimizes to "en"
2284 * "de_Latn_US" minimizes to "de"
2286 * "sr_Cyrl_RS" minimizes to "sr"
2288 * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
2289 * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
2291 * @param loc The ULocale to minimize
2292 * @return The minimized ULocale instance.
2295 public static ULocale minimizeSubtags(ULocale loc) {
2296 String[] tags = new String[3];
2298 int trailingIndex = parseTagString(
2302 String originalLang = tags[0];
2303 String originalScript = tags[1];
2304 String originalRegion = tags[2];
2305 String originalTrailing = null;
2307 if (trailingIndex < loc.localeID.length()) {
2309 * Create a String that contains everything
2310 * after the language, script, and region.
2312 originalTrailing = loc.localeID.substring(trailingIndex);
2316 * First, we need to first get the maximization
2317 * by adding any likely subtags.
2319 String maximizedLocaleID =
2320 createLikelySubtagsString(
2327 * If maximization fails, there's nothing
2330 if (isEmptyString(maximizedLocaleID)) {
2335 * Start first with just the language.
2338 createLikelySubtagsString(
2344 if (tag.equals(maximizedLocaleID)) {
2345 String newLocaleID =
2352 return new ULocale(newLocaleID);
2357 * Next, try the language and region.
2359 if (originalRegion.length() != 0) {
2362 createLikelySubtagsString(
2368 if (tag.equals(maximizedLocaleID)) {
2369 String newLocaleID =
2376 return new ULocale(newLocaleID);
2381 * Finally, try the language and script. This is our last chance,
2382 * since trying with all three subtags would only yield the
2383 * maximal version that we already have.
2385 if (originalRegion.length() != 0 &&
2386 originalScript.length() != 0) {
2389 createLikelySubtagsString(
2395 if (tag.equals(maximizedLocaleID)) {
2396 String newLocaleID =
2403 return new ULocale(newLocaleID);
2411 * A trivial utility function that checks for a null
2412 * reference or checks the length of the supplied String.
2414 * @param string The string to check
2416 * @return true if the String is empty, or if the reference is null.
2418 private static boolean isEmptyString(String string) {
2419 return string == null || string.length() == 0;
2423 * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
2424 * not be a zero-length string.
2426 * @param tag The tag to add.
2427 * @param buffer The output buffer.
2429 private static void appendTag(String tag, StringBuilder buffer) {
2430 if (buffer.length() != 0) {
2431 buffer.append(UNDERSCORE);
2438 * Create a tag string from the supplied parameters. The lang, script and region
2439 * parameters may be null references.
2441 * If any of the language, script or region parameters are empty, and the alternateTags
2442 * parameter is not null, it will be parsed for potential language, script and region tags
2443 * to be used when constructing the new tag. If the alternateTags parameter is null, or
2444 * it contains no language tag, the default tag for the unknown language is used.
2446 * @param lang The language tag to use.
2447 * @param script The script tag to use.
2448 * @param region The region tag to use.
2449 * @param trailing Any trailing data to append to the new tag.
2450 * @param alternateTags A string containing any alternate tags.
2451 * @return The new tag string.
2453 private static String createTagString(String lang, String script, String region,
2454 String trailing, String alternateTags) {
2456 LocaleIDParser parser = null;
2457 boolean regionAppended = false;
2459 StringBuilder tag = new StringBuilder();
2461 if (!isEmptyString(lang)) {
2466 else if (isEmptyString(alternateTags)) {
2468 * Append the value for an unknown language, if
2469 * we found no language.
2476 parser = new LocaleIDParser(alternateTags);
2478 String alternateLang = parser.getLanguage();
2481 * Append the value for an unknown language, if
2482 * we found no language.
2485 !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
2489 if (!isEmptyString(script)) {
2494 else if (!isEmptyString(alternateTags)) {
2496 * Parse the alternateTags string for the script.
2498 if (parser == null) {
2499 parser = new LocaleIDParser(alternateTags);
2502 String alternateScript = parser.getScript();
2504 if (!isEmptyString(alternateScript)) {
2511 if (!isEmptyString(region)) {
2516 regionAppended = true;
2518 else if (!isEmptyString(alternateTags)) {
2520 * Parse the alternateTags string for the region.
2522 if (parser == null) {
2523 parser = new LocaleIDParser(alternateTags);
2526 String alternateRegion = parser.getCountry();
2528 if (!isEmptyString(alternateRegion)) {
2533 regionAppended = true;
2537 if (trailing != null && trailing.length() > 1) {
2539 * The current ICU format expects two underscores
2540 * will separate the variant from the preceeding
2541 * parts of the tag, if there is no region.
2545 if (trailing.charAt(0) == UNDERSCORE) {
2546 if (trailing.charAt(1) == UNDERSCORE) {
2554 if (regionAppended) {
2556 * If we appended a region, we may need to strip
2557 * the extra separator from the variant portion.
2559 if (separators == 2) {
2560 tag.append(trailing.substring(1));
2563 tag.append(trailing);
2568 * If we did not append a region, we may need to add
2569 * an extra separator to the variant portion.
2571 if (separators == 1) {
2572 tag.append(UNDERSCORE);
2574 tag.append(trailing);
2578 return tag.toString();
2582 * Create a tag string from the supplied parameters. The lang, script and region
2583 * parameters may be null references.If the lang parameter is an empty string, the
2584 * default value for an unknown language is written to the output buffer.
2586 * @param lang The language tag to use.
2587 * @param script The script tag to use.
2588 * @param region The region tag to use.
2589 * @param trailing Any trailing data to append to the new tag.
2590 * @return The new String.
2592 static String createTagString(String lang, String script, String region, String trailing) {
2593 return createTagString(lang, script, region, trailing, null);
2597 * Parse the language, script, and region subtags from a tag string, and return the results.
2599 * This function does not return the canonical strings for the unknown script and region.
2601 * @param localeID The locale ID to parse.
2602 * @param tags An array of three String references to return the subtag strings.
2603 * @return The number of chars of the localeID parameter consumed.
2605 private static int parseTagString(String localeID, String tags[]) {
2606 LocaleIDParser parser = new LocaleIDParser(localeID);
2608 String lang = parser.getLanguage();
2609 String script = parser.getScript();
2610 String region = parser.getCountry();
2612 if (isEmptyString(lang)) {
2613 tags[0] = UNDEFINED_LANGUAGE;
2619 if (script.equals(UNDEFINED_SCRIPT)) {
2626 if (region.equals(UNDEFINED_REGION)) {
2634 * Search for the variant. If there is one, then return the index of
2635 * the preceeding separator.
2636 * If there's no variant, search for the keyword delimiter,
2637 * and return its index. Otherwise, return the length of the
2640 * $TOTO(dbertoni) we need to take into account that we might
2641 * find a part of the language as the variant, since it can
2642 * can have a variant portion that is long enough to contain
2643 * the same characters as the variant.
2645 String variant = parser.getVariant();
2647 if (!isEmptyString(variant)){
2648 int index = localeID.indexOf(variant);
2651 return index > 0 ? index - 1 : index;
2655 int index = localeID.indexOf('@');
2657 return index == -1 ? localeID.length() : index;
2661 private static String lookupLikelySubtags(String localeId) {
2662 UResourceBundle bundle =
2663 UResourceBundle.getBundleInstance(
2664 ICUResourceBundle.ICU_BASE_NAME, "likelySubtags");
2666 return bundle.getString(localeId);
2668 catch(MissingResourceException e) {
2673 private static String createLikelySubtagsString(String lang, String script, String region,
2677 * Try the language with the script and region first.
2679 if (!isEmptyString(script) && !isEmptyString(region)) {
2688 String likelySubtags = lookupLikelySubtags(searchTag);
2691 if (likelySubtags == null) {
2692 if (likelySubtags2 != null) {
2693 System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
2696 else if (likelySubtags2 == null) {
2697 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
2699 else if (!likelySubtags.equals(likelySubtags2)) {
2700 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2
2704 if (likelySubtags != null) {
2705 // Always use the language tag from the
2706 // maximal string, since it may be more
2707 // specific than the one provided.
2708 return createTagString(
2718 * Try the language with just the script.
2720 if (!isEmptyString(script)) {
2729 String likelySubtags = lookupLikelySubtags(searchTag);
2730 if (likelySubtags != null) {
2731 // Always use the language tag from the
2732 // maximal string, since it may be more
2733 // specific than the one provided.
2734 return createTagString(
2744 * Try the language with just the region.
2746 if (!isEmptyString(region)) {
2755 String likelySubtags = lookupLikelySubtags(searchTag);
2757 if (likelySubtags != null) {
2758 // Always use the language tag from the
2759 // maximal string, since it may be more
2760 // specific than the one provided.
2761 return createTagString(
2771 * Finally, try just the language.
2781 String likelySubtags = lookupLikelySubtags(searchTag);
2783 if (likelySubtags != null) {
2784 // Always use the language tag from the
2785 // maximal string, since it may be more
2786 // specific than the one provided.
2787 return createTagString(
2799 // --------------------------------
2800 // BCP47/OpenJDK APIs
2801 // --------------------------------
2804 * {@icu} The key for the private use locale extension ('x').
2806 * @see #getExtension(char)
2807 * @see Builder#setExtension(char, String)
2811 public static final char PRIVATE_USE_EXTENSION = 'x';
2814 * {@icu} The key for Unicode locale extension ('u').
2816 * @see #getExtension(char)
2817 * @see Builder#setExtension(char, String)
2821 public static final char UNICODE_LOCALE_EXTENSION = 'u';
2824 * {@icu} Returns the extension (or private use) value associated with
2825 * the specified key, or null if there is no extension
2826 * associated with the key. To be well-formed, the key must be one
2827 * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
2828 * for example 'z' and 'Z' represent the same extension.
2830 * @param key the extension key
2831 * @return The extension, or null if this locale defines no
2832 * extension for the specified key.
2833 * @throws IllegalArgumentException if key is not well-formed
2834 * @see #PRIVATE_USE_EXTENSION
2835 * @see #UNICODE_LOCALE_EXTENSION
2839 public String getExtension(char key) {
2840 if (!LocaleExtensions.isValidKey(key)) {
2841 throw new IllegalArgumentException("Invalid extension key: " + key);
2843 return extensions().getExtensionValue(key);
2847 * {@icu} Returns the set of extension keys associated with this locale, or the
2848 * empty set if it has no extensions. The returned set is unmodifiable.
2849 * The keys will all be lower-case.
2851 * @return the set of extension keys, or the empty set if this locale has
2855 public Set<Character> getExtensionKeys() {
2856 return extensions().getKeys();
2860 * {@icu} Returns the set of unicode locale attributes associated with
2861 * this locale, or the empty set if it has no attributes. The
2862 * returned set is unmodifiable.
2864 * @return The set of attributes.
2867 public Set<String> getUnicodeLocaleAttributes() {
2868 return extensions().getUnicodeLocaleAttributes();
2872 * {@icu} Returns the Unicode locale type associated with the specified Unicode locale key
2873 * for this locale. Returns the empty string for keys that are defined with no type.
2874 * Returns null if the key is not defined. Keys are case-insensitive. The key must
2875 * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
2878 * @param key the Unicode locale key
2879 * @return The Unicode locale type associated with the key, or null if the
2880 * locale does not define the key.
2881 * @throws IllegalArgumentException if the key is not well-formed
2882 * @throws NullPointerException if <code>key</code> is null
2886 public String getUnicodeLocaleType(String key) {
2887 if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
2888 throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
2890 return extensions().getUnicodeLocaleType(key);
2894 * {@icu} Returns the set of Unicode locale keys defined by this locale, or the empty set if
2895 * this locale has none. The returned set is immutable. Keys are all lower case.
2897 * @return The set of Unicode locale keys, or the empty set if this locale has
2898 * no Unicode locale keywords.
2902 public Set<String> getUnicodeLocaleKeys() {
2903 return extensions().getUnicodeLocaleKeys();
2907 * {@icu} Returns a well-formed IETF BCP 47 language tag representing
2910 * <p>If this <code>ULocale</code> has a language, script, country, or
2911 * variant that does not satisfy the IETF BCP 47 language tag
2912 * syntax requirements, this method handles these fields as
2915 * <p><b>Language:</b> If language is empty, or not well-formed
2916 * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
2918 * <p><b>Script:</b> If script is not well-formed (for example "12"
2919 * or "Latin"), it will be omitted.
2921 * <p><b>Country:</b> If country is not well-formed (for example "12"
2922 * or "USA"), it will be omitted.
2924 * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
2925 * (delimited by '-' or '_') is emitted as a subtag. Otherwise:
2928 * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
2929 * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
2930 * ill-formed sub-segment and all following will be appended to
2931 * the private use subtag. The first appended subtag will be
2932 * "lvariant", followed by the sub-segments in order, separated by
2933 * hyphen. For example, "x-lvariant-WIN",
2934 * "Oracle-x-lvariant-JDK-Standard-Edition".
2936 * <li>if any sub-segment does not match
2937 * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
2938 * and the problematic sub-segment and all following sub-segments
2939 * will be omitted. If the remainder is non-empty, it will be
2940 * emitted as a private use subtag as above (even if the remainder
2941 * turns out to be well-formed). For example,
2942 * "Solaris_isjustthecoolestthing" is emitted as
2943 * "x-lvariant-Solaris", not as "solaris".</li></ul>
2945 * <p><b>Note:</b> Although the language tag created by this
2946 * method is well-formed (satisfies the syntax requirements
2947 * defined by the IETF BCP 47 specification), it is not
2948 * necessarily a valid BCP 47 language tag. For example,
2950 * new Locale("xx", "YY").toLanguageTag();</pre>
2952 * will return "xx-YY", but the language subtag "xx" and the
2953 * region subtag "YY" are invalid because they are not registered
2954 * in the IANA Language Subtag Registry.
2956 * @return a BCP47 language tag representing the locale
2957 * @see #forLanguageTag(String)
2961 public String toLanguageTag() {
2962 BaseLocale base = base();
2963 LocaleExtensions exts = extensions();
2965 if (base.getVariant().equalsIgnoreCase("POSIX")) {
2966 // special handling for variant POSIX
2967 base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
2968 if (exts.getUnicodeLocaleType("va") == null) {
2970 InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
2972 ilocbld.setLocale(BaseLocale.ROOT, exts);
2973 ilocbld.setUnicodeLocaleKeyword("va", "posix");
2974 exts = ilocbld.getLocaleExtensions();
2975 } catch (LocaleSyntaxException e) {
2976 // this should not happen
2977 throw new RuntimeException(e);
2982 LanguageTag tag = LanguageTag.parseLocale(base, exts);
2984 StringBuilder buf = new StringBuilder();
2985 String subtag = tag.getLanguage();
2986 if (subtag.length() > 0) {
2987 buf.append(LanguageTag.canonicalizeLanguage(subtag));
2990 subtag = tag.getScript();
2991 if (subtag.length() > 0) {
2992 buf.append(LanguageTag.SEP);
2993 buf.append(LanguageTag.canonicalizeScript(subtag));
2996 subtag = tag.getRegion();
2997 if (subtag.length() > 0) {
2998 buf.append(LanguageTag.SEP);
2999 buf.append(LanguageTag.canonicalizeRegion(subtag));
3002 List<String>subtags = tag.getVariants();
3003 for (String s : subtags) {
3004 buf.append(LanguageTag.SEP);
3005 buf.append(LanguageTag.canonicalizeVariant(s));
3008 subtags = tag.getExtensions();
3009 for (String s : subtags) {
3010 buf.append(LanguageTag.SEP);
3011 buf.append(LanguageTag.canonicalizeExtension(s));
3014 subtag = tag.getPrivateuse();
3015 if (subtag.length() > 0) {
3016 if (buf.length() > 0) {
3017 buf.append(LanguageTag.SEP);
3019 buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
3020 buf.append(LanguageTag.canonicalizePrivateuse(subtag));
3023 return buf.toString();
3027 * {@icu} Returns a locale for the specified IETF BCP 47 language tag string.
3029 * <p>If the specified language tag contains any ill-formed subtags,
3030 * the first such subtag and all following subtags are ignored. Compare
3031 * to {@link ULocale.Builder#setLanguageTag} which throws an exception
3034 * <p>The following <b>conversions</b> are performed:<ul>
3036 * <li>The language code "und" is mapped to language "".
3038 * <li>The portion of a private use subtag prefixed by "lvariant",
3039 * if any, is removed and appended to the variant field in the
3040 * result locale (without case normalization). If it is then
3041 * empty, the private use subtag is discarded:
3045 * loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
3046 * loc.getVariant(); // returns "ICU4J"
3047 * loc.getExtension('x'); // returns null
3049 * loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
3050 * loc.getVariant(); // returns "ICU4J_ABC_DEF"
3051 * loc.getExtension('x'); // returns "urp"
3054 * <li>When the languageTag argument contains an extlang subtag,
3055 * the first such subtag is used as the language, and the primary
3056 * language subtag and other extlang subtags are ignored:
3059 * ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
3060 * ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
3063 * <li>Case is normalized. Language is normalized to lower case,
3064 * script to title case, country to upper case, variant to upper case,
3065 * and extensions to lower case.
3067 * <p>This implements the 'Language-Tag' production of BCP47, and
3068 * so supports grandfathered (regular and irregular) as well as
3069 * private use language tags. Stand alone private use tags are
3070 * represented as empty language and extension 'x-whatever',
3071 * and grandfathered tags are converted to their canonical replacements
3074 * <p>Grandfathered tags with canonical replacements are as follows:
3077 * <tbody align="center">
3078 * <tr><th>grandfathered tag</th><th> </th><th>modern replacement</th></tr>
3079 * <tr><td>art-lojban</td><td> </td><td>jbo</td></tr>
3080 * <tr><td>i-ami</td><td> </td><td>ami</td></tr>
3081 * <tr><td>i-bnn</td><td> </td><td>bnn</td></tr>
3082 * <tr><td>i-hak</td><td> </td><td>hak</td></tr>
3083 * <tr><td>i-klingon</td><td> </td><td>tlh</td></tr>
3084 * <tr><td>i-lux</td><td> </td><td>lb</td></tr>
3085 * <tr><td>i-navajo</td><td> </td><td>nv</td></tr>
3086 * <tr><td>i-pwn</td><td> </td><td>pwn</td></tr>
3087 * <tr><td>i-tao</td><td> </td><td>tao</td></tr>
3088 * <tr><td>i-tay</td><td> </td><td>tay</td></tr>
3089 * <tr><td>i-tsu</td><td> </td><td>tsu</td></tr>
3090 * <tr><td>no-bok</td><td> </td><td>nb</td></tr>
3091 * <tr><td>no-nyn</td><td> </td><td>nn</td></tr>
3092 * <tr><td>sgn-BE-FR</td><td> </td><td>sfb</td></tr>
3093 * <tr><td>sgn-BE-NL</td><td> </td><td>vgt</td></tr>
3094 * <tr><td>sgn-CH-DE</td><td> </td><td>sgg</td></tr>
3095 * <tr><td>zh-guoyu</td><td> </td><td>cmn</td></tr>
3096 * <tr><td>zh-hakka</td><td> </td><td>hak</td></tr>
3097 * <tr><td>zh-min-nan</td><td> </td><td>nan</td></tr>
3098 * <tr><td>zh-xiang</td><td> </td><td>hsn</td></tr>
3102 * <p>Grandfathered tags with no modern replacement will be
3103 * converted as follows:
3106 * <tbody align="center">
3107 * <tr><th>grandfathered tag</th><th> </th><th>converts to</th></tr>
3108 * <tr><td>cel-gaulish</td><td> </td><td>xtg-x-cel-gaulish</td></tr>
3109 * <tr><td>en-GB-oed</td><td> </td><td>en-GB-x-oed</td></tr>
3110 * <tr><td>i-default</td><td> </td><td>en-x-i-default</td></tr>
3111 * <tr><td>i-enochian</td><td> </td><td>und-x-i-enochian</td></tr>
3112 * <tr><td>i-mingo</td><td> </td><td>see-x-i-mingo</td></tr>
3113 * <tr><td>zh-min</td><td> </td><td>nan-x-zh-min</td></tr>
3117 * <p>For a list of all grandfathered tags, see the
3118 * IANA Language Subtag Registry (search for "Type: grandfathered").
3120 * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
3121 * and <code>forLanguageTag</code> will round-trip.
3123 * @param languageTag the language tag
3124 * @return The locale that best represents the language tag.
3125 * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
3126 * @see #toLanguageTag()
3127 * @see ULocale.Builder#setLanguageTag(String)
3131 public static ULocale forLanguageTag(String languageTag) {
3132 LanguageTag tag = LanguageTag.parse(languageTag, null);
3133 InternalLocaleBuilder bldr = new InternalLocaleBuilder();
3134 bldr.setLanguageTag(tag);
3135 return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
3140 * <code>Builder</code> is used to build instances of <code>ULocale</code>
3141 * from values configured by the setters. Unlike the <code>ULocale</code>
3142 * constructors, the <code>Builder</code> checks if a value configured by a
3143 * setter satisfies the syntax requirements defined by the <code>ULocale</code>
3144 * class. A <code>ULocale</code> object created by a <code>Builder</code> is
3145 * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
3146 * without losing information.
3148 * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
3149 * syntactic restrictions on variant, while BCP 47 requires each variant
3150 * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
3151 * alphanumerics. The method <code>setVariant</code> throws
3152 * <code>IllformedLocaleException</code> for a variant that does not satisfy
3153 * this restriction. If it is necessary to support such a variant, use a
3154 * ULocale constructor. However, keep in mind that a <code>ULocale</code>
3155 * object created this way might lose the variant information when
3156 * transformed to a BCP 47 language tag.
3158 * <p>The following example shows how to create a <code>Locale</code> object
3159 * with the <code>Builder</code>.
3162 * ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
3166 * <p>Builders can be reused; <code>clear()</code> resets all
3167 * fields to their default values.
3169 * @see ULocale#toLanguageTag()
3173 public static final class Builder {
3175 private final InternalLocaleBuilder _locbld;
3178 * Constructs an empty Builder. The default value of all
3179 * fields, extensions, and private use information is the
3185 _locbld = new InternalLocaleBuilder();
3189 * Resets the <code>Builder</code> to match the provided
3190 * <code>locale</code>. Existing state is discarded.
3192 * <p>All fields of the locale must be well-formed, see {@link Locale}.
3194 * <p>Locales with any ill-formed fields cause
3195 * <code>IllformedLocaleException</code> to be thrown.
3197 * @param locale the locale
3198 * @return This builder.
3199 * @throws IllformedLocaleException if <code>locale</code> has
3200 * any ill-formed fields.
3201 * @throws NullPointerException if <code>locale</code> is null.
3205 public Builder setLocale(ULocale locale) {
3207 _locbld.setLocale(locale.base(), locale.extensions());
3208 } catch (LocaleSyntaxException e) {
3209 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3215 * Resets the Builder to match the provided IETF BCP 47
3216 * language tag. Discards the existing state. Null and the
3217 * empty string cause the builder to be reset, like {@link
3218 * #clear}. Grandfathered tags (see {@link
3219 * ULocale#forLanguageTag}) are converted to their canonical
3220 * form before being processed. Otherwise, the language tag
3221 * must be well-formed (see {@link ULocale}) or an exception is
3222 * thrown (unlike <code>ULocale.forLanguageTag</code>, which
3223 * just discards ill-formed and following portions of the
3226 * @param languageTag the language tag
3227 * @return This builder.
3228 * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
3229 * @see ULocale#forLanguageTag(String)
3233 public Builder setLanguageTag(String languageTag) {
3234 ParseStatus sts = new ParseStatus();
3235 LanguageTag tag = LanguageTag.parse(languageTag, sts);
3236 if (sts.isError()) {
3237 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
3239 _locbld.setLanguageTag(tag);
3245 * Sets the language. If <code>language</code> is the empty string or
3246 * null, the language in this <code>Builder</code> is removed. Otherwise,
3247 * the language must be <a href="./Locale.html#def_language">well-formed</a>
3248 * or an exception is thrown.
3250 * <p>The typical language value is a two or three-letter language
3251 * code as defined in ISO639.
3253 * @param language the language
3254 * @return This builder.
3255 * @throws IllformedLocaleException if <code>language</code> is ill-formed
3259 public Builder setLanguage(String language) {
3261 _locbld.setLanguage(language);
3262 } catch (LocaleSyntaxException e) {
3263 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3269 * Sets the script. If <code>script</code> is null or the empty string,
3270 * the script in this <code>Builder</code> is removed.
3271 * Otherwise, the script must be well-formed or an exception is thrown.
3273 * <p>The typical script value is a four-letter script code as defined by ISO 15924.
3275 * @param script the script
3276 * @return This builder.
3277 * @throws IllformedLocaleException if <code>script</code> is ill-formed
3281 public Builder setScript(String script) {
3283 _locbld.setScript(script);
3284 } catch (LocaleSyntaxException e) {
3285 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3291 * Sets the region. If region is null or the empty string, the region
3292 * in this <code>Builder</code> is removed. Otherwise,
3293 * the region must be well-formed or an exception is thrown.
3295 * <p>The typical region value is a two-letter ISO 3166 code or a
3296 * three-digit UN M.49 area code.
3298 * <p>The country value in the <code>Locale</code> created by the
3299 * <code>Builder</code> is always normalized to upper case.
3301 * @param region the region
3302 * @return This builder.
3303 * @throws IllformedLocaleException if <code>region</code> is ill-formed
3307 public Builder setRegion(String region) {
3309 _locbld.setRegion(region);
3310 } catch (LocaleSyntaxException e) {
3311 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3317 * Sets the variant. If variant is null or the empty string, the
3318 * variant in this <code>Builder</code> is removed. Otherwise, it
3319 * must consist of one or more well-formed subtags, or an exception is thrown.
3321 * <p><b>Note:</b> This method checks if <code>variant</code>
3322 * satisfies the IETF BCP 47 variant subtag's syntax requirements,
3323 * and normalizes the value to lowercase letters. However,
3324 * the <code>ULocale</code> class does not impose any syntactic
3325 * restriction on variant. To set such a variant,
3326 * use a ULocale constructor.
3328 * @param variant the variant
3329 * @return This builder.
3330 * @throws IllformedLocaleException if <code>variant</code> is ill-formed
3334 public Builder setVariant(String variant) {
3336 _locbld.setVariant(variant);
3337 } catch (LocaleSyntaxException e) {
3338 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3344 * Sets the extension for the given key. If the value is null or the
3345 * empty string, the extension is removed. Otherwise, the extension
3346 * must be well-formed or an exception is thrown.
3348 * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
3349 * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
3350 * Setting a value for this key replaces any existing Unicode locale key/type
3351 * pairs with those defined in the extension.
3353 * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
3354 * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
3355 * well-formed, the value for this key needs only to have subtags of one to
3356 * eight alphanumeric characters, not two to eight as in the general case.
3358 * @param key the extension key
3359 * @param value the extension value
3360 * @return This builder.
3361 * @throws IllformedLocaleException if <code>key</code> is illegal
3362 * or <code>value</code> is ill-formed
3363 * @see #setUnicodeLocaleKeyword(String, String)
3367 public Builder setExtension(char key, String value) {
3369 _locbld.setExtension(key, value);
3370 } catch (LocaleSyntaxException e) {
3371 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3377 * Sets the Unicode locale keyword type for the given key. If the type
3378 * is null, the Unicode keyword is removed. Otherwise, the key must be
3379 * non-null and both key and type must be well-formed or an exception
3382 * <p>Keys and types are converted to lower case.
3384 * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
3385 * replaces all Unicode locale keywords with those defined in the
3388 * @param key the Unicode locale key
3389 * @param type the Unicode locale type
3390 * @return This builder.
3391 * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
3393 * @throws NullPointerException if <code>key</code> is null
3394 * @see #setExtension(char, String)
3398 public Builder setUnicodeLocaleKeyword(String key, String type) {
3400 _locbld.setUnicodeLocaleKeyword(key, type);
3401 } catch (LocaleSyntaxException e) {
3402 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3408 * Adds a unicode locale attribute, if not already present, otherwise
3409 * has no effect. The attribute must not be null and must be well-formed
3410 * or an exception is thrown.
3412 * @param attribute the attribute
3413 * @return This builder.
3414 * @throws NullPointerException if <code>attribute</code> is null
3415 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
3416 * @see #setExtension(char, String)
3420 public Builder addUnicodeLocaleAttribute(String attribute) {
3422 _locbld.addUnicodeLocaleAttribute(attribute);
3423 } catch (LocaleSyntaxException e) {
3424 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3430 * Removes a unicode locale attribute, if present, otherwise has no
3431 * effect. The attribute must not be null and must be well-formed
3432 * or an exception is thrown.
3434 * <p>Attribute comparision for removal is case-insensitive.
3436 * @param attribute the attribute
3437 * @return This builder.
3438 * @throws NullPointerException if <code>attribute</code> is null
3439 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
3440 * @see #setExtension(char, String)
3444 public Builder removeUnicodeLocaleAttribute(String attribute) {
3446 _locbld.removeUnicodeLocaleAttribute(attribute);
3447 } catch (LocaleSyntaxException e) {
3448 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3454 * Resets the builder to its initial, empty state.
3456 * @return this builder
3460 public Builder clear() {
3466 * Resets the extensions to their initial, empty state.
3467 * Language, script, region and variant are unchanged.
3469 * @return this builder
3470 * @see #setExtension(char, String)
3474 public Builder clearExtensions() {
3475 _locbld.clearExtensions();
3480 * Returns an instance of <code>ULocale</code> created from the fields set
3483 * @return a new Locale
3487 public ULocale build() {
3488 return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
3492 private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
3493 String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
3496 Set<Character> extKeys = exts.getKeys();
3497 if (!extKeys.isEmpty()) {
3498 // legacy locale ID assume Unicode locale keywords and
3499 // other extensions are at the same level.
3500 // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
3502 TreeMap<String, String> kwds = new TreeMap<String, String>();
3503 for (Character key : extKeys) {
3504 Extension ext = exts.getExtension(key);
3505 if (ext instanceof UnicodeLocaleExtension) {
3506 UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
3507 Set<String> ukeys = uext.getUnicodeLocaleKeys();
3508 for (String bcpKey : ukeys) {
3509 String bcpType = uext.getUnicodeLocaleType(bcpKey);
3510 // convert to legacy key/type
3511 String lkey = bcp47ToLDMLKey(bcpKey);
3512 String ltype = bcp47ToLDMLType(lkey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords
3513 // special handling for u-va-posix, since this is a variant, not a keyword
3514 if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
3517 kwds.put(lkey, ltype);
3520 // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
3521 Set<String> uattributes = uext.getUnicodeLocaleAttributes();
3522 if (uattributes.size() > 0) {
3523 StringBuilder attrbuf = new StringBuilder();
3524 for (String attr : uattributes) {
3525 if (attrbuf.length() > 0) {
3526 attrbuf.append('-');
3528 attrbuf.append(attr);
3530 kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
3533 kwds.put(String.valueOf(key), ext.getValue());
3537 if (!kwds.isEmpty()) {
3538 StringBuilder buf = new StringBuilder(id);
3540 Set<Map.Entry<String, String>> kset = kwds.entrySet();
3541 boolean insertSep = false;
3542 for (Map.Entry<String, String> kwd : kset) {
3548 buf.append(kwd.getKey());
3550 buf.append(kwd.getValue());
3553 id = buf.toString();
3556 return new ULocale(id);
3559 private BaseLocale base() {
3560 if (baseLocale == null) {
3561 String language = getLanguage();
3562 if (equals(ULocale.ROOT)) {
3565 baseLocale = BaseLocale.getInstance(language, getScript(), getCountry(), getVariant());
3570 private LocaleExtensions extensions() {
3571 if (extensions == null) {
3572 Iterator<String> kwitr = getKeywords();
3573 if (kwitr == null) {
3574 extensions = LocaleExtensions.EMPTY_EXTENSIONS;
3576 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
3577 while (kwitr.hasNext()) {
3578 String key = kwitr.next();
3579 if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
3580 // special keyword used for representing Unicode locale attributes
3581 String[] uattributes = getKeywordValue(key).split("[-_]");
3582 for (String uattr : uattributes) {
3584 intbld.addUnicodeLocaleAttribute(uattr);
3585 } catch (LocaleSyntaxException e) {
3586 // ignore and fall through
3589 } else if (key.length() >= 2) {
3590 String bcpKey = ldmlKeyToBCP47(key);
3591 String bcpType = ldmlTypeToBCP47(key, getKeywordValue(key));
3592 if (bcpKey != null && bcpType != null) {
3594 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
3595 } catch (LocaleSyntaxException e) {
3596 // ignore and fall through
3599 } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
3601 intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
3603 } catch (LocaleSyntaxException e) {
3604 // ignore and fall through
3608 extensions = intbld.getLocaleExtensions();
3615 // LDML legacy/BCP47 key and type mapping functions
3617 private static String ldmlKeyToBCP47(String key) {
3618 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
3619 ICUResourceBundle.ICU_BASE_NAME,
3621 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
3622 UResourceBundle keyMap = keyTypeData.get("keyMap");
3624 // normalize key to lowercase
3625 key = AsciiUtil.toLowerString(key);
3626 String bcpKey = null;
3628 bcpKey = keyMap.getString(key);
3629 } catch (MissingResourceException mre) {
3633 if (bcpKey == null) {
3634 if (key.length() == 2 && LanguageTag.isExtensionSubtag(key)) {
3642 private static String bcp47ToLDMLKey(String bcpKey) {
3643 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
3644 ICUResourceBundle.ICU_BASE_NAME,
3646 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
3647 UResourceBundle keyMap = keyTypeData.get("keyMap");
3649 // normalize bcp key to lowercase
3650 bcpKey = AsciiUtil.toLowerString(bcpKey);
3652 for (int i = 0; i < keyMap.getSize(); i++) {
3653 UResourceBundle mapData = keyMap.get(i);
3654 if (bcpKey.equals(mapData.getString())) {
3655 key = mapData.getKey();
3665 private static String ldmlTypeToBCP47(String key, String type) {
3666 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
3667 ICUResourceBundle.ICU_BASE_NAME,
3669 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
3670 UResourceBundle typeMap = keyTypeData.get("typeMap");
3672 // keys are case-insensitive, while types are case-sensitive
3673 key = AsciiUtil.toLowerString(key);
3674 UResourceBundle typeMapForKey = null;
3675 String bcpType = null;
3676 String typeResKey = key.equals("timezone") ? type.replace('/', ':') : type;
3678 typeMapForKey = typeMap.get(key);
3679 bcpType = typeMapForKey.getString(typeResKey);
3680 } catch (MissingResourceException mre) {
3684 if (bcpType == null && typeMapForKey != null) {
3685 // is this type alias?
3686 UResourceBundle typeAlias = keyTypeData.get("typeAlias");
3688 UResourceBundle typeAliasForKey = typeAlias.get(key);
3689 typeResKey = typeAliasForKey.getString(typeResKey);
3690 bcpType = typeMapForKey.getString(typeResKey.replace('/', ':'));
3691 } catch (MissingResourceException mre) {
3696 if (bcpType == null) {
3697 int typeLen = type.length();
3698 if (typeLen >= 3 && typeLen <= 8 && LanguageTag.isExtensionSubtag(type)) {
3706 private static String bcp47ToLDMLType(String key, String bcpType) {
3707 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
3708 ICUResourceBundle.ICU_BASE_NAME,
3710 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
3711 UResourceBundle typeMap = keyTypeData.get("typeMap");
3713 // normalize key/bcpType to lowercase
3714 key = AsciiUtil.toLowerString(key);
3715 bcpType = AsciiUtil.toLowerString(bcpType);
3719 UResourceBundle typeMapForKey = typeMap.get(key);
3721 // Note: Linear search for time zone ID might be too slow.
3722 // ICU services do not use timezone keywords for now.
3723 // In future, we may need to build the optimized inverse
3726 for (int i = 0; i < typeMapForKey.getSize(); i++) {
3727 UResourceBundle mapData = typeMapForKey.get(i);
3728 if (bcpType.equals(mapData.getString())) {
3729 type = mapData.getKey();
3730 if (key.equals("timezone")) {
3731 type = type.replace(':', '/');
3736 } catch (MissingResourceException mre) {
3749 private static final class JDKLocaleHelper {
3750 private static boolean isJava7orNewer = false;
3753 * New methods in Java 7 Locale class
3755 private static Method mGetScript;
3756 private static Method mGetExtensionKeys;
3757 private static Method mGetExtension;
3758 private static Method mGetUnicodeLocaleKeys;
3759 private static Method mGetUnicodeLocaleAttributes;
3760 private static Method mGetUnicodeLocaleType;
3761 private static Method mForLanguageTag;
3763 private static Method mGetDefault;
3764 private static Method mSetDefault;
3765 private static Object eDISPLAY;
3766 private static Object eFORMAT;
3769 * This table is used for mapping between ICU and special Java
3770 * 6 locales. When an ICU locale matches <minumum base> with
3771 * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
3772 * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
3773 * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped
3774 * to Java locale "no_NO_NY".
3776 private static final String[][] JAVA6_MAPDATA = {
3777 // { <Java>, <ICU base>, <keyword>, <value>, <minimum base>
3778 { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"},
3779 { "no_NO_NY", "nn_NO", null, null, "nn"},
3780 { "th_TH_TH", "th_TH", "numbers", "thai", "th"},
3786 mGetScript = Locale.class.getMethod("getScript", (Class[]) null);
3787 mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null);
3788 mGetExtension = Locale.class.getMethod("getExtension", char.class);
3789 mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null);
3790 mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null);
3791 mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class);
3792 mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
3794 Class<?> cCategory = null;
3795 Class<?>[] classes = Locale.class.getDeclaredClasses();
3796 for (Class<?> c : classes) {
3797 if (c.getName().equals("java.util.Locale$Category")) {
3802 if (cCategory == null) {
3805 mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
3806 mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
3808 Method mName = cCategory.getMethod("name", (Class[]) null);
3809 Object[] enumConstants = cCategory.getEnumConstants();
3810 for (Object e : enumConstants) {
3811 String catVal = (String)mName.invoke(e, (Object[])null);
3812 if (catVal.equals("DISPLAY")) {
3814 } else if (catVal.equals("FORMAT")) {
3818 if (eDISPLAY == null || eFORMAT == null) {
3821 isJava7orNewer = true;
3822 } catch (NoSuchMethodException e) {
3823 } catch (IllegalArgumentException e) {
3824 } catch (IllegalAccessException e) {
3825 } catch (InvocationTargetException e) {
3826 } catch (SecurityException e) {
3832 private JDKLocaleHelper() {
3835 public static boolean isJava7orNewer() {
3836 return isJava7orNewer;
3839 public static ULocale toULocale(Locale loc) {
3840 return isJava7orNewer ? toULocale7(loc) : toULocale6(loc);
3843 public static Locale toLocale(ULocale uloc) {
3844 return isJava7orNewer ? toLocale7(uloc) : toLocale6(uloc);
3847 private static ULocale toULocale7(Locale loc) {
3848 String language = loc.getLanguage();
3850 String country = loc.getCountry();
3851 String variant = loc.getVariant();
3853 Set<String> attributes = null;
3854 Map<String, String> keywords = null;
3857 script = (String) mGetScript.invoke(loc, (Object[]) null);
3858 @SuppressWarnings("unchecked")
3859 Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null);
3860 if (!extKeys.isEmpty()) {
3861 for (Character extKey : extKeys) {
3862 if (extKey.charValue() == 'u') {
3863 // Found Unicode locale extension
3866 @SuppressWarnings("unchecked")
3867 Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null);
3868 if (!uAttributes.isEmpty()) {
3869 attributes = new TreeSet<String>();
3870 for (String attr : uAttributes) {
3871 attributes.add(attr);
3876 @SuppressWarnings("unchecked")
3877 Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null);
3878 for (String kwKey : uKeys) {
3879 String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey);
3880 if (kwVal != null) {
3881 if (kwKey.equals("va")) {
3882 // va-* is interpreted as a variant
3883 variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
3885 if (keywords == null) {
3886 keywords = new TreeMap<String, String>();
3888 keywords.put(kwKey, kwVal);
3893 String extVal = (String) mGetExtension.invoke(loc, extKey);
3894 if (extVal != null) {
3895 if (keywords == null) {
3896 keywords = new TreeMap<String, String>();
3898 keywords.put(String.valueOf(extKey), extVal);
3903 } catch (IllegalAccessException e) {
3904 throw new RuntimeException(e);
3905 } catch (InvocationTargetException e) {
3906 throw new RuntimeException(e);
3909 // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
3910 // and it should be transformed to nn_NO.
3912 // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
3913 // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
3915 if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
3921 StringBuilder buf = new StringBuilder(language);
3923 if (script.length() > 0) {
3928 if (country.length() > 0) {
3930 buf.append(country);
3933 if (variant.length() > 0) {
3934 if (country.length() == 0) {
3938 buf.append(variant);
3941 if (attributes != null) {
3942 // transform Unicode attributes into a keyword
3943 StringBuilder attrBuf = new StringBuilder();
3944 for (String attr : attributes) {
3945 if (attrBuf.length() != 0) {
3946 attrBuf.append('-');
3948 attrBuf.append(attr);
3950 if (keywords == null) {
3951 keywords = new TreeMap<String, String>();
3953 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
3956 if (keywords != null) {
3958 boolean addSep = false;
3959 for (Entry<String, String> kwEntry : keywords.entrySet()) {
3960 String kwKey = kwEntry.getKey();
3961 String kwVal = kwEntry.getValue();
3963 if (kwKey.length() != 1) {
3964 // Unicode locale key
3965 kwKey = bcp47ToLDMLKey(kwKey);
3966 // use "yes" as the value of typeless keywords
3967 kwVal = bcp47ToLDMLType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal));
3981 return new ULocale(getName(buf.toString()), loc);
3984 private static ULocale toULocale6(Locale loc) {
3985 ULocale uloc = null;
3986 String locStr = loc.toString();
3987 if (locStr.length() == 0) {
3988 uloc = ULocale.ROOT;
3990 for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
3991 if (JAVA6_MAPDATA[i][0].equals(locStr)) {
3992 LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]);
3993 p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]);
3994 locStr = p.getName();
3998 uloc = new ULocale(getName(locStr), loc);
4003 private static Locale toLocale7(ULocale uloc) {
4005 String ulocStr = uloc.getName();
4006 if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
4007 // With script or keywords available, the best way
4008 // to get a mapped Locale is to go through a language tag.
4009 // A Locale with script or keywords can only have variants
4010 // that is 1 to 8 alphanum. If this ULocale has a variant
4011 // subtag not satisfying the criteria, the variant subtag
4013 String tag = uloc.toLanguageTag();
4015 // Workaround for variant casing problem:
4017 // The variant field in ICU is case insensitive and normalized
4018 // to upper case letters by getVariant(), while
4019 // the variant field in JDK Locale is case sensitive.
4020 // ULocale#toLanguageTag use lower case characters for
4021 // BCP 47 variant and private use x-lvariant.
4023 // Locale#forLanguageTag in JDK preserves character casing
4024 // for variant. Because ICU always normalizes variant to
4025 // upper case, we convert language tag to upper case here.
4026 tag = AsciiUtil.toUpperString(tag);
4029 loc = (Locale)mForLanguageTag.invoke(null, tag);
4030 } catch (IllegalAccessException e) {
4031 throw new RuntimeException(e);
4032 } catch (InvocationTargetException e) {
4033 throw new RuntimeException(e);
4037 // Without script or keywords, use a Locale constructor,
4038 // so we can preserve any ill-formed variants.
4039 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
4044 private static Locale toLocale6(ULocale uloc) {
4045 String locstr = uloc.getBaseName();
4046 for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
4047 if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) {
4048 if (JAVA6_MAPDATA[i][2] != null) {
4049 String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]);
4050 if (val != null && val.equals(JAVA6_MAPDATA[i][3])) {
4051 locstr = JAVA6_MAPDATA[i][0];
4055 locstr = JAVA6_MAPDATA[i][0];
4060 LocaleIDParser p = new LocaleIDParser(locstr);
4061 String[] names = p.getLanguageScriptCountryVariant();
4062 return new Locale(names[0], names[2], names[3]);
4065 public static Locale getDefault(Category category) {
4066 Locale loc = Locale.getDefault();
4067 if (isJava7orNewer) {
4079 loc = (Locale)mGetDefault.invoke(null, cat);
4080 } catch (InvocationTargetException e) {
4081 // fall through - use the base default
4082 } catch (IllegalArgumentException e) {
4083 // fall through - use the base default
4084 } catch (IllegalAccessException e) {
4085 // fall through - use the base default
4092 public static void setDefault(Category category, Locale newLocale) {
4093 if (isJava7orNewer) {
4105 mSetDefault.invoke(null, cat, newLocale);
4106 } catch (InvocationTargetException e) {
4107 // fall through - no effects
4108 } catch (IllegalArgumentException e) {
4109 // fall through - no effects
4110 } catch (IllegalAccessException e) {
4111 // fall through - no effects
4117 // Returns true if the given Locale matches the original
4118 // default locale initialized by JVM by checking user.XXX
4119 // system properties. When the system properties are not accessible,
4120 // this method returns false.
4121 public static boolean isOriginalDefaultLocale(Locale loc) {
4122 if (isJava7orNewer) {
4125 script = (String) mGetScript.invoke(loc, (Object[]) null);
4126 } catch (Exception e) {
4130 return loc.getLanguage().equals(getSystemProperty("user.language"))
4131 && loc.getCountry().equals(getSystemProperty("user.country"))
4132 && loc.getVariant().equals(getSystemProperty("user.variant"))
4133 && script.equals(getSystemProperty("user.script"));
4135 return loc.getLanguage().equals(getSystemProperty("user.language"))
4136 && loc.getCountry().equals(getSystemProperty("user.country"))
4137 && loc.getVariant().equals(getSystemProperty("user.variant"));
4140 public static String getSystemProperty(String key) {
4142 final String fkey = key;
4143 if (System.getSecurityManager() != null) {
4145 val = AccessController.doPrivileged(new PrivilegedAction<String>() {
4146 public String run() {
4147 return System.getProperty(fkey);
4150 } catch (AccessControlException e) {
4154 val = System.getProperty(fkey);