1 /* 2 ****************************************************************************** 3 * Copyright (C) 2003-2015, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ****************************************************************************** 6 */ 7 8 package com.ibm.icu.util; 9 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; 20 import java.util.Map; 21 import java.util.Map.Entry; 22 import java.util.MissingResourceException; 23 import java.util.Set; 24 import java.util.TreeMap; 25 import java.util.TreeSet; 26 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.KeyTypeData; 39 import com.ibm.icu.impl.locale.LanguageTag; 40 import com.ibm.icu.impl.locale.LocaleExtensions; 41 import com.ibm.icu.impl.locale.LocaleSyntaxException; 42 import com.ibm.icu.impl.locale.ParseStatus; 43 import com.ibm.icu.impl.locale.UnicodeLocaleExtension; 44 import com.ibm.icu.lang.UScript; 45 import com.ibm.icu.text.LocaleDisplayNames; 46 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling; 47 48 /** 49 * {@icuenhanced java.util.Locale}.{@icu _usage_} 50 * 51 * A class analogous to {@link java.util.Locale} that provides additional 52 * support for ICU protocol. In ICU 3.0 this class is enhanced to support 53 * RFC 3066 language identifiers. 54 * 55 * <p>Many classes and services in ICU follow a factory idiom, in 56 * which a factory method or object responds to a client request with 57 * an object. The request includes a locale (the <i>requested</i> 58 * locale), and the returned object is constructed using data for that 59 * locale. The system may lack data for the requested locale, in 60 * which case the locale fallback mechanism will be invoked until a 61 * populated locale is found (the <i>valid</i> locale). Furthermore, 62 * even when a populated locale is found (the <i>valid</i> locale), 63 * further fallback may be required to reach a locale containing the 64 * specific data required by the service (the <i>actual</i> locale). 65 * 66 * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids. 67 * Normalization 'cleans up' ICU locale ids as follows: 68 * <ul> 69 * <li>language, script, country, variant, and keywords are properly cased<br> 70 * (lower, title, upper, upper, and lower case respectively)</li> 71 * <li>hyphens used as separators are converted to underscores</li> 72 * <li>three-letter language and country ids are converted to two-letter 73 * equivalents where available</li> 74 * <li>surrounding spaces are removed from keywords and values</li> 75 * <li>if there are multiple keywords, they are put in sorted order</li> 76 * </ul> 77 * Canonicalization additionally performs the following: 78 * <ul> 79 * <li>POSIX ids are converted to ICU format IDs</li> 80 * <li>'grandfathered' 3066 ids are converted to ICU standard form</li> 81 * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form, 82 * with the currency 83 * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO). 84 * </ul> 85 * All ULocale constructors automatically normalize the locale id. To handle 86 * POSIX ids, <code>canonicalize</code> can be called to convert the id 87 * to canonical form, or the <code>canonicalInstance</code> factory method 88 * can be called.</p> 89 * 90 * <p>This class provides selectors {@link #VALID_LOCALE} and {@link 91 * #ACTUAL_LOCALE} intended for use in methods named 92 * <tt>getLocale()</tt>. These methods exist in several ICU classes, 93 * including {@link com.ibm.icu.util.Calendar}, {@link 94 * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat}, 95 * {@link com.ibm.icu.text.BreakIterator}, 96 * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>, 97 * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link 98 * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if 99 * any. Once an object of one of these classes has been created, 100 * <tt>getLocale()</tt> may be called on it to determine the valid and 101 * actual locale arrived at during the object's construction. 102 * 103 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i> 104 * locale is not, in most cases. 105 * 106 * @see java.util.Locale 107 * @author weiv 108 * @author Alan Liu 109 * @author Ram Viswanadha 110 * @stable ICU 2.8 111 */ 112 public final class ULocale implements Serializable, Comparable<ULocale> { 113 // using serialver from jdk1.4.2_05 114 private static final long serialVersionUID = 3715177670352309217L; 115 116 private static ICUCache<String, String> nameCache = new SimpleCache<String, String>(); 117 118 /** 119 * Useful constant for language. 120 * @stable ICU 3.0 121 */ 122 public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH); 123 124 /** 125 * Useful constant for language. 126 * @stable ICU 3.0 127 */ 128 public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH); 129 130 /** 131 * Useful constant for language. 132 * @stable ICU 3.0 133 */ 134 public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN); 135 136 /** 137 * Useful constant for language. 138 * @stable ICU 3.0 139 */ 140 public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN); 141 142 /** 143 * Useful constant for language. 144 * @stable ICU 3.0 145 */ 146 public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE); 147 148 /** 149 * Useful constant for language. 150 * @stable ICU 3.0 151 */ 152 public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN); 153 154 /** 155 * Useful constant for language. 156 * @stable ICU 3.0 157 */ 158 public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE); 159 160 161 // Special note about static initializer for 162 // - SIMPLIFIED_CHINESE 163 // - TRADTIONAL_CHINESE 164 // - CHINA 165 // - TAIWAN 166 // 167 // Equivalent JDK Locale for ULocale.SIMPLIFIED_CHINESE is different 168 // by JRE version. JRE 7 or later supports a script tag "Hans", while 169 // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually 170 // zh_CN, not zh_Hans. This is same in Java 7 or later versions. 171 // 172 // ULocale#toLocale() implementation uses Java reflection to create a Locale 173 // with a script tag. When a new ULocale is constructed with the single arg 174 // constructor, the volatile field 'Locale locale' is initialized by 175 // #toLocale() method. 176 // 177 // Because we cannot hardcode corresponding JDK Locale representation below, 178 // SIMPLIFIED_CHINESE is constructed without JDK Locale argument, and 179 // #toLocale() is used for resolving the best matching JDK Locale at runtime. 180 // 181 // The same thing applies to TRADITIONAL_CHINESE. 182 183 /** 184 * Useful constant for language. 185 * @stable ICU 3.0 186 */ 187 public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans"); 188 189 190 /** 191 * Useful constant for language. 192 * @stable ICU 3.0 193 */ 194 public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant"); 195 196 /** 197 * Useful constant for country/region. 198 * @stable ICU 3.0 199 */ 200 public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE); 201 202 /** 203 * Useful constant for country/region. 204 * @stable ICU 3.0 205 */ 206 public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY); 207 208 /** 209 * Useful constant for country/region. 210 * @stable ICU 3.0 211 */ 212 public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY); 213 214 /** 215 * Useful constant for country/region. 216 * @stable ICU 3.0 217 */ 218 public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN); 219 220 /** 221 * Useful constant for country/region. 222 * @stable ICU 3.0 223 */ 224 public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA); 225 226 /** 227 * Useful constant for country/region. 228 * @stable ICU 3.0 229 */ 230 public static final ULocale CHINA = new ULocale("zh_Hans_CN"); 231 232 /** 233 * Useful constant for country/region. 234 * @stable ICU 3.0 235 */ 236 public static final ULocale PRC = CHINA; 237 238 /** 239 * Useful constant for country/region. 240 * @stable ICU 3.0 241 */ 242 public static final ULocale TAIWAN = new ULocale("zh_Hant_TW"); 243 244 /** 245 * Useful constant for country/region. 246 * @stable ICU 3.0 247 */ 248 public static final ULocale UK = new ULocale("en_GB", Locale.UK); 249 250 /** 251 * Useful constant for country/region. 252 * @stable ICU 3.0 253 */ 254 public static final ULocale US = new ULocale("en_US", Locale.US); 255 256 /** 257 * Useful constant for country/region. 258 * @stable ICU 3.0 259 */ 260 public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA); 261 262 /** 263 * Useful constant for country/region. 264 * @stable ICU 3.0 265 */ 266 public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH); 267 268 /** 269 * Handy constant. 270 */ 271 private static final String EMPTY_STRING = ""; 272 273 // Used in both ULocale and LocaleIDParser, so moved up here. 274 private static final char UNDERSCORE = '_'; 275 276 // default empty locale 277 private static final Locale EMPTY_LOCALE = new Locale("", ""); 278 279 // special keyword key for Unicode locale attributes 280 private static final String LOCALE_ATTRIBUTE_KEY = "attribute"; 281 282 /** 283 * The root ULocale. 284 * @stable ICU 2.8 285 */ 286 public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE); 287 288 /** 289 * Enum for locale categories. These locale categories are used to get/set the default locale for 290 * the specific functionality represented by the category. 291 * @stable ICU 49 292 */ 293 public enum Category { 294 /** 295 * Category used to represent the default locale for displaying user interfaces. 296 * @stable ICU 49 297 */ 298 DISPLAY, 299 /** 300 * Category used to represent the default locale for formatting date, number and/or currency. 301 * @stable ICU 49 302 */ 303 FORMAT 304 } 305 306 private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>(); 307 308 /** 309 * Cache the locale. 310 */ 311 private transient volatile Locale locale; 312 313 /** 314 * The raw localeID that we were passed in. 315 */ 316 private String localeID; 317 318 /** 319 * Cache the locale data container fields. 320 * In future, we want to use them as the primary locale identifier storage. 321 */ 322 private transient volatile BaseLocale baseLocale; 323 private transient volatile LocaleExtensions extensions; 324 325 326 private static String[][] CANONICALIZE_MAP; 327 private static String[][] variantsToKeywords; 328 initCANONICALIZE_MAP()329 private static void initCANONICALIZE_MAP() { 330 if (CANONICALIZE_MAP == null) { 331 /** 332 * This table lists pairs of locale ids for canonicalization. The 333 * The 1st item is the normalized id. The 2nd item is the 334 * canonicalized id. The 3rd is the keyword. The 4th is the keyword value. 335 */ 336 String[][] tempCANONICALIZE_MAP = { 337 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */ 338 { "C", "en_US_POSIX", null, null }, /* POSIX name */ 339 { "art_LOJBAN", "jbo", null, null }, /* registered name */ 340 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */ 341 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */ 342 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" }, 343 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */ 344 { "de_1901", "de__1901", null, null }, /* registered name */ 345 { "de_1906", "de__1906", null, null }, /* registered name */ 346 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */ 347 { "de_AT_PREEURO", "de_AT", "currency", "ATS" }, 348 { "de_DE_PREEURO", "de_DE", "currency", "DEM" }, 349 { "de_LU_PREEURO", "de_LU", "currency", "EUR" }, 350 { "el_GR_PREEURO", "el_GR", "currency", "GRD" }, 351 { "en_BOONT", "en__BOONT", null, null }, /* registered name */ 352 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */ 353 { "en_BE_PREEURO", "en_BE", "currency", "BEF" }, 354 { "en_IE_PREEURO", "en_IE", "currency", "IEP" }, 355 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */ 356 { "es_ES_PREEURO", "es_ES", "currency", "ESP" }, 357 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" }, 358 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" }, 359 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" }, 360 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" }, 361 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" }, 362 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" }, 363 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" }, 364 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */ 365 { "it_IT_PREEURO", "it_IT", "currency", "ITL" }, 366 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, 367 // { "nb_NO_NY", "nn_NO", null, null }, 368 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" }, 369 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" }, 370 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" }, 371 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */ 372 { "sr_SP_CYRL", "sr_Cyrl_RS", null, null }, /* .NET name */ 373 { "sr_SP_LATN", "sr_Latn_RS", null, null }, /* .NET name */ 374 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */ 375 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */ 376 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */ 377 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */ 378 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */ 379 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */ 380 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */ 381 { "zh_GAN", "zh__GAN", null, null }, /* registered name */ 382 { "zh_GUOYU", "zh", null, null }, /* registered name */ 383 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */ 384 { "zh_MIN", "zh__MIN", null, null }, /* registered name */ 385 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */ 386 { "zh_WUU", "zh__WUU", null, null }, /* registered name */ 387 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */ 388 { "zh_YUE", "zh__YUE", null, null } /* registered name */ 389 }; 390 391 synchronized (ULocale.class) { 392 if (CANONICALIZE_MAP == null) { 393 CANONICALIZE_MAP = tempCANONICALIZE_MAP; 394 } 395 } 396 } 397 if (variantsToKeywords == null) { 398 /** 399 * This table lists pairs of locale ids for canonicalization. The 400 * The first item is the normalized variant id. 401 */ 402 String[][] tempVariantsToKeywords = { 403 { "EURO", "currency", "EUR" }, 404 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */ 405 { "STROKE", "collation", "stroke" } /* Solaris variant */ 406 }; 407 408 synchronized (ULocale.class) { 409 if (variantsToKeywords == null) { 410 variantsToKeywords = tempVariantsToKeywords; 411 } 412 } 413 } 414 } 415 416 /** 417 * Private constructor used by static initializers. 418 */ ULocale(String localeID, Locale locale)419 private ULocale(String localeID, Locale locale) { 420 this.localeID = localeID; 421 this.locale = locale; 422 } 423 424 /** 425 * Construct a ULocale object from a {@link java.util.Locale}. 426 * @param loc a JDK locale 427 */ ULocale(Locale loc)428 private ULocale(Locale loc) { 429 this.localeID = getName(forLocale(loc).toString()); 430 this.locale = loc; 431 } 432 433 /** 434 * {@icu} Returns a ULocale object for a {@link java.util.Locale}. 435 * The ULocale is canonicalized. 436 * @param loc a JDK locale 437 * @stable ICU 3.2 438 */ forLocale(Locale loc)439 public static ULocale forLocale(Locale loc) { 440 if (loc == null) { 441 return null; 442 } 443 ULocale result = CACHE.get(loc); 444 if (result == null) { 445 result = JDKLocaleHelper.toULocale(loc); 446 CACHE.put(loc, result); 447 } 448 return result; 449 } 450 451 /** 452 * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists 453 * of optional language, script, country, and variant fields in that order, 454 * separated by underscores, followed by an optional keyword list. The 455 * script, if present, is four characters long-- this distinguishes it 456 * from a country code, which is two characters long. Other fields 457 * are distinguished by position as indicated by the underscores. The 458 * start of the keyword list is indicated by '@', and consists of two 459 * or more keyword/value pairs separated by semicolons(';'). 460 * 461 * <p>This constructor does not canonicalize the localeID. So, for 462 * example, "zh__pinyin" remains unchanged instead of converting 463 * to "zh@collation=pinyin". By default ICU only recognizes the 464 * latter as specifying pinyin collation. Use {@link #createCanonical} 465 * or {@link #canonicalize} if you need to canonicalize the localeID. 466 * 467 * @param localeID string representation of the locale, e.g: 468 * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional" 469 * @stable ICU 2.8 470 */ ULocale(String localeID)471 public ULocale(String localeID) { 472 this.localeID = getName(localeID); 473 } 474 475 /** 476 * Convenience overload of ULocale(String, String, String) for 477 * compatibility with java.util.Locale. 478 * @see #ULocale(String, String, String) 479 * @stable ICU 3.4 480 */ ULocale(String a, String b)481 public ULocale(String a, String b) { 482 this(a, b, null); 483 } 484 485 /** 486 * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and 487 * c. These fields are concatenated using underscores to form a localeID of the form 488 * a_b_c, which is then handled like the localeID passed to <code>ULocale(String 489 * localeID)</code>. 490 * 491 * <p>Java locale strings consisting of language, country, and 492 * variant will be handled by this form, since the country code 493 * (being shorter than four letters long) will not be interpreted 494 * as a script code. If a script code is present, the final 495 * argument ('c') will be interpreted as the country code. It is 496 * recommended that this constructor only be used to ease porting, 497 * and that clients instead use the single-argument constructor 498 * when constructing a ULocale from a localeID. 499 * @param a first component of the locale id 500 * @param b second component of the locale id 501 * @param c third component of the locale id 502 * @see #ULocale(String) 503 * @stable ICU 3.0 504 */ ULocale(String a, String b, String c)505 public ULocale(String a, String b, String c) { 506 localeID = getName(lscvToID(a, b, c, EMPTY_STRING)); 507 } 508 509 /** 510 * {@icu} Creates a ULocale from the id by first canonicalizing the id. 511 * @param nonCanonicalID the locale id to canonicalize 512 * @return the locale created from the canonical version of the ID. 513 * @stable ICU 3.0 514 */ createCanonical(String nonCanonicalID)515 public static ULocale createCanonical(String nonCanonicalID) { 516 return new ULocale(canonicalize(nonCanonicalID), (Locale)null); 517 } 518 lscvToID(String lang, String script, String country, String variant)519 private static String lscvToID(String lang, String script, String country, String variant) { 520 StringBuilder buf = new StringBuilder(); 521 522 if (lang != null && lang.length() > 0) { 523 buf.append(lang); 524 } 525 if (script != null && script.length() > 0) { 526 buf.append(UNDERSCORE); 527 buf.append(script); 528 } 529 if (country != null && country.length() > 0) { 530 buf.append(UNDERSCORE); 531 buf.append(country); 532 } 533 if (variant != null && variant.length() > 0) { 534 if (country == null || country.length() == 0) { 535 buf.append(UNDERSCORE); 536 } 537 buf.append(UNDERSCORE); 538 buf.append(variant); 539 } 540 return buf.toString(); 541 } 542 543 /** 544 * {@icu} Converts this ULocale object to a {@link java.util.Locale}. 545 * @return a JDK locale that either exactly represents this object 546 * or is the closest approximation. 547 * @stable ICU 2.8 548 */ toLocale()549 public Locale toLocale() { 550 if (locale == null) { 551 locale = JDKLocaleHelper.toLocale(this); 552 } 553 return locale; 554 } 555 556 /** 557 * Keep our own default ULocale. 558 */ 559 private static Locale defaultLocale = Locale.getDefault(); 560 private static ULocale defaultULocale; 561 562 private static Locale[] defaultCategoryLocales = new Locale[Category.values().length]; 563 private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length]; 564 565 static { 566 defaultULocale = forLocale(defaultLocale); 567 568 // For Java 6 or older JRE, ICU initializes the default script from 569 // "user.script" system property. The system property was added 570 // in Java 7. On JRE 7, Locale.getDefault() should reflect the 571 // property value to the Locale's default. So ICU just relies on 572 // Locale.getDefault(). 573 574 // Note: The "user.script" property is only used by initialization. 575 // 576 if (JDKLocaleHelper.hasLocaleCategories()) { 577 for (Category cat: Category.values()) { 578 int idx = cat.ordinal(); 579 defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat); 580 defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]); 581 } 582 } else { 583 // Make sure the current default Locale is original. 584 // If not, it means that someone updated the default Locale. 585 // In this case, user.XXX properties are already out of date 586 // and we should not use user.script. 587 if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) { 588 // Use "user.script" if available 589 String userScript = JDKLocaleHelper.getSystemProperty("user.script"); 590 if (userScript != null && LanguageTag.isScript(userScript)) { 591 // Note: Builder or forLanguageTag cannot be used here 592 // when one of Locale fields is not well-formed. 593 BaseLocale base = defaultULocale.base(); 594 BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript, 595 base.getRegion(), base.getVariant()); 596 defaultULocale = getInstance(newBase, defaultULocale.extensions()); 597 } 598 } 599 600 // Java 6 or older does not have separated category locales, 601 // use the non-category default for all 602 for (Category cat: Category.values()) { 603 int idx = cat.ordinal(); 604 defaultCategoryLocales[idx] = defaultLocale; 605 defaultCategoryULocales[idx] = defaultULocale; 606 } 607 } 608 } 609 610 /** 611 * Returns the current default ULocale. 612 * <p> 613 * The default ULocale is synchronized to the default Java Locale. This method checks 614 * the current default Java Locale and returns an equivalent ULocale. 615 * <p> 616 * <b>Note:</b> Before Java 7, the JDK Locale was not able to represent a locale's script. 617 * Therefore, the script field in the default ULocale is always empty unless 618 * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)} 619 * on Java 6 or older systems. 620 * <p> 621 * <b>Note for ICU 49 or later:</b> Some JRE implementations allow users to override the default 622 * JDK Locale using system properties - <code>user.language</code>, <code>user.country</code> 623 * and <code>user.variant</code>. In addition to these system properties, some Java 7 624 * implementations support <code>user.script</code> for overriding the default Locale's script. 625 * ICU 49 and later versions use the <code>user.script</code> system property on Java 6 626 * or older systems supporting other <code>user.*</code> system properties to initialize 627 * the default ULocale. The <code>user.script</code> override for default ULocale is not 628 * used on Java 7, or if the current Java default Locale is changed after start up. 629 * 630 * @return the default ULocale. 631 * @stable ICU 2.8 632 */ getDefault()633 public static ULocale getDefault() { 634 synchronized (ULocale.class) { 635 if (defaultULocale == null) { 636 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese), 637 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently 638 // stored in a resource bundle. However, UResourceBundle currently requires 639 // non-null default ULocale. For now, this implementation returns ULocale.ROOT 640 // to avoid the problem. 641 642 // TODO: Consider moving BCP47 mapping data out of resource bundle later. 643 644 return ULocale.ROOT; 645 } 646 Locale currentDefault = Locale.getDefault(); 647 if (!defaultLocale.equals(currentDefault)) { 648 defaultLocale = currentDefault; 649 defaultULocale = forLocale(currentDefault); 650 651 if (!JDKLocaleHelper.hasLocaleCategories()) { 652 // Detected Java default Locale change. 653 // We need to update category defaults to match the 654 // Java 7's behavior on Java 6 or older environment. 655 for (Category cat : Category.values()) { 656 int idx = cat.ordinal(); 657 defaultCategoryLocales[idx] = currentDefault; 658 defaultCategoryULocales[idx] = forLocale(currentDefault); 659 } 660 } 661 } 662 return defaultULocale; 663 } 664 } 665 666 /** 667 * Sets the default ULocale. This also sets the default Locale. 668 * If the caller does not have write permission to the 669 * user.language property, a security exception will be thrown, 670 * and the default ULocale will remain unchanged. 671 * <p> 672 * By setting the default ULocale with this method, all of the default categoy locales 673 * are also set to the specified default ULocale. 674 * @param newLocale the new default locale 675 * @throws SecurityException if a security manager exists and its 676 * <code>checkPermission</code> method doesn't allow the operation. 677 * @throws NullPointerException if <code>newLocale</code> is null 678 * @see SecurityManager#checkPermission(java.security.Permission) 679 * @see java.util.PropertyPermission 680 * @see ULocale#setDefault(Category, ULocale) 681 * @stable ICU 3.0 682 */ setDefault(ULocale newLocale)683 public static synchronized void setDefault(ULocale newLocale){ 684 defaultLocale = newLocale.toLocale(); 685 Locale.setDefault(defaultLocale); 686 defaultULocale = newLocale; 687 // This method also updates all category default locales 688 for (Category cat : Category.values()) { 689 setDefault(cat, newLocale); 690 } 691 } 692 693 /** 694 * Returns the current default ULocale for the specified category. 695 * 696 * @param category the category 697 * @return the default ULocale for the specified category. 698 * @stable ICU 49 699 */ getDefault(Category category)700 public static ULocale getDefault(Category category) { 701 synchronized (ULocale.class) { 702 int idx = category.ordinal(); 703 if (defaultCategoryULocales[idx] == null) { 704 // Just in case this method is called during ULocale class 705 // initialization. Unlike getDefault(), we do not have 706 // cyclic dependency for category default. 707 return ULocale.ROOT; 708 } 709 if (JDKLocaleHelper.hasLocaleCategories()) { 710 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category); 711 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) { 712 defaultCategoryLocales[idx] = currentCategoryDefault; 713 defaultCategoryULocales[idx] = forLocale(currentCategoryDefault); 714 } 715 } else { 716 // java.util.Locale.setDefault(Locale) in Java 7 updates 717 // category locale defaults. On Java 6 or older environment, 718 // ICU4J checks if the default locale has changed and update 719 // category ULocales here if necessary. 720 721 // Note: When java.util.Locale.setDefault(Locale) is called 722 // with a Locale same with the previous one, Java 7 still 723 // updates category locale defaults. On Java 6 or older env, 724 // there is no good way to detect the event, ICU4J simply 725 // check if the default Java Locale has changed since last 726 // time. 727 728 Locale currentDefault = Locale.getDefault(); 729 if (!defaultLocale.equals(currentDefault)) { 730 defaultLocale = currentDefault; 731 defaultULocale = forLocale(currentDefault); 732 733 for (Category cat : Category.values()) { 734 int tmpIdx = cat.ordinal(); 735 defaultCategoryLocales[tmpIdx] = currentDefault; 736 defaultCategoryULocales[tmpIdx] = forLocale(currentDefault); 737 } 738 } 739 740 // No synchronization with JDK Locale, because category default 741 // is not supported in Java 6 or older versions 742 } 743 return defaultCategoryULocales[idx]; 744 } 745 } 746 747 /** 748 * Sets the default <code>ULocale</code> for the specified <code>Category</code>. 749 * This also sets the default <code>Locale</code> for the specified <code>Category</code> 750 * of the JVM. If the caller does not have write permission to the 751 * user.language property, a security exception will be thrown, 752 * and the default ULocale for the specified Category will remain unchanged. 753 * 754 * @param category the specified category to set the default locale 755 * @param newLocale the new default locale 756 * @see SecurityManager#checkPermission(java.security.Permission) 757 * @see java.util.PropertyPermission 758 * @stable ICU 49 759 */ setDefault(Category category, ULocale newLocale)760 public static synchronized void setDefault(Category category, ULocale newLocale) { 761 Locale newJavaDefault = newLocale.toLocale(); 762 int idx = category.ordinal(); 763 defaultCategoryULocales[idx] = newLocale; 764 defaultCategoryLocales[idx] = newJavaDefault; 765 JDKLocaleHelper.setDefault(category, newJavaDefault); 766 } 767 768 /** 769 * This is for compatibility with Locale-- in actuality, since ULocale is 770 * immutable, there is no reason to clone it, so this API returns 'this'. 771 * @stable ICU 3.0 772 */ clone()773 public Object clone() { 774 return this; 775 } 776 777 /** 778 * Returns the hashCode. 779 * @stable ICU 3.0 780 */ hashCode()781 public int hashCode() { 782 return localeID.hashCode(); 783 } 784 785 /** 786 * Returns true if the other object is another ULocale with the 787 * same full name. 788 * Note that since names are not canonicalized, two ULocales that 789 * function identically might not compare equal. 790 * 791 * @return true if this Locale is equal to the specified object. 792 * @stable ICU 3.0 793 */ equals(Object obj)794 public boolean equals(Object obj) { 795 if (this == obj) { 796 return true; 797 } 798 if (obj instanceof ULocale) { 799 return localeID.equals(((ULocale)obj).localeID); 800 } 801 return false; 802 } 803 804 /** 805 * Compares two ULocale for ordering. 806 * <p><b>Note:</b> The order might change in future.</p> 807 * 808 * @param other the ULocale to be compared. 809 * @return a negative integer, zero, or a positive integer as this ULocale is less than, equal to, or greater 810 * than the specified ULocale. 811 * @throws NullPointerException if <code>other</code> is null. 812 * 813 * @stable ICU 53 814 */ compareTo(ULocale other)815 public int compareTo(ULocale other) { 816 if (this == other) { 817 return 0; 818 } 819 820 int cmp = 0; 821 822 // Language 823 cmp = getLanguage().compareTo(other.getLanguage()); 824 if (cmp == 0) { 825 // Script 826 cmp = getScript().compareTo(other.getScript()); 827 if (cmp == 0) { 828 // Region 829 cmp = getCountry().compareTo(other.getCountry()); 830 if (cmp == 0) { 831 // Variant 832 cmp = getVariant().compareTo(other.getVariant()); 833 if (cmp == 0) { 834 // Keywords 835 Iterator<String> thisKwdItr = getKeywords(); 836 Iterator<String> otherKwdItr = other.getKeywords(); 837 838 if (thisKwdItr == null) { 839 cmp = otherKwdItr == null ? 0 : -1; 840 } else if (otherKwdItr == null) { 841 cmp = 1; 842 } else { 843 // Both have keywords 844 while (cmp == 0 && thisKwdItr.hasNext()) { 845 if (!otherKwdItr.hasNext()) { 846 cmp = 1; 847 break; 848 } 849 // Compare keyword keys 850 String thisKey = thisKwdItr.next(); 851 String otherKey = otherKwdItr.next(); 852 cmp = thisKey.compareTo(otherKey); 853 if (cmp == 0) { 854 // Compare keyword values 855 String thisVal = getKeywordValue(thisKey); 856 String otherVal = other.getKeywordValue(otherKey); 857 if (thisVal == null) { 858 cmp = otherVal == null ? 0 : -1; 859 } else if (otherVal == null) { 860 cmp = 1; 861 } else { 862 cmp = thisVal.compareTo(otherVal); 863 } 864 } 865 } 866 if (cmp == 0 && otherKwdItr.hasNext()) { 867 cmp = -1; 868 } 869 } 870 } 871 } 872 } 873 } 874 875 // Normalize the result value: 876 // Note: String.compareTo() may return value other than -1, 0, 1. 877 // A value other than those are OK by the definition, but we don't want 878 // associate any semantics other than negative/zero/positive. 879 return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0); 880 } 881 882 /** 883 * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>, 884 * not <code>Locale</code>. Returns a list of all installed locales. 885 * @stable ICU 3.0 886 */ getAvailableLocales()887 public static ULocale[] getAvailableLocales() { 888 return ICUResourceBundle.getAvailableULocales(); 889 } 890 891 /** 892 * Returns a list of all 2-letter country codes defined in ISO 3166. 893 * Can be used to create Locales. 894 * @stable ICU 3.0 895 */ getISOCountries()896 public static String[] getISOCountries() { 897 return LocaleIDs.getISOCountries(); 898 } 899 900 /** 901 * Returns a list of all 2-letter language codes defined in ISO 639. 902 * Can be used to create Locales. 903 * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed. 904 * The list this function returns includes both the new and the old codes for the 905 * languages whose codes have changed.] 906 * @stable ICU 3.0 907 */ getISOLanguages()908 public static String[] getISOLanguages() { 909 return LocaleIDs.getISOLanguages(); 910 } 911 912 /** 913 * Returns the language code for this locale, which will either be the empty string 914 * or a lowercase ISO 639 code. 915 * @see #getDisplayLanguage() 916 * @see #getDisplayLanguage(ULocale) 917 * @stable ICU 3.0 918 */ getLanguage()919 public String getLanguage() { 920 return base().getLanguage(); 921 } 922 923 /** 924 * Returns the language code for the locale ID, 925 * which will either be the empty string 926 * or a lowercase ISO 639 code. 927 * @see #getDisplayLanguage() 928 * @see #getDisplayLanguage(ULocale) 929 * @stable ICU 3.0 930 */ getLanguage(String localeID)931 public static String getLanguage(String localeID) { 932 return new LocaleIDParser(localeID).getLanguage(); 933 } 934 935 /** 936 * Returns the script code for this locale, which might be the empty string. 937 * @see #getDisplayScript() 938 * @see #getDisplayScript(ULocale) 939 * @stable ICU 3.0 940 */ getScript()941 public String getScript() { 942 return base().getScript(); 943 } 944 945 /** 946 * {@icu} Returns the script code for the specified locale, which might be the empty 947 * string. 948 * @see #getDisplayScript() 949 * @see #getDisplayScript(ULocale) 950 * @stable ICU 3.0 951 */ getScript(String localeID)952 public static String getScript(String localeID) { 953 return new LocaleIDParser(localeID).getScript(); 954 } 955 956 /** 957 * Returns the country/region code for this locale, which will either be the empty string 958 * or an uppercase ISO 3166 2-letter code. 959 * @see #getDisplayCountry() 960 * @see #getDisplayCountry(ULocale) 961 * @stable ICU 3.0 962 */ getCountry()963 public String getCountry() { 964 return base().getRegion(); 965 } 966 967 /** 968 * {@icu} Returns the country/region code for this locale, which will either be the empty string 969 * or an uppercase ISO 3166 2-letter code. 970 * @param localeID The locale identification string. 971 * @see #getDisplayCountry() 972 * @see #getDisplayCountry(ULocale) 973 * @stable ICU 3.0 974 */ getCountry(String localeID)975 public static String getCountry(String localeID) { 976 return new LocaleIDParser(localeID).getCountry(); 977 } 978 979 /** 980 * Returns the variant code for this locale, which might be the empty string. 981 * @see #getDisplayVariant() 982 * @see #getDisplayVariant(ULocale) 983 * @stable ICU 3.0 984 */ getVariant()985 public String getVariant() { 986 return base().getVariant(); 987 } 988 989 /** 990 * {@icu} Returns the variant code for the specified locale, which might be the empty string. 991 * @see #getDisplayVariant() 992 * @see #getDisplayVariant(ULocale) 993 * @stable ICU 3.0 994 */ getVariant(String localeID)995 public static String getVariant(String localeID) { 996 return new LocaleIDParser(localeID).getVariant(); 997 } 998 999 /** 1000 * {@icu} Returns the fallback locale for the specified locale, which might be the 1001 * empty string. 1002 * @stable ICU 3.2 1003 */ getFallback(String localeID)1004 public static String getFallback(String localeID) { 1005 return getFallbackString(getName(localeID)); 1006 } 1007 1008 /** 1009 * {@icu} Returns the fallback locale for this locale. If this locale is root, 1010 * returns null. 1011 * @stable ICU 3.2 1012 */ getFallback()1013 public ULocale getFallback() { 1014 if (localeID.length() == 0 || localeID.charAt(0) == '@') { 1015 return null; 1016 } 1017 return new ULocale(getFallbackString(localeID), (Locale)null); 1018 } 1019 1020 /** 1021 * Returns the given (canonical) locale id minus the last part before the tags. 1022 */ getFallbackString(String fallback)1023 private static String getFallbackString(String fallback) { 1024 int extStart = fallback.indexOf('@'); 1025 if (extStart == -1) { 1026 extStart = fallback.length(); 1027 } 1028 int last = fallback.lastIndexOf('_', extStart); 1029 if (last == -1) { 1030 last = 0; 1031 } else { 1032 // truncate empty segment 1033 while (last > 0) { 1034 if (fallback.charAt(last - 1) != '_') { 1035 break; 1036 } 1037 last--; 1038 } 1039 } 1040 return fallback.substring(0, last) + fallback.substring(extStart); 1041 } 1042 1043 /** 1044 * {@icu} Returns the (normalized) base name for this locale, 1045 * like {@link #getName()}, but without keywords. 1046 * 1047 * @return the base name as a String. 1048 * @stable ICU 3.0 1049 */ getBaseName()1050 public String getBaseName() { 1051 return getBaseName(localeID); 1052 } 1053 1054 /** 1055 * {@icu} Returns the (normalized) base name for the specified locale, 1056 * like {@link #getName(String)}, but without keywords. 1057 * 1058 * @param localeID the locale ID as a string 1059 * @return the base name as a String. 1060 * @stable ICU 3.0 1061 */ getBaseName(String localeID)1062 public static String getBaseName(String localeID){ 1063 if (localeID.indexOf('@') == -1) { 1064 return localeID; 1065 } 1066 return new LocaleIDParser(localeID).getBaseName(); 1067 } 1068 1069 /** 1070 * {@icu} Returns the (normalized) full name for this locale. 1071 * 1072 * @return String the full name of the localeID 1073 * @stable ICU 3.0 1074 */ getName()1075 public String getName() { 1076 return localeID; // always normalized 1077 } 1078 1079 /** 1080 * Gets the shortest length subtag's size. 1081 * 1082 * @param localeID 1083 * @return The size of the shortest length subtag 1084 **/ getShortestSubtagLength(String localeID)1085 private static int getShortestSubtagLength(String localeID) { 1086 int localeIDLength = localeID.length(); 1087 int length = localeIDLength; 1088 boolean reset = true; 1089 int tmpLength = 0; 1090 1091 for (int i = 0; i < localeIDLength; i++) { 1092 if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') { 1093 if (reset) { 1094 reset = false; 1095 tmpLength = 0; 1096 } 1097 tmpLength++; 1098 } else { 1099 if (tmpLength != 0 && tmpLength < length) { 1100 length = tmpLength; 1101 } 1102 reset = true; 1103 } 1104 } 1105 1106 return length; 1107 } 1108 1109 /** 1110 * {@icu} Returns the (normalized) full name for the specified locale. 1111 * 1112 * @param localeID the localeID as a string 1113 * @return String the full name of the localeID 1114 * @stable ICU 3.0 1115 */ getName(String localeID)1116 public static String getName(String localeID){ 1117 String tmpLocaleID; 1118 // Convert BCP47 id if necessary 1119 if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) { 1120 tmpLocaleID = forLanguageTag(localeID).getName(); 1121 if (tmpLocaleID.length() == 0) { 1122 tmpLocaleID = localeID; 1123 } 1124 } else { 1125 tmpLocaleID = localeID; 1126 } 1127 String name = nameCache.get(tmpLocaleID); 1128 if (name == null) { 1129 name = new LocaleIDParser(tmpLocaleID).getName(); 1130 nameCache.put(tmpLocaleID, name); 1131 } 1132 return name; 1133 } 1134 1135 /** 1136 * Returns a string representation of this object. 1137 * @stable ICU 3.0 1138 */ toString()1139 public String toString() { 1140 return localeID; 1141 } 1142 1143 /** 1144 * {@icu} Returns an iterator over keywords for this locale. If there 1145 * are no keywords, returns null. 1146 * @return iterator over keywords, or null if there are no keywords. 1147 * @stable ICU 3.0 1148 */ getKeywords()1149 public Iterator<String> getKeywords() { 1150 return getKeywords(localeID); 1151 } 1152 1153 /** 1154 * {@icu} Returns an iterator over keywords for the specified locale. If there 1155 * are no keywords, returns null. 1156 * @return an iterator over the keywords in the specified locale, or null 1157 * if there are no keywords. 1158 * @stable ICU 3.0 1159 */ getKeywords(String localeID)1160 public static Iterator<String> getKeywords(String localeID){ 1161 return new LocaleIDParser(localeID).getKeywords(); 1162 } 1163 1164 /** 1165 * {@icu} Returns the value for a keyword in this locale. If the keyword is not 1166 * defined, returns null. 1167 * @param keywordName name of the keyword whose value is desired. Case insensitive. 1168 * @return the value of the keyword, or null. 1169 * @stable ICU 3.0 1170 */ getKeywordValue(String keywordName)1171 public String getKeywordValue(String keywordName){ 1172 return getKeywordValue(localeID, keywordName); 1173 } 1174 1175 /** 1176 * {@icu} Returns the value for a keyword in the specified locale. If the keyword is 1177 * not defined, returns null. The locale name does not need to be normalized. 1178 * @param keywordName name of the keyword whose value is desired. Case insensitive. 1179 * @return String the value of the keyword as a string 1180 * @stable ICU 3.0 1181 */ getKeywordValue(String localeID, String keywordName)1182 public static String getKeywordValue(String localeID, String keywordName) { 1183 return new LocaleIDParser(localeID).getKeywordValue(keywordName); 1184 } 1185 1186 /** 1187 * {@icu} Returns the canonical name for the specified locale ID. This is used to 1188 * convert POSIX and other grandfathered IDs to standard ICU form. 1189 * @param localeID the locale id 1190 * @return the canonicalized id 1191 * @stable ICU 3.0 1192 */ canonicalize(String localeID)1193 public static String canonicalize(String localeID){ 1194 LocaleIDParser parser = new LocaleIDParser(localeID, true); 1195 String baseName = parser.getBaseName(); 1196 boolean foundVariant = false; 1197 1198 // formerly, we always set to en_US_POSIX if the basename was empty, but 1199 // now we require that the entire id be empty, so that "@foo=bar" 1200 // will pass through unchanged. 1201 // {dlf} I'd rather keep "" unchanged. 1202 if (localeID.equals("")) { 1203 return ""; 1204 // return "en_US_POSIX"; 1205 } 1206 1207 // we have an ID in the form xx_Yyyy_ZZ_KKKKK 1208 1209 initCANONICALIZE_MAP(); 1210 1211 /* convert the variants to appropriate ID */ 1212 for (int i = 0; i < variantsToKeywords.length; i++) { 1213 String[] vals = variantsToKeywords[i]; 1214 int idx = baseName.lastIndexOf("_" + vals[0]); 1215 if (idx > -1) { 1216 foundVariant = true; 1217 1218 baseName = baseName.substring(0, idx); 1219 if (baseName.endsWith("_")) { 1220 baseName = baseName.substring(0, --idx); 1221 } 1222 parser.setBaseName(baseName); 1223 parser.defaultKeywordValue(vals[1], vals[2]); 1224 break; 1225 } 1226 } 1227 1228 /* See if this is an already known locale */ 1229 for (int i = 0; i < CANONICALIZE_MAP.length; i++) { 1230 if (CANONICALIZE_MAP[i][0].equals(baseName)) { 1231 foundVariant = true; 1232 1233 String[] vals = CANONICALIZE_MAP[i]; 1234 parser.setBaseName(vals[1]); 1235 if (vals[2] != null) { 1236 parser.defaultKeywordValue(vals[2], vals[3]); 1237 } 1238 break; 1239 } 1240 } 1241 1242 /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */ 1243 if (!foundVariant) { 1244 if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) { 1245 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null)); 1246 } 1247 } 1248 1249 return parser.getName(); 1250 } 1251 1252 /** 1253 * {@icu} Given a keyword and a value, return a new locale with an updated 1254 * keyword and value. If the keyword is null, this removes all keywords from the locale id. 1255 * Otherwise, if the value is null, this removes the value for this keyword from the 1256 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id. 1257 * The keyword and value must not be empty. 1258 * 1259 * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed. 1260 * 1261 * @param keyword the keyword to add/remove, or null to remove all keywords. 1262 * @param value the value to add/set, or null to remove this particular keyword. 1263 * @return the updated locale 1264 * @stable ICU 3.2 1265 */ setKeywordValue(String keyword, String value)1266 public ULocale setKeywordValue(String keyword, String value) { 1267 return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null); 1268 } 1269 1270 /** 1271 * Given a locale id, a keyword, and a value, return a new locale id with an updated 1272 * keyword and value. If the keyword is null, this removes all keywords from the locale id. 1273 * Otherwise, if the value is null, this removes the value for this keyword from the 1274 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id. 1275 * The keyword and value must not be empty. 1276 * 1277 * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed. 1278 * 1279 * @param localeID the locale id to modify 1280 * @param keyword the keyword to add/remove, or null to remove all keywords. 1281 * @param value the value to add/set, or null to remove this particular keyword. 1282 * @return the updated locale id 1283 * @stable ICU 3.2 1284 */ setKeywordValue(String localeID, String keyword, String value)1285 public static String setKeywordValue(String localeID, String keyword, String value) { 1286 LocaleIDParser parser = new LocaleIDParser(localeID); 1287 parser.setKeywordValue(keyword, value); 1288 return parser.getName(); 1289 } 1290 1291 /* 1292 * Given a locale id, a keyword, and a value, return a new locale id with an updated 1293 * keyword and value, if the keyword does not already have a value. The keyword and 1294 * value must not be null or empty. 1295 * @param localeID the locale id to modify 1296 * @param keyword the keyword to add, if not already present 1297 * @param value the value to add, if not already present 1298 * @return the updated locale id 1299 */ 1300 /* private static String defaultKeywordValue(String localeID, String keyword, String value) { 1301 LocaleIDParser parser = new LocaleIDParser(localeID); 1302 parser.defaultKeywordValue(keyword, value); 1303 return parser.getName(); 1304 }*/ 1305 1306 /** 1307 * Returns a three-letter abbreviation for this locale's language. If the locale 1308 * doesn't specify a language, returns the empty string. Otherwise, returns 1309 * a lowercase ISO 639-2/T language code. 1310 * The ISO 639-2 language codes can be found on-line at 1311 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a> 1312 * @exception MissingResourceException Throws MissingResourceException if the 1313 * three-letter language abbreviation is not available for this locale. 1314 * @stable ICU 3.0 1315 */ getISO3Language()1316 public String getISO3Language(){ 1317 return getISO3Language(localeID); 1318 } 1319 1320 /** 1321 * {@icu} Returns a three-letter abbreviation for this locale's language. If the locale 1322 * doesn't specify a language, returns the empty string. Otherwise, returns 1323 * a lowercase ISO 639-2/T language code. 1324 * The ISO 639-2 language codes can be found on-line at 1325 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a> 1326 * @exception MissingResourceException Throws MissingResourceException if the 1327 * three-letter language abbreviation is not available for this locale. 1328 * @stable ICU 3.0 1329 */ getISO3Language(String localeID)1330 public static String getISO3Language(String localeID) { 1331 return LocaleIDs.getISO3Language(getLanguage(localeID)); 1332 } 1333 1334 /** 1335 * Returns a three-letter abbreviation for this locale's country/region. If the locale 1336 * doesn't specify a country, returns the empty string. Otherwise, returns 1337 * an uppercase ISO 3166 3-letter country code. 1338 * @exception MissingResourceException Throws MissingResourceException if the 1339 * three-letter country abbreviation is not available for this locale. 1340 * @stable ICU 3.0 1341 */ getISO3Country()1342 public String getISO3Country() { 1343 return getISO3Country(localeID); 1344 } 1345 1346 /** 1347 * {@icu} Returns a three-letter abbreviation for this locale's country/region. If the locale 1348 * doesn't specify a country, returns the empty string. Otherwise, returns 1349 * an uppercase ISO 3166 3-letter country code. 1350 * @exception MissingResourceException Throws MissingResourceException if the 1351 * three-letter country abbreviation is not available for this locale. 1352 * @stable ICU 3.0 1353 */ getISO3Country(String localeID)1354 public static String getISO3Country(String localeID) { 1355 return LocaleIDs.getISO3Country(getCountry(localeID)); 1356 } 1357 1358 /** 1359 * Pairs of (language subtag, + or -) for finding out fast if common languages 1360 * are LTR (minus) or RTL (plus). 1361 */ 1362 private static final String LANG_DIR_STRING = 1363 "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-"; 1364 1365 /** 1366 * {@icu} Returns whether this locale's script is written right-to-left. 1367 * If there is no script subtag, then the likely script is used, 1368 * see {@link #addLikelySubtags(ULocale)}. 1369 * If no likely script is known, then false is returned. 1370 * 1371 * <p>A script is right-to-left according to the CLDR script metadata 1372 * which corresponds to whether the script's letters have Bidi_Class=R or AL. 1373 * 1374 * <p>Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". 1375 * 1376 * @return true if the locale's script is written right-to-left 1377 * @draft ICU 54 1378 * @provisional This API might change or be removed in a future release. 1379 */ isRightToLeft()1380 public boolean isRightToLeft() { 1381 String script = getScript(); 1382 if (script.length() == 0) { 1383 // Fastpath: We know the likely scripts and their writing direction 1384 // for some common languages. 1385 String lang = getLanguage(); 1386 if (lang.length() == 0) { 1387 return false; 1388 } 1389 int langIndex = LANG_DIR_STRING.indexOf(lang); 1390 if (langIndex >= 0) { 1391 switch (LANG_DIR_STRING.charAt(langIndex + lang.length())) { 1392 case '-': return false; 1393 case '+': return true; 1394 default: break; // partial match of a longer code 1395 } 1396 } 1397 // Otherwise, find the likely script. 1398 ULocale likely = addLikelySubtags(this); 1399 script = likely.getScript(); 1400 if (script.length() == 0) { 1401 return false; 1402 } 1403 } 1404 int scriptCode = UScript.getCodeFromName(script); 1405 return UScript.isRightToLeft(scriptCode); 1406 } 1407 1408 // display names 1409 1410 /** 1411 * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale. 1412 * @return the localized language name. 1413 * @see Category#DISPLAY 1414 * @stable ICU 3.0 1415 */ getDisplayLanguage()1416 public String getDisplayLanguage() { 1417 return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false); 1418 } 1419 1420 /** 1421 * Returns this locale's language localized for display in the provided locale. 1422 * @param displayLocale the locale in which to display the name. 1423 * @return the localized language name. 1424 * @stable ICU 3.0 1425 */ getDisplayLanguage(ULocale displayLocale)1426 public String getDisplayLanguage(ULocale displayLocale) { 1427 return getDisplayLanguageInternal(this, displayLocale, false); 1428 } 1429 1430 /** 1431 * {@icu} Returns a locale's language localized for display in the provided locale. 1432 * This is a cover for the ICU4C API. 1433 * @param localeID the id of the locale whose language will be displayed 1434 * @param displayLocaleID the id of the locale in which to display the name. 1435 * @return the localized language name. 1436 * @stable ICU 3.0 1437 */ getDisplayLanguage(String localeID, String displayLocaleID)1438 public static String getDisplayLanguage(String localeID, String displayLocaleID) { 1439 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID), 1440 false); 1441 } 1442 1443 /** 1444 * {@icu} Returns a locale's language localized for display in the provided locale. 1445 * This is a cover for the ICU4C API. 1446 * @param localeID the id of the locale whose language will be displayed. 1447 * @param displayLocale the locale in which to display the name. 1448 * @return the localized language name. 1449 * @stable ICU 3.0 1450 */ getDisplayLanguage(String localeID, ULocale displayLocale)1451 public static String getDisplayLanguage(String localeID, ULocale displayLocale) { 1452 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false); 1453 } 1454 /** 1455 * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale. 1456 * If a dialect name is present in the data, then it is returned. 1457 * @return the localized language name. 1458 * @see Category#DISPLAY 1459 * @stable ICU 4.4 1460 */ getDisplayLanguageWithDialect()1461 public String getDisplayLanguageWithDialect() { 1462 return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true); 1463 } 1464 1465 /** 1466 * {@icu} Returns this locale's language localized for display in the provided locale. 1467 * If a dialect name is present in the data, then it is returned. 1468 * @param displayLocale the locale in which to display the name. 1469 * @return the localized language name. 1470 * @stable ICU 4.4 1471 */ getDisplayLanguageWithDialect(ULocale displayLocale)1472 public String getDisplayLanguageWithDialect(ULocale displayLocale) { 1473 return getDisplayLanguageInternal(this, displayLocale, true); 1474 } 1475 1476 /** 1477 * {@icu} Returns a locale's language localized for display in the provided locale. 1478 * If a dialect name is present in the data, then it is returned. 1479 * This is a cover for the ICU4C API. 1480 * @param localeID the id of the locale whose language will be displayed 1481 * @param displayLocaleID the id of the locale in which to display the name. 1482 * @return the localized language name. 1483 * @stable ICU 4.4 1484 */ getDisplayLanguageWithDialect(String localeID, String displayLocaleID)1485 public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) { 1486 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID), 1487 true); 1488 } 1489 1490 /** 1491 * {@icu} Returns a locale's language localized for display in the provided locale. 1492 * If a dialect name is present in the data, then it is returned. 1493 * This is a cover for the ICU4C API. 1494 * @param localeID the id of the locale whose language will be displayed. 1495 * @param displayLocale the locale in which to display the name. 1496 * @return the localized language name. 1497 * @stable ICU 4.4 1498 */ getDisplayLanguageWithDialect(String localeID, ULocale displayLocale)1499 public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) { 1500 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true); 1501 } 1502 getDisplayLanguageInternal(ULocale locale, ULocale displayLocale, boolean useDialect)1503 private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale, 1504 boolean useDialect) { 1505 String lang = useDialect ? locale.getBaseName() : locale.getLanguage(); 1506 return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang); 1507 } 1508 1509 /** 1510 * Returns this locale's script localized for display in the default <code>DISPLAY</code> locale. 1511 * @return the localized script name. 1512 * @see Category#DISPLAY 1513 * @stable ICU 3.0 1514 */ getDisplayScript()1515 public String getDisplayScript() { 1516 return getDisplayScriptInternal(this, getDefault(Category.DISPLAY)); 1517 } 1518 1519 /** 1520 * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale. 1521 * @return the localized script name. 1522 * @see Category#DISPLAY 1523 * @internal 1524 * @deprecated This API is ICU internal only. 1525 */ 1526 @Deprecated getDisplayScriptInContext()1527 public String getDisplayScriptInContext() { 1528 return getDisplayScriptInContextInternal(this, getDefault(Category.DISPLAY)); 1529 } 1530 1531 /** 1532 * Returns this locale's script localized for display in the provided locale. 1533 * @param displayLocale the locale in which to display the name. 1534 * @return the localized script name. 1535 * @stable ICU 3.0 1536 */ getDisplayScript(ULocale displayLocale)1537 public String getDisplayScript(ULocale displayLocale) { 1538 return getDisplayScriptInternal(this, displayLocale); 1539 } 1540 1541 /** 1542 * {@icu} Returns this locale's script localized for display in the provided locale. 1543 * @param displayLocale the locale in which to display the name. 1544 * @return the localized script name. 1545 * @internal 1546 * @deprecated This API is ICU internal only. 1547 */ 1548 @Deprecated getDisplayScriptInContext(ULocale displayLocale)1549 public String getDisplayScriptInContext(ULocale displayLocale) { 1550 return getDisplayScriptInContextInternal(this, displayLocale); 1551 } 1552 1553 /** 1554 * {@icu} Returns a locale's script localized for display in the provided locale. 1555 * This is a cover for the ICU4C API. 1556 * @param localeID the id of the locale whose script will be displayed 1557 * @param displayLocaleID the id of the locale in which to display the name. 1558 * @return the localized script name. 1559 * @stable ICU 3.0 1560 */ getDisplayScript(String localeID, String displayLocaleID)1561 public static String getDisplayScript(String localeID, String displayLocaleID) { 1562 return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID)); 1563 } 1564 /** 1565 * {@icu} Returns a locale's script localized for display in the provided locale. 1566 * This is a cover for the ICU4C API. 1567 * @param localeID the id of the locale whose script will be displayed 1568 * @param displayLocaleID the id of the locale in which to display the name. 1569 * @return the localized script name. 1570 * @internal 1571 * @deprecated This API is ICU internal only. 1572 */ 1573 @Deprecated getDisplayScriptInContext(String localeID, String displayLocaleID)1574 public static String getDisplayScriptInContext(String localeID, String displayLocaleID) { 1575 return getDisplayScriptInContextInternal(new ULocale(localeID), new ULocale(displayLocaleID)); 1576 } 1577 1578 /** 1579 * {@icu} Returns a locale's script localized for display in the provided locale. 1580 * @param localeID the id of the locale whose script will be displayed. 1581 * @param displayLocale the locale in which to display the name. 1582 * @return the localized script name. 1583 * @stable ICU 3.0 1584 */ getDisplayScript(String localeID, ULocale displayLocale)1585 public static String getDisplayScript(String localeID, ULocale displayLocale) { 1586 return getDisplayScriptInternal(new ULocale(localeID), displayLocale); 1587 } 1588 /** 1589 * {@icu} Returns a locale's script localized for display in the provided locale. 1590 * @param localeID the id of the locale whose script will be displayed. 1591 * @param displayLocale the locale in which to display the name. 1592 * @return the localized script name. 1593 * @internal 1594 * @deprecated This API is ICU internal only. 1595 */ 1596 @Deprecated getDisplayScriptInContext(String localeID, ULocale displayLocale)1597 public static String getDisplayScriptInContext(String localeID, ULocale displayLocale) { 1598 return getDisplayScriptInContextInternal(new ULocale(localeID), displayLocale); 1599 } 1600 1601 // displayLocaleID is canonical, localeID need not be since parsing will fix this. getDisplayScriptInternal(ULocale locale, ULocale displayLocale)1602 private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) { 1603 return LocaleDisplayNames.getInstance(displayLocale) 1604 .scriptDisplayName(locale.getScript()); 1605 } 1606 getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale)1607 private static String getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale) { 1608 return LocaleDisplayNames.getInstance(displayLocale) 1609 .scriptDisplayNameInContext(locale.getScript()); 1610 } 1611 1612 /** 1613 * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale. 1614 * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR"). 1615 * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead. 1616 * @return the localized country name. 1617 * @see Category#DISPLAY 1618 * @stable ICU 3.0 1619 */ getDisplayCountry()1620 public String getDisplayCountry() { 1621 return getDisplayCountryInternal(this, getDefault(Category.DISPLAY)); 1622 } 1623 1624 /** 1625 * Returns this locale's country localized for display in the provided locale. 1626 * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR"). 1627 * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead. 1628 * @param displayLocale the locale in which to display the name. 1629 * @return the localized country name. 1630 * @stable ICU 3.0 1631 */ getDisplayCountry(ULocale displayLocale)1632 public String getDisplayCountry(ULocale displayLocale){ 1633 return getDisplayCountryInternal(this, displayLocale); 1634 } 1635 1636 /** 1637 * {@icu} Returns a locale's country localized for display in the provided locale. 1638 * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR"). 1639 * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead. 1640 * This is a cover for the ICU4C API. 1641 * @param localeID the id of the locale whose country will be displayed 1642 * @param displayLocaleID the id of the locale in which to display the name. 1643 * @return the localized country name. 1644 * @stable ICU 3.0 1645 */ getDisplayCountry(String localeID, String displayLocaleID)1646 public static String getDisplayCountry(String localeID, String displayLocaleID) { 1647 return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID)); 1648 } 1649 1650 /** 1651 * {@icu} Returns a locale's country localized for display in the provided locale. 1652 * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR"). 1653 * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead. 1654 * This is a cover for the ICU4C API. 1655 * @param localeID the id of the locale whose country will be displayed. 1656 * @param displayLocale the locale in which to display the name. 1657 * @return the localized country name. 1658 * @stable ICU 3.0 1659 */ getDisplayCountry(String localeID, ULocale displayLocale)1660 public static String getDisplayCountry(String localeID, ULocale displayLocale) { 1661 return getDisplayCountryInternal(new ULocale(localeID), displayLocale); 1662 } 1663 1664 // displayLocaleID is canonical, localeID need not be since parsing will fix this. getDisplayCountryInternal(ULocale locale, ULocale displayLocale)1665 private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) { 1666 return LocaleDisplayNames.getInstance(displayLocale) 1667 .regionDisplayName(locale.getCountry()); 1668 } 1669 1670 /** 1671 * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale. 1672 * @return the localized variant name. 1673 * @see Category#DISPLAY 1674 * @stable ICU 3.0 1675 */ getDisplayVariant()1676 public String getDisplayVariant() { 1677 return getDisplayVariantInternal(this, getDefault(Category.DISPLAY)); 1678 } 1679 1680 /** 1681 * Returns this locale's variant localized for display in the provided locale. 1682 * @param displayLocale the locale in which to display the name. 1683 * @return the localized variant name. 1684 * @stable ICU 3.0 1685 */ getDisplayVariant(ULocale displayLocale)1686 public String getDisplayVariant(ULocale displayLocale) { 1687 return getDisplayVariantInternal(this, displayLocale); 1688 } 1689 1690 /** 1691 * {@icu} Returns a locale's variant localized for display in the provided locale. 1692 * This is a cover for the ICU4C API. 1693 * @param localeID the id of the locale whose variant will be displayed 1694 * @param displayLocaleID the id of the locale in which to display the name. 1695 * @return the localized variant name. 1696 * @stable ICU 3.0 1697 */ getDisplayVariant(String localeID, String displayLocaleID)1698 public static String getDisplayVariant(String localeID, String displayLocaleID){ 1699 return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID)); 1700 } 1701 1702 /** 1703 * {@icu} Returns a locale's variant localized for display in the provided locale. 1704 * This is a cover for the ICU4C API. 1705 * @param localeID the id of the locale whose variant will be displayed. 1706 * @param displayLocale the locale in which to display the name. 1707 * @return the localized variant name. 1708 * @stable ICU 3.0 1709 */ getDisplayVariant(String localeID, ULocale displayLocale)1710 public static String getDisplayVariant(String localeID, ULocale displayLocale) { 1711 return getDisplayVariantInternal(new ULocale(localeID), displayLocale); 1712 } 1713 getDisplayVariantInternal(ULocale locale, ULocale displayLocale)1714 private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) { 1715 return LocaleDisplayNames.getInstance(displayLocale) 1716 .variantDisplayName(locale.getVariant()); 1717 } 1718 1719 /** 1720 * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale. 1721 * @param keyword the keyword to be displayed. 1722 * @return the localized keyword name. 1723 * @see #getKeywords() 1724 * @see Category#DISPLAY 1725 * @stable ICU 3.0 1726 */ getDisplayKeyword(String keyword)1727 public static String getDisplayKeyword(String keyword) { 1728 return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY)); 1729 } 1730 1731 /** 1732 * {@icu} Returns a keyword localized for display in the specified locale. 1733 * @param keyword the keyword to be displayed. 1734 * @param displayLocaleID the id of the locale in which to display the keyword. 1735 * @return the localized keyword name. 1736 * @see #getKeywords(String) 1737 * @stable ICU 3.0 1738 */ getDisplayKeyword(String keyword, String displayLocaleID)1739 public static String getDisplayKeyword(String keyword, String displayLocaleID) { 1740 return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID)); 1741 } 1742 1743 /** 1744 * {@icu} Returns a keyword localized for display in the specified locale. 1745 * @param keyword the keyword to be displayed. 1746 * @param displayLocale the locale in which to display the keyword. 1747 * @return the localized keyword name. 1748 * @see #getKeywords(String) 1749 * @stable ICU 3.0 1750 */ getDisplayKeyword(String keyword, ULocale displayLocale)1751 public static String getDisplayKeyword(String keyword, ULocale displayLocale) { 1752 return getDisplayKeywordInternal(keyword, displayLocale); 1753 } 1754 getDisplayKeywordInternal(String keyword, ULocale displayLocale)1755 private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) { 1756 return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword); 1757 } 1758 1759 /** 1760 * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale. 1761 * @param keyword the keyword whose value is to be displayed. 1762 * @return the localized value name. 1763 * @see Category#DISPLAY 1764 * @stable ICU 3.0 1765 */ getDisplayKeywordValue(String keyword)1766 public String getDisplayKeywordValue(String keyword) { 1767 return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY)); 1768 } 1769 1770 /** 1771 * {@icu} Returns a keyword value localized for display in the specified locale. 1772 * @param keyword the keyword whose value is to be displayed. 1773 * @param displayLocale the locale in which to display the value. 1774 * @return the localized value name. 1775 * @stable ICU 3.0 1776 */ getDisplayKeywordValue(String keyword, ULocale displayLocale)1777 public String getDisplayKeywordValue(String keyword, ULocale displayLocale) { 1778 return getDisplayKeywordValueInternal(this, keyword, displayLocale); 1779 } 1780 1781 /** 1782 * {@icu} Returns a keyword value localized for display in the specified locale. 1783 * This is a cover for the ICU4C API. 1784 * @param localeID the id of the locale whose keyword value is to be displayed. 1785 * @param keyword the keyword whose value is to be displayed. 1786 * @param displayLocaleID the id of the locale in which to display the value. 1787 * @return the localized value name. 1788 * @stable ICU 3.0 1789 */ getDisplayKeywordValue(String localeID, String keyword, String displayLocaleID)1790 public static String getDisplayKeywordValue(String localeID, String keyword, 1791 String displayLocaleID) { 1792 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, 1793 new ULocale(displayLocaleID)); 1794 } 1795 1796 /** 1797 * {@icu} Returns a keyword value localized for display in the specified locale. 1798 * This is a cover for the ICU4C API. 1799 * @param localeID the id of the locale whose keyword value is to be displayed. 1800 * @param keyword the keyword whose value is to be displayed. 1801 * @param displayLocale the id of the locale in which to display the value. 1802 * @return the localized value name. 1803 * @stable ICU 3.0 1804 */ getDisplayKeywordValue(String localeID, String keyword, ULocale displayLocale)1805 public static String getDisplayKeywordValue(String localeID, String keyword, 1806 ULocale displayLocale) { 1807 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale); 1808 } 1809 1810 // displayLocaleID is canonical, localeID need not be since parsing will fix this. getDisplayKeywordValueInternal(ULocale locale, String keyword, ULocale displayLocale)1811 private static String getDisplayKeywordValueInternal(ULocale locale, String keyword, 1812 ULocale displayLocale) { 1813 keyword = AsciiUtil.toLowerString(keyword.trim()); 1814 String value = locale.getKeywordValue(keyword); 1815 return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value); 1816 } 1817 1818 /** 1819 * Returns this locale name localized for display in the default <code>DISPLAY</code> locale. 1820 * @return the localized locale name. 1821 * @see Category#DISPLAY 1822 * @stable ICU 3.0 1823 */ getDisplayName()1824 public String getDisplayName() { 1825 return getDisplayNameInternal(this, getDefault(Category.DISPLAY)); 1826 } 1827 1828 /** 1829 * Returns this locale name localized for display in the provided locale. 1830 * @param displayLocale the locale in which to display the locale name. 1831 * @return the localized locale name. 1832 * @stable ICU 3.0 1833 */ getDisplayName(ULocale displayLocale)1834 public String getDisplayName(ULocale displayLocale) { 1835 return getDisplayNameInternal(this, displayLocale); 1836 } 1837 1838 /** 1839 * {@icu} Returns the locale ID localized for display in the provided locale. 1840 * This is a cover for the ICU4C API. 1841 * @param localeID the locale whose name is to be displayed. 1842 * @param displayLocaleID the id of the locale in which to display the locale name. 1843 * @return the localized locale name. 1844 * @stable ICU 3.0 1845 */ getDisplayName(String localeID, String displayLocaleID)1846 public static String getDisplayName(String localeID, String displayLocaleID) { 1847 return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID)); 1848 } 1849 1850 /** 1851 * {@icu} Returns the locale ID localized for display in the provided locale. 1852 * This is a cover for the ICU4C API. 1853 * @param localeID the locale whose name is to be displayed. 1854 * @param displayLocale the locale in which to display the locale name. 1855 * @return the localized locale name. 1856 * @stable ICU 3.0 1857 */ getDisplayName(String localeID, ULocale displayLocale)1858 public static String getDisplayName(String localeID, ULocale displayLocale) { 1859 return getDisplayNameInternal(new ULocale(localeID), displayLocale); 1860 } 1861 getDisplayNameInternal(ULocale locale, ULocale displayLocale)1862 private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) { 1863 return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale); 1864 } 1865 1866 /** 1867 * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale. 1868 * If a dialect name is present in the locale data, then it is returned. 1869 * @return the localized locale name. 1870 * @see Category#DISPLAY 1871 * @stable ICU 4.4 1872 */ getDisplayNameWithDialect()1873 public String getDisplayNameWithDialect() { 1874 return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY)); 1875 } 1876 1877 /** 1878 * {@icu} Returns this locale name localized for display in the provided locale. 1879 * If a dialect name is present in the locale data, then it is returned. 1880 * @param displayLocale the locale in which to display the locale name. 1881 * @return the localized locale name. 1882 * @stable ICU 4.4 1883 */ getDisplayNameWithDialect(ULocale displayLocale)1884 public String getDisplayNameWithDialect(ULocale displayLocale) { 1885 return getDisplayNameWithDialectInternal(this, displayLocale); 1886 } 1887 1888 /** 1889 * {@icu} Returns the locale ID localized for display in the provided locale. 1890 * If a dialect name is present in the locale data, then it is returned. 1891 * This is a cover for the ICU4C API. 1892 * @param localeID the locale whose name is to be displayed. 1893 * @param displayLocaleID the id of the locale in which to display the locale name. 1894 * @return the localized locale name. 1895 * @stable ICU 4.4 1896 */ getDisplayNameWithDialect(String localeID, String displayLocaleID)1897 public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) { 1898 return getDisplayNameWithDialectInternal(new ULocale(localeID), 1899 new ULocale(displayLocaleID)); 1900 } 1901 1902 /** 1903 * {@icu} Returns the locale ID localized for display in the provided locale. 1904 * If a dialect name is present in the locale data, then it is returned. 1905 * This is a cover for the ICU4C API. 1906 * @param localeID the locale whose name is to be displayed. 1907 * @param displayLocale the locale in which to display the locale name. 1908 * @return the localized locale name. 1909 * @stable ICU 4.4 1910 */ getDisplayNameWithDialect(String localeID, ULocale displayLocale)1911 public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) { 1912 return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale); 1913 } 1914 getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale)1915 private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) { 1916 return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES) 1917 .localeDisplayName(locale); 1918 } 1919 1920 /** 1921 * {@icu} Returns this locale's layout orientation for characters. The possible 1922 * values are "left-to-right", "right-to-left", "top-to-bottom" or 1923 * "bottom-to-top". 1924 * @return The locale's layout orientation for characters. 1925 * @stable ICU 4.0 1926 */ getCharacterOrientation()1927 public String getCharacterOrientation() { 1928 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this, 1929 "layout", "characters"); 1930 } 1931 1932 /** 1933 * {@icu} Returns this locale's layout orientation for lines. The possible 1934 * values are "left-to-right", "right-to-left", "top-to-bottom" or 1935 * "bottom-to-top". 1936 * @return The locale's layout orientation for lines. 1937 * @stable ICU 4.0 1938 */ getLineOrientation()1939 public String getLineOrientation() { 1940 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this, 1941 "layout", "lines"); 1942 } 1943 1944 /** 1945 * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the 1946 * resource containing the data. This is always at or above the 1947 * valid locale. If the valid locale does not contain the 1948 * specific data being requested, then the actual locale will be 1949 * above the valid locale. If the object was not constructed from 1950 * locale data, then the valid locale is <i>null</i>. 1951 * 1952 * @draft ICU 2.8 (retain) 1953 * @provisional This API might change or be removed in a future release. 1954 */ 1955 public static Type ACTUAL_LOCALE = new Type(); 1956 1957 /** 1958 * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific 1959 * locale for which any data exists. This is always at or above 1960 * the requested locale, and at or below the actual locale. If 1961 * the requested locale does not correspond to any resource data, 1962 * then the valid locale will be above the requested locale. If 1963 * the object was not constructed from locale data, then the 1964 * actual locale is <i>null</i>. 1965 * 1966 * <p>Note: The valid locale will be returned correctly in ICU 1967 * 3.0 or later. In ICU 2.8, it is not returned correctly. 1968 * @draft ICU 2.8 (retain) 1969 * @provisional This API might change or be removed in a future release. 1970 */ 1971 public static Type VALID_LOCALE = new Type(); 1972 1973 /** 1974 * Opaque selector enum for <tt>getLocale()</tt>. 1975 * @see com.ibm.icu.util.ULocale 1976 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE 1977 * @see com.ibm.icu.util.ULocale#VALID_LOCALE 1978 * @draft ICU 2.8 (retainAll) 1979 * @provisional This API might change or be removed in a future release. 1980 */ 1981 public static final class Type { Type()1982 private Type() {} 1983 } 1984 1985 /** 1986 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available 1987 * locale for the user. NullPointerException is thrown if acceptLanguageList or 1988 * availableLocales is null. If fallback is non-null, it will contain true if a 1989 * fallback locale (one not in the acceptLanguageList) was returned. The value on 1990 * entry is ignored. ULocale will be one of the locales in availableLocales, or the 1991 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in 1992 * availableLocales matched). No ULocale array element should be null; behavior is 1993 * undefined if this is the case. 1994 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales 1995 * @param availableLocales list of available locales. One of these will be returned. 1996 * @param fallback if non-null, a 1-element array containing a boolean to be set with 1997 * the fallback status 1998 * @return one of the locales from the availableLocales list, or null if none match 1999 * @stable ICU 3.4 2000 */ acceptLanguage(String acceptLanguageList, ULocale[] availableLocales, boolean[] fallback)2001 public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales, 2002 boolean[] fallback) { 2003 if (acceptLanguageList == null) { 2004 throw new NullPointerException(); 2005 } 2006 ULocale acceptList[] = null; 2007 try { 2008 acceptList = parseAcceptLanguage(acceptLanguageList, true); 2009 } catch (ParseException pe) { 2010 acceptList = null; 2011 } 2012 if (acceptList == null) { 2013 return null; 2014 } 2015 return acceptLanguage(acceptList, availableLocales, fallback); 2016 } 2017 2018 /** 2019 * {@icu} Based on a list of acceptable locales, determine an available locale for the 2020 * user. NullPointerException is thrown if acceptLanguageList or availableLocales is 2021 * null. If fallback is non-null, it will contain true if a fallback locale (one not 2022 * in the acceptLanguageList) was returned. The value on entry is ignored. ULocale 2023 * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT 2024 * locale was used as a fallback (because nothing else in availableLocales matched). 2025 * No ULocale array element should be null; behavior is undefined if this is the case. 2026 * @param acceptLanguageList list of acceptable locales 2027 * @param availableLocales list of available locales. One of these will be returned. 2028 * @param fallback if non-null, a 1-element array containing a boolean to be set with 2029 * the fallback status 2030 * @return one of the locales from the availableLocales list, or null if none match 2031 * @stable ICU 3.4 2032 */ 2033 acceptLanguage(ULocale[] acceptLanguageList, ULocale[] availableLocales, boolean[] fallback)2034 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[] 2035 availableLocales, boolean[] fallback) { 2036 // fallbacklist 2037 int i,j; 2038 if(fallback != null) { 2039 fallback[0]=true; 2040 } 2041 for(i=0;i<acceptLanguageList.length;i++) { 2042 ULocale aLocale = acceptLanguageList[i]; 2043 boolean[] setFallback = fallback; 2044 do { 2045 for(j=0;j<availableLocales.length;j++) { 2046 if(availableLocales[j].equals(aLocale)) { 2047 if(setFallback != null) { 2048 setFallback[0]=false; // first time with this locale - not a fallback. 2049 } 2050 return availableLocales[j]; 2051 } 2052 // compare to scriptless alias, so locales such as 2053 // zh_TW, zh_CN are considered as available locales - see #7190 2054 if (aLocale.getScript().length() == 0 2055 && availableLocales[j].getScript().length() > 0 2056 && availableLocales[j].getLanguage().equals(aLocale.getLanguage()) 2057 && availableLocales[j].getCountry().equals(aLocale.getCountry()) 2058 && availableLocales[j].getVariant().equals(aLocale.getVariant())) { 2059 ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]); 2060 if (minAvail.getScript().length() == 0) { 2061 if(setFallback != null) { 2062 setFallback[0] = false; // not a fallback. 2063 } 2064 return aLocale; 2065 } 2066 } 2067 } 2068 Locale loc = aLocale.toLocale(); 2069 Locale parent = LocaleUtility.fallback(loc); 2070 if(parent != null) { 2071 aLocale = new ULocale(parent); 2072 } else { 2073 aLocale = null; 2074 } 2075 setFallback = null; // Do not set fallback in later iterations 2076 } while (aLocale != null); 2077 } 2078 return null; 2079 } 2080 2081 /** 2082 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available 2083 * locale for the user. NullPointerException is thrown if acceptLanguageList or 2084 * availableLocales is null. If fallback is non-null, it will contain true if a 2085 * fallback locale (one not in the acceptLanguageList) was returned. The value on 2086 * entry is ignored. ULocale will be one of the locales in availableLocales, or the 2087 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in 2088 * availableLocales matched). No ULocale array element should be null; behavior is 2089 * undefined if this is the case. This function will choose a locale from the 2090 * ULocale.getAvailableLocales() list as available. 2091 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales 2092 * @param fallback if non-null, a 1-element array containing a boolean to be set with 2093 * the fallback status 2094 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if 2095 * none match 2096 * @stable ICU 3.4 2097 */ acceptLanguage(String acceptLanguageList, boolean[] fallback)2098 public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) { 2099 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(), 2100 fallback); 2101 } 2102 2103 /** 2104 * {@icu} Based on an ordered array of acceptable locales, determine an available 2105 * locale for the user. NullPointerException is thrown if acceptLanguageList or 2106 * availableLocales is null. If fallback is non-null, it will contain true if a 2107 * fallback locale (one not in the acceptLanguageList) was returned. The value on 2108 * entry is ignored. ULocale will be one of the locales in availableLocales, or the 2109 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in 2110 * availableLocales matched). No ULocale array element should be null; behavior is 2111 * undefined if this is the case. This function will choose a locale from the 2112 * ULocale.getAvailableLocales() list as available. 2113 * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first) 2114 * @param fallback if non-null, a 1-element array containing a boolean to be set with 2115 * the fallback status 2116 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match 2117 * @stable ICU 3.4 2118 */ acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback)2119 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) { 2120 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(), 2121 fallback); 2122 } 2123 2124 /** 2125 * Package local method used for parsing Accept-Language string 2126 */ parseAcceptLanguage(String acceptLanguage, boolean isLenient)2127 static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient) 2128 throws ParseException { 2129 class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> { 2130 private double q; 2131 private double serial; 2132 public ULocaleAcceptLanguageQ(double theq, int theserial) { 2133 q = theq; 2134 serial = theserial; 2135 } 2136 public int compareTo(ULocaleAcceptLanguageQ other) { 2137 if (q > other.q) { // reverse - to sort in descending order 2138 return -1; 2139 } else if (q < other.q) { 2140 return 1; 2141 } 2142 if (serial < other.serial) { 2143 return -1; 2144 } else if (serial > other.serial) { 2145 return 1; 2146 } else { 2147 return 0; // same object 2148 } 2149 } 2150 } 2151 2152 // parse out the acceptLanguage into an array 2153 TreeMap<ULocaleAcceptLanguageQ, ULocale> map = 2154 new TreeMap<ULocaleAcceptLanguageQ, ULocale>(); 2155 StringBuilder languageRangeBuf = new StringBuilder(); 2156 StringBuilder qvalBuf = new StringBuilder(); 2157 int state = 0; 2158 acceptLanguage += ","; // append comma to simplify the parsing code 2159 int n; 2160 boolean subTag = false; 2161 boolean q1 = false; 2162 for (n = 0; n < acceptLanguage.length(); n++) { 2163 boolean gotLanguageQ = false; 2164 char c = acceptLanguage.charAt(n); 2165 switch (state) { 2166 case 0: // before language-range start 2167 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) { 2168 // in language-range 2169 languageRangeBuf.append(c); 2170 state = 1; 2171 subTag = false; 2172 } else if (c == '*') { 2173 languageRangeBuf.append(c); 2174 state = 2; 2175 } else if (c != ' ' && c != '\t') { 2176 // invalid character 2177 state = -1; 2178 } 2179 break; 2180 case 1: // in language-range 2181 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) { 2182 languageRangeBuf.append(c); 2183 } else if (c == '-') { 2184 subTag = true; 2185 languageRangeBuf.append(c); 2186 } else if (c == '_') { 2187 if (isLenient) { 2188 subTag = true; 2189 languageRangeBuf.append(c); 2190 } else { 2191 state = -1; 2192 } 2193 } else if ('0' <= c && c <= '9') { 2194 if (subTag) { 2195 languageRangeBuf.append(c); 2196 } else { 2197 // DIGIT is allowed only in language sub tag 2198 state = -1; 2199 } 2200 } else if (c == ',') { 2201 // language-q end 2202 gotLanguageQ = true; 2203 } else if (c == ' ' || c == '\t') { 2204 // language-range end 2205 state = 3; 2206 } else if (c == ';') { 2207 // before q 2208 state = 4; 2209 } else { 2210 // invalid character for language-range 2211 state = -1; 2212 } 2213 break; 2214 case 2: // saw wild card range 2215 if (c == ',') { 2216 // language-q end 2217 gotLanguageQ = true; 2218 } else if (c == ' ' || c == '\t') { 2219 // language-range end 2220 state = 3; 2221 } else if (c == ';') { 2222 // before q 2223 state = 4; 2224 } else { 2225 // invalid 2226 state = -1; 2227 } 2228 break; 2229 case 3: // language-range end 2230 if (c == ',') { 2231 // language-q end 2232 gotLanguageQ = true; 2233 } else if (c == ';') { 2234 // before q 2235 state =4; 2236 } else if (c != ' ' && c != '\t') { 2237 // invalid 2238 state = -1; 2239 } 2240 break; 2241 case 4: // before q 2242 if (c == 'q') { 2243 // before equal 2244 state = 5; 2245 } else if (c != ' ' && c != '\t') { 2246 // invalid 2247 state = -1; 2248 } 2249 break; 2250 case 5: // before equal 2251 if (c == '=') { 2252 // before q value 2253 state = 6; 2254 } else if (c != ' ' && c != '\t') { 2255 // invalid 2256 state = -1; 2257 } 2258 break; 2259 case 6: // before q value 2260 if (c == '0') { 2261 // q value start with 0 2262 q1 = false; 2263 qvalBuf.append(c); 2264 state = 7; 2265 } else if (c == '1') { 2266 // q value start with 1 2267 qvalBuf.append(c); 2268 state = 7; 2269 } else if (c == '.') { 2270 if (isLenient) { 2271 qvalBuf.append(c); 2272 state = 8; 2273 } else { 2274 state = -1; 2275 } 2276 } else if (c != ' ' && c != '\t') { 2277 // invalid 2278 state = -1; 2279 } 2280 break; 2281 case 7: // q value start 2282 if (c == '.') { 2283 // before q value fraction part 2284 qvalBuf.append(c); 2285 state = 8; 2286 } else if (c == ',') { 2287 // language-q end 2288 gotLanguageQ = true; 2289 } else if (c == ' ' || c == '\t') { 2290 // after q value 2291 state = 10; 2292 } else { 2293 // invalid 2294 state = -1; 2295 } 2296 break; 2297 case 8: // before q value fraction part 2298 if ('0' <= c || c <= '9') { 2299 if (q1 && c != '0' && !isLenient) { 2300 // if q value starts with 1, the fraction part must be 0 2301 state = -1; 2302 } else { 2303 // in q value fraction part 2304 qvalBuf.append(c); 2305 state = 9; 2306 } 2307 } else { 2308 // invalid 2309 state = -1; 2310 } 2311 break; 2312 case 9: // in q value fraction part 2313 if ('0' <= c && c <= '9') { 2314 if (q1 && c != '0') { 2315 // if q value starts with 1, the fraction part must be 0 2316 state = -1; 2317 } else { 2318 qvalBuf.append(c); 2319 } 2320 } else if (c == ',') { 2321 // language-q end 2322 gotLanguageQ = true; 2323 } else if (c == ' ' || c == '\t') { 2324 // after q value 2325 state = 10; 2326 } else { 2327 // invalid 2328 state = -1; 2329 } 2330 break; 2331 case 10: // after q value 2332 if (c == ',') { 2333 // language-q end 2334 gotLanguageQ = true; 2335 } else if (c != ' ' && c != '\t') { 2336 // invalid 2337 state = -1; 2338 } 2339 break; 2340 } 2341 if (state == -1) { 2342 // error state 2343 throw new ParseException("Invalid Accept-Language", n); 2344 } 2345 if (gotLanguageQ) { 2346 double q = 1.0; 2347 if (qvalBuf.length() != 0) { 2348 try { 2349 q = Double.parseDouble(qvalBuf.toString()); 2350 } catch (NumberFormatException nfe) { 2351 // Already validated, so it should never happen 2352 q = 1.0; 2353 } 2354 if (q > 1.0) { 2355 q = 1.0; 2356 } 2357 } 2358 if (languageRangeBuf.charAt(0) != '*') { 2359 int serial = map.size(); 2360 ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial); 2361 // sort in reverse order.. 1.0, 0.9, 0.8 .. etc 2362 map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString()))); 2363 } 2364 2365 // reset buffer and parse state 2366 languageRangeBuf.setLength(0); 2367 qvalBuf.setLength(0); 2368 state = 0; 2369 } 2370 } 2371 if (state != 0) { 2372 // Well, the parser should handle all cases. So just in case. 2373 throw new ParseException("Invalid AcceptlLanguage", n); 2374 } 2375 2376 // pull out the map 2377 ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]); 2378 return acceptList; 2379 } 2380 2381 private static final String UNDEFINED_LANGUAGE = "und"; 2382 private static final String UNDEFINED_SCRIPT = "Zzzz"; 2383 private static final String UNDEFINED_REGION = "ZZ"; 2384 2385 /** 2386 * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm 2387 * described in the following CLDR technical report: 2388 * 2389 * http://www.unicode.org/reports/tr35/#Likely_Subtags 2390 * 2391 * If the provided ULocale instance is already in the maximal form, or there is no 2392 * data available available for maximization, it will be returned. For example, 2393 * "und-Zzzz" cannot be maximized, since there is no reasonable maximization. 2394 * Otherwise, a new ULocale instance with the maximal form is returned. 2395 * 2396 * Examples: 2397 * 2398 * "en" maximizes to "en_Latn_US" 2399 * 2400 * "de" maximizes to "de_Latn_US" 2401 * 2402 * "sr" maximizes to "sr_Cyrl_RS" 2403 * 2404 * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) 2405 * 2406 * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) 2407 * 2408 * @param loc The ULocale to maximize 2409 * @return The maximized ULocale instance. 2410 * @stable ICU 4.0 2411 */ addLikelySubtags(ULocale loc)2412 public static ULocale addLikelySubtags(ULocale loc) { 2413 String[] tags = new String[3]; 2414 String trailing = null; 2415 2416 int trailingIndex = parseTagString( 2417 loc.localeID, 2418 tags); 2419 2420 if (trailingIndex < loc.localeID.length()) { 2421 trailing = loc.localeID.substring(trailingIndex); 2422 } 2423 2424 String newLocaleID = 2425 createLikelySubtagsString( 2426 tags[0], 2427 tags[1], 2428 tags[2], 2429 trailing); 2430 2431 return newLocaleID == null ? loc : new ULocale(newLocaleID); 2432 } 2433 2434 /** 2435 * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described 2436 * in the following CLDR technical report:<blockquote> 2437 * 2438 * <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags" 2439 *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote> 2440 * 2441 * If the provided ULocale instance is already in the minimal form, or there 2442 * is no data available for minimization, it will be returned. Since the 2443 * minimization algorithm relies on proper maximization, see the comments 2444 * for addLikelySubtags for reasons why there might not be any data. 2445 * 2446 * Examples:<pre> 2447 * 2448 * "en_Latn_US" minimizes to "en" 2449 * 2450 * "de_Latn_US" minimizes to "de" 2451 * 2452 * "sr_Cyrl_RS" minimizes to "sr" 2453 * 2454 * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the 2455 * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre> 2456 * 2457 * @param loc The ULocale to minimize 2458 * @return The minimized ULocale instance. 2459 * @stable ICU 4.0 2460 */ minimizeSubtags(ULocale loc)2461 public static ULocale minimizeSubtags(ULocale loc) { 2462 return minimizeSubtags(loc, Minimize.FAVOR_REGION); 2463 } 2464 2465 /** 2466 * Options for minimizeSubtags. 2467 * @internal 2468 * @deprecated This API is ICU internal only. 2469 */ 2470 @Deprecated 2471 public enum Minimize { 2472 /** 2473 * Favor including the script, when either the region <b>or</b> the script could be suppressed, but not both. 2474 * @internal 2475 * @deprecated This API is ICU internal only. 2476 */ 2477 @Deprecated 2478 FAVOR_SCRIPT, 2479 /** 2480 * Favor including the region, when either the region <b>or</b> the script could be suppressed, but not both. 2481 * @internal 2482 * @deprecated This API is ICU internal only. 2483 */ 2484 @Deprecated 2485 FAVOR_REGION 2486 } 2487 2488 /** 2489 * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described 2490 * in the following CLDR technical report:<blockquote> 2491 * 2492 * <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags" 2493 *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote> 2494 * 2495 * If the provided ULocale instance is already in the minimal form, or there 2496 * is no data available for minimization, it will be returned. Since the 2497 * minimization algorithm relies on proper maximization, see the comments 2498 * for addLikelySubtags for reasons why there might not be any data. 2499 * 2500 * Examples:<pre> 2501 * 2502 * "en_Latn_US" minimizes to "en" 2503 * 2504 * "de_Latn_US" minimizes to "de" 2505 * 2506 * "sr_Cyrl_RS" minimizes to "sr" 2507 * 2508 * "zh_Hant_TW" minimizes to "zh_TW" if fieldToFavor == {@link Minimize#FAVOR_REGION} 2509 * "zh_Hant_TW" minimizes to "zh_Hant" if fieldToFavor == {@link Minimize#FAVOR_SCRIPT} 2510 * </pre> 2511 * The fieldToFavor only has an effect if either the region or the script could be suppressed, but not both. 2512 * @param loc The ULocale to minimize 2513 * @param fieldToFavor Indicate which should be preferred, when either the region <b>or</b> the script could be suppressed, but not both. 2514 * @return The minimized ULocale instance. 2515 * @internal 2516 * @deprecated This API is ICU internal only. 2517 */ 2518 @Deprecated minimizeSubtags(ULocale loc, Minimize fieldToFavor)2519 public static ULocale minimizeSubtags(ULocale loc, Minimize fieldToFavor) { 2520 String[] tags = new String[3]; 2521 2522 int trailingIndex = parseTagString( 2523 loc.localeID, 2524 tags); 2525 2526 String originalLang = tags[0]; 2527 String originalScript = tags[1]; 2528 String originalRegion = tags[2]; 2529 String originalTrailing = null; 2530 2531 if (trailingIndex < loc.localeID.length()) { 2532 /* 2533 * Create a String that contains everything 2534 * after the language, script, and region. 2535 */ 2536 originalTrailing = loc.localeID.substring(trailingIndex); 2537 } 2538 2539 /** 2540 * First, we need to first get the maximization 2541 * by adding any likely subtags. 2542 **/ 2543 String maximizedLocaleID = 2544 createLikelySubtagsString( 2545 originalLang, 2546 originalScript, 2547 originalRegion, 2548 null); 2549 2550 /** 2551 * If maximization fails, there's nothing 2552 * we can do. 2553 **/ 2554 if (isEmptyString(maximizedLocaleID)) { 2555 return loc; 2556 } 2557 else { 2558 /** 2559 * Start first with just the language. 2560 **/ 2561 String tag = 2562 createLikelySubtagsString( 2563 originalLang, 2564 null, 2565 null, 2566 null); 2567 2568 if (tag.equals(maximizedLocaleID)) { 2569 String newLocaleID = 2570 createTagString( 2571 originalLang, 2572 null, 2573 null, 2574 originalTrailing); 2575 2576 return new ULocale(newLocaleID); 2577 } 2578 } 2579 2580 /** 2581 * Next, try the language and region. 2582 **/ 2583 if (fieldToFavor == Minimize.FAVOR_REGION) { 2584 if (originalRegion.length() != 0) { 2585 String tag = 2586 createLikelySubtagsString( 2587 originalLang, 2588 null, 2589 originalRegion, 2590 null); 2591 2592 if (tag.equals(maximizedLocaleID)) { 2593 String newLocaleID = 2594 createTagString( 2595 originalLang, 2596 null, 2597 originalRegion, 2598 originalTrailing); 2599 2600 return new ULocale(newLocaleID); 2601 } 2602 } 2603 if (originalScript.length() != 0){ 2604 String tag = 2605 createLikelySubtagsString( 2606 originalLang, 2607 originalScript, 2608 null, 2609 null); 2610 2611 if (tag.equals(maximizedLocaleID)) { 2612 String newLocaleID = 2613 createTagString( 2614 originalLang, 2615 originalScript, 2616 null, 2617 originalTrailing); 2618 2619 return new ULocale(newLocaleID); 2620 } 2621 } 2622 } else { // FAVOR_SCRIPT, so 2623 if (originalScript.length() != 0){ 2624 String tag = 2625 createLikelySubtagsString( 2626 originalLang, 2627 originalScript, 2628 null, 2629 null); 2630 2631 if (tag.equals(maximizedLocaleID)) { 2632 String newLocaleID = 2633 createTagString( 2634 originalLang, 2635 originalScript, 2636 null, 2637 originalTrailing); 2638 2639 return new ULocale(newLocaleID); 2640 } 2641 } 2642 if (originalRegion.length() != 0) { 2643 String tag = 2644 createLikelySubtagsString( 2645 originalLang, 2646 null, 2647 originalRegion, 2648 null); 2649 2650 if (tag.equals(maximizedLocaleID)) { 2651 String newLocaleID = 2652 createTagString( 2653 originalLang, 2654 null, 2655 originalRegion, 2656 originalTrailing); 2657 2658 return new ULocale(newLocaleID); 2659 } 2660 } 2661 } 2662 return loc; 2663 } 2664 2665 /** 2666 * A trivial utility function that checks for a null 2667 * reference or checks the length of the supplied String. 2668 * 2669 * @param string The string to check 2670 * 2671 * @return true if the String is empty, or if the reference is null. 2672 */ isEmptyString(String string)2673 private static boolean isEmptyString(String string) { 2674 return string == null || string.length() == 0; 2675 } 2676 2677 /** 2678 * Append a tag to a StringBuilder, adding the separator if necessary.The tag must 2679 * not be a zero-length string. 2680 * 2681 * @param tag The tag to add. 2682 * @param buffer The output buffer. 2683 **/ appendTag(String tag, StringBuilder buffer)2684 private static void appendTag(String tag, StringBuilder buffer) { 2685 if (buffer.length() != 0) { 2686 buffer.append(UNDERSCORE); 2687 } 2688 2689 buffer.append(tag); 2690 } 2691 2692 /** 2693 * Create a tag string from the supplied parameters. The lang, script and region 2694 * parameters may be null references. 2695 * 2696 * If any of the language, script or region parameters are empty, and the alternateTags 2697 * parameter is not null, it will be parsed for potential language, script and region tags 2698 * to be used when constructing the new tag. If the alternateTags parameter is null, or 2699 * it contains no language tag, the default tag for the unknown language is used. 2700 * 2701 * @param lang The language tag to use. 2702 * @param script The script tag to use. 2703 * @param region The region tag to use. 2704 * @param trailing Any trailing data to append to the new tag. 2705 * @param alternateTags A string containing any alternate tags. 2706 * @return The new tag string. 2707 **/ createTagString(String lang, String script, String region, String trailing, String alternateTags)2708 private static String createTagString(String lang, String script, String region, 2709 String trailing, String alternateTags) { 2710 2711 LocaleIDParser parser = null; 2712 boolean regionAppended = false; 2713 2714 StringBuilder tag = new StringBuilder(); 2715 2716 if (!isEmptyString(lang)) { 2717 appendTag( 2718 lang, 2719 tag); 2720 } 2721 else if (isEmptyString(alternateTags)) { 2722 /* 2723 * Append the value for an unknown language, if 2724 * we found no language. 2725 */ 2726 appendTag( 2727 UNDEFINED_LANGUAGE, 2728 tag); 2729 } 2730 else { 2731 parser = new LocaleIDParser(alternateTags); 2732 2733 String alternateLang = parser.getLanguage(); 2734 2735 /* 2736 * Append the value for an unknown language, if 2737 * we found no language. 2738 */ 2739 appendTag( 2740 !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE, 2741 tag); 2742 } 2743 2744 if (!isEmptyString(script)) { 2745 appendTag( 2746 script, 2747 tag); 2748 } 2749 else if (!isEmptyString(alternateTags)) { 2750 /* 2751 * Parse the alternateTags string for the script. 2752 */ 2753 if (parser == null) { 2754 parser = new LocaleIDParser(alternateTags); 2755 } 2756 2757 String alternateScript = parser.getScript(); 2758 2759 if (!isEmptyString(alternateScript)) { 2760 appendTag( 2761 alternateScript, 2762 tag); 2763 } 2764 } 2765 2766 if (!isEmptyString(region)) { 2767 appendTag( 2768 region, 2769 tag); 2770 2771 regionAppended = true; 2772 } 2773 else if (!isEmptyString(alternateTags)) { 2774 /* 2775 * Parse the alternateTags string for the region. 2776 */ 2777 if (parser == null) { 2778 parser = new LocaleIDParser(alternateTags); 2779 } 2780 2781 String alternateRegion = parser.getCountry(); 2782 2783 if (!isEmptyString(alternateRegion)) { 2784 appendTag( 2785 alternateRegion, 2786 tag); 2787 2788 regionAppended = true; 2789 } 2790 } 2791 2792 if (trailing != null && trailing.length() > 1) { 2793 /* 2794 * The current ICU format expects two underscores 2795 * will separate the variant from the preceeding 2796 * parts of the tag, if there is no region. 2797 */ 2798 int separators = 0; 2799 2800 if (trailing.charAt(0) == UNDERSCORE) { 2801 if (trailing.charAt(1) == UNDERSCORE) { 2802 separators = 2; 2803 } 2804 } 2805 else { 2806 separators = 1; 2807 } 2808 2809 if (regionAppended) { 2810 /* 2811 * If we appended a region, we may need to strip 2812 * the extra separator from the variant portion. 2813 */ 2814 if (separators == 2) { 2815 tag.append(trailing.substring(1)); 2816 } 2817 else { 2818 tag.append(trailing); 2819 } 2820 } 2821 else { 2822 /* 2823 * If we did not append a region, we may need to add 2824 * an extra separator to the variant portion. 2825 */ 2826 if (separators == 1) { 2827 tag.append(UNDERSCORE); 2828 } 2829 tag.append(trailing); 2830 } 2831 } 2832 2833 return tag.toString(); 2834 } 2835 2836 /** 2837 * Create a tag string from the supplied parameters. The lang, script and region 2838 * parameters may be null references.If the lang parameter is an empty string, the 2839 * default value for an unknown language is written to the output buffer. 2840 * 2841 * @param lang The language tag to use. 2842 * @param script The script tag to use. 2843 * @param region The region tag to use. 2844 * @param trailing Any trailing data to append to the new tag. 2845 * @return The new String. 2846 **/ createTagString(String lang, String script, String region, String trailing)2847 static String createTagString(String lang, String script, String region, String trailing) { 2848 return createTagString(lang, script, region, trailing, null); 2849 } 2850 2851 /** 2852 * Parse the language, script, and region subtags from a tag string, and return the results. 2853 * 2854 * This function does not return the canonical strings for the unknown script and region. 2855 * 2856 * @param localeID The locale ID to parse. 2857 * @param tags An array of three String references to return the subtag strings. 2858 * @return The number of chars of the localeID parameter consumed. 2859 **/ parseTagString(String localeID, String tags[])2860 private static int parseTagString(String localeID, String tags[]) { 2861 LocaleIDParser parser = new LocaleIDParser(localeID); 2862 2863 String lang = parser.getLanguage(); 2864 String script = parser.getScript(); 2865 String region = parser.getCountry(); 2866 2867 if (isEmptyString(lang)) { 2868 tags[0] = UNDEFINED_LANGUAGE; 2869 } 2870 else { 2871 tags[0] = lang; 2872 } 2873 2874 if (script.equals(UNDEFINED_SCRIPT)) { 2875 tags[1] = ""; 2876 } 2877 else { 2878 tags[1] = script; 2879 } 2880 2881 if (region.equals(UNDEFINED_REGION)) { 2882 tags[2] = ""; 2883 } 2884 else { 2885 tags[2] = region; 2886 } 2887 2888 /* 2889 * Search for the variant. If there is one, then return the index of 2890 * the preceeding separator. 2891 * If there's no variant, search for the keyword delimiter, 2892 * and return its index. Otherwise, return the length of the 2893 * string. 2894 * 2895 * $TOTO(dbertoni) we need to take into account that we might 2896 * find a part of the language as the variant, since it can 2897 * can have a variant portion that is long enough to contain 2898 * the same characters as the variant. 2899 */ 2900 String variant = parser.getVariant(); 2901 2902 if (!isEmptyString(variant)){ 2903 int index = localeID.indexOf(variant); 2904 2905 2906 return index > 0 ? index - 1 : index; 2907 } 2908 else 2909 { 2910 int index = localeID.indexOf('@'); 2911 2912 return index == -1 ? localeID.length() : index; 2913 } 2914 } 2915 lookupLikelySubtags(String localeId)2916 private static String lookupLikelySubtags(String localeId) { 2917 UResourceBundle bundle = 2918 UResourceBundle.getBundleInstance( 2919 ICUResourceBundle.ICU_BASE_NAME, "likelySubtags"); 2920 try { 2921 return bundle.getString(localeId); 2922 } 2923 catch(MissingResourceException e) { 2924 return null; 2925 } 2926 } 2927 createLikelySubtagsString(String lang, String script, String region, String variants)2928 private static String createLikelySubtagsString(String lang, String script, String region, 2929 String variants) { 2930 2931 /** 2932 * Try the language with the script and region first. 2933 */ 2934 if (!isEmptyString(script) && !isEmptyString(region)) { 2935 2936 String searchTag = 2937 createTagString( 2938 lang, 2939 script, 2940 region, 2941 null); 2942 2943 String likelySubtags = lookupLikelySubtags(searchTag); 2944 2945 /* 2946 if (likelySubtags == null) { 2947 if (likelySubtags2 != null) { 2948 System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\""); 2949 } 2950 } 2951 else if (likelySubtags2 == null) { 2952 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\""); 2953 } 2954 else if (!likelySubtags.equals(likelySubtags2)) { 2955 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2 2956 + "\""); 2957 } 2958 */ 2959 if (likelySubtags != null) { 2960 // Always use the language tag from the 2961 // maximal string, since it may be more 2962 // specific than the one provided. 2963 return createTagString( 2964 null, 2965 null, 2966 null, 2967 variants, 2968 likelySubtags); 2969 } 2970 } 2971 2972 /** 2973 * Try the language with just the script. 2974 **/ 2975 if (!isEmptyString(script)) { 2976 2977 String searchTag = 2978 createTagString( 2979 lang, 2980 script, 2981 null, 2982 null); 2983 2984 String likelySubtags = lookupLikelySubtags(searchTag); 2985 if (likelySubtags != null) { 2986 // Always use the language tag from the 2987 // maximal string, since it may be more 2988 // specific than the one provided. 2989 return createTagString( 2990 null, 2991 null, 2992 region, 2993 variants, 2994 likelySubtags); 2995 } 2996 } 2997 2998 /** 2999 * Try the language with just the region. 3000 **/ 3001 if (!isEmptyString(region)) { 3002 3003 String searchTag = 3004 createTagString( 3005 lang, 3006 null, 3007 region, 3008 null); 3009 3010 String likelySubtags = lookupLikelySubtags(searchTag); 3011 3012 if (likelySubtags != null) { 3013 // Always use the language tag from the 3014 // maximal string, since it may be more 3015 // specific than the one provided. 3016 return createTagString( 3017 null, 3018 script, 3019 null, 3020 variants, 3021 likelySubtags); 3022 } 3023 } 3024 3025 /** 3026 * Finally, try just the language. 3027 **/ 3028 { 3029 String searchTag = 3030 createTagString( 3031 lang, 3032 null, 3033 null, 3034 null); 3035 3036 String likelySubtags = lookupLikelySubtags(searchTag); 3037 3038 if (likelySubtags != null) { 3039 // Always use the language tag from the 3040 // maximal string, since it may be more 3041 // specific than the one provided. 3042 return createTagString( 3043 null, 3044 script, 3045 region, 3046 variants, 3047 likelySubtags); 3048 } 3049 } 3050 3051 return null; 3052 } 3053 3054 // -------------------------------- 3055 // BCP47/OpenJDK APIs 3056 // -------------------------------- 3057 3058 /** 3059 * The key for the private use locale extension ('x'). 3060 * 3061 * @see #getExtension(char) 3062 * @see Builder#setExtension(char, String) 3063 * 3064 * @stable ICU 4.2 3065 */ 3066 public static final char PRIVATE_USE_EXTENSION = 'x'; 3067 3068 /** 3069 * The key for Unicode locale extension ('u'). 3070 * 3071 * @see #getExtension(char) 3072 * @see Builder#setExtension(char, String) 3073 * 3074 * @stable ICU 4.2 3075 */ 3076 public static final char UNICODE_LOCALE_EXTENSION = 'u'; 3077 3078 /** 3079 * Returns the extension (or private use) value associated with 3080 * the specified key, or null if there is no extension 3081 * associated with the key. To be well-formed, the key must be one 3082 * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so 3083 * for example 'z' and 'Z' represent the same extension. 3084 * 3085 * @param key the extension key 3086 * @return The extension, or null if this locale defines no 3087 * extension for the specified key. 3088 * @throws IllegalArgumentException if key is not well-formed 3089 * @see #PRIVATE_USE_EXTENSION 3090 * @see #UNICODE_LOCALE_EXTENSION 3091 * 3092 * @stable ICU 4.2 3093 */ getExtension(char key)3094 public String getExtension(char key) { 3095 if (!LocaleExtensions.isValidKey(key)) { 3096 throw new IllegalArgumentException("Invalid extension key: " + key); 3097 } 3098 return extensions().getExtensionValue(key); 3099 } 3100 3101 /** 3102 * Returns the set of extension keys associated with this locale, or the 3103 * empty set if it has no extensions. The returned set is unmodifiable. 3104 * The keys will all be lower-case. 3105 * 3106 * @return the set of extension keys, or the empty set if this locale has 3107 * no extensions 3108 * @stable ICU 4.2 3109 */ getExtensionKeys()3110 public Set<Character> getExtensionKeys() { 3111 return extensions().getKeys(); 3112 } 3113 3114 /** 3115 * Returns the set of unicode locale attributes associated with 3116 * this locale, or the empty set if it has no attributes. The 3117 * returned set is unmodifiable. 3118 * 3119 * @return The set of attributes. 3120 * @stable ICU 4.6 3121 */ getUnicodeLocaleAttributes()3122 public Set<String> getUnicodeLocaleAttributes() { 3123 return extensions().getUnicodeLocaleAttributes(); 3124 } 3125 3126 /** 3127 * Returns the Unicode locale type associated with the specified Unicode locale key 3128 * for this locale. Returns the empty string for keys that are defined with no type. 3129 * Returns null if the key is not defined. Keys are case-insensitive. The key must 3130 * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is 3131 * thrown. 3132 * 3133 * @param key the Unicode locale key 3134 * @return The Unicode locale type associated with the key, or null if the 3135 * locale does not define the key. 3136 * @throws IllegalArgumentException if the key is not well-formed 3137 * @throws NullPointerException if <code>key</code> is null 3138 * 3139 * @stable ICU 4.4 3140 */ getUnicodeLocaleType(String key)3141 public String getUnicodeLocaleType(String key) { 3142 if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) { 3143 throw new IllegalArgumentException("Invalid Unicode locale key: " + key); 3144 } 3145 return extensions().getUnicodeLocaleType(key); 3146 } 3147 3148 /** 3149 * Returns the set of Unicode locale keys defined by this locale, or the empty set if 3150 * this locale has none. The returned set is immutable. Keys are all lower case. 3151 * 3152 * @return The set of Unicode locale keys, or the empty set if this locale has 3153 * no Unicode locale keywords. 3154 * 3155 * @stable ICU 4.4 3156 */ getUnicodeLocaleKeys()3157 public Set<String> getUnicodeLocaleKeys() { 3158 return extensions().getUnicodeLocaleKeys(); 3159 } 3160 3161 /** 3162 * Returns a well-formed IETF BCP 47 language tag representing 3163 * this locale. 3164 * 3165 * <p>If this <code>ULocale</code> has a language, script, country, or 3166 * variant that does not satisfy the IETF BCP 47 language tag 3167 * syntax requirements, this method handles these fields as 3168 * described below: 3169 * 3170 * <p><b>Language:</b> If language is empty, or not well-formed 3171 * (for example "a" or "e2"), it will be emitted as "und" (Undetermined). 3172 * 3173 * <p><b>Script:</b> If script is not well-formed (for example "12" 3174 * or "Latin"), it will be omitted. 3175 * 3176 * <p><b>Country:</b> If country is not well-formed (for example "12" 3177 * or "USA"), it will be omitted. 3178 * 3179 * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment 3180 * (delimited by '-' or '_') is emitted as a subtag. Otherwise: 3181 * <ul> 3182 * 3183 * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code> 3184 * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first 3185 * ill-formed sub-segment and all following will be appended to 3186 * the private use subtag. The first appended subtag will be 3187 * "lvariant", followed by the sub-segments in order, separated by 3188 * hyphen. For example, "x-lvariant-WIN", 3189 * "Oracle-x-lvariant-JDK-Standard-Edition". 3190 * 3191 * <li>if any sub-segment does not match 3192 * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated 3193 * and the problematic sub-segment and all following sub-segments 3194 * will be omitted. If the remainder is non-empty, it will be 3195 * emitted as a private use subtag as above (even if the remainder 3196 * turns out to be well-formed). For example, 3197 * "Solaris_isjustthecoolestthing" is emitted as 3198 * "x-lvariant-Solaris", not as "solaris".</li></ul> 3199 * 3200 * <p><b>Note:</b> Although the language tag created by this 3201 * method is well-formed (satisfies the syntax requirements 3202 * defined by the IETF BCP 47 specification), it is not 3203 * necessarily a valid BCP 47 language tag. For example, 3204 * <pre> 3205 * new Locale("xx", "YY").toLanguageTag();</pre> 3206 * 3207 * will return "xx-YY", but the language subtag "xx" and the 3208 * region subtag "YY" are invalid because they are not registered 3209 * in the IANA Language Subtag Registry. 3210 * 3211 * @return a BCP47 language tag representing the locale 3212 * @see #forLanguageTag(String) 3213 * 3214 * @stable ICU 4.2 3215 */ toLanguageTag()3216 public String toLanguageTag() { 3217 BaseLocale base = base(); 3218 LocaleExtensions exts = extensions(); 3219 3220 if (base.getVariant().equalsIgnoreCase("POSIX")) { 3221 // special handling for variant POSIX 3222 base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), ""); 3223 if (exts.getUnicodeLocaleType("va") == null) { 3224 // add va-posix 3225 InternalLocaleBuilder ilocbld = new InternalLocaleBuilder(); 3226 try { 3227 ilocbld.setLocale(BaseLocale.ROOT, exts); 3228 ilocbld.setUnicodeLocaleKeyword("va", "posix"); 3229 exts = ilocbld.getLocaleExtensions(); 3230 } catch (LocaleSyntaxException e) { 3231 // this should not happen 3232 throw new RuntimeException(e); 3233 } 3234 } 3235 } 3236 3237 LanguageTag tag = LanguageTag.parseLocale(base, exts); 3238 3239 StringBuilder buf = new StringBuilder(); 3240 String subtag = tag.getLanguage(); 3241 if (subtag.length() > 0) { 3242 buf.append(LanguageTag.canonicalizeLanguage(subtag)); 3243 } 3244 3245 subtag = tag.getScript(); 3246 if (subtag.length() > 0) { 3247 buf.append(LanguageTag.SEP); 3248 buf.append(LanguageTag.canonicalizeScript(subtag)); 3249 } 3250 3251 subtag = tag.getRegion(); 3252 if (subtag.length() > 0) { 3253 buf.append(LanguageTag.SEP); 3254 buf.append(LanguageTag.canonicalizeRegion(subtag)); 3255 } 3256 3257 List<String>subtags = tag.getVariants(); 3258 for (String s : subtags) { 3259 buf.append(LanguageTag.SEP); 3260 buf.append(LanguageTag.canonicalizeVariant(s)); 3261 } 3262 3263 subtags = tag.getExtensions(); 3264 for (String s : subtags) { 3265 buf.append(LanguageTag.SEP); 3266 buf.append(LanguageTag.canonicalizeExtension(s)); 3267 } 3268 3269 subtag = tag.getPrivateuse(); 3270 if (subtag.length() > 0) { 3271 if (buf.length() > 0) { 3272 buf.append(LanguageTag.SEP); 3273 } 3274 buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP); 3275 buf.append(LanguageTag.canonicalizePrivateuse(subtag)); 3276 } 3277 3278 return buf.toString(); 3279 } 3280 3281 /** 3282 * Returns a locale for the specified IETF BCP 47 language tag string. 3283 * 3284 * <p>If the specified language tag contains any ill-formed subtags, 3285 * the first such subtag and all following subtags are ignored. Compare 3286 * to {@link ULocale.Builder#setLanguageTag} which throws an exception 3287 * in this case. 3288 * 3289 * <p>The following <b>conversions</b> are performed:<ul> 3290 * 3291 * <li>The language code "und" is mapped to language "". 3292 * 3293 * <li>The portion of a private use subtag prefixed by "lvariant", 3294 * if any, is removed and appended to the variant field in the 3295 * result locale (without case normalization). If it is then 3296 * empty, the private use subtag is discarded: 3297 * 3298 * <pre> 3299 * ULocale loc; 3300 * loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j); 3301 * loc.getVariant(); // returns "ICU4J" 3302 * loc.getExtension('x'); // returns null 3303 * 3304 * loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def"); 3305 * loc.getVariant(); // returns "ICU4J_ABC_DEF" 3306 * loc.getExtension('x'); // returns "urp" 3307 * </pre> 3308 * 3309 * <li>When the languageTag argument contains an extlang subtag, 3310 * the first such subtag is used as the language, and the primary 3311 * language subtag and other extlang subtags are ignored: 3312 * 3313 * <pre> 3314 * ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao" 3315 * ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US" 3316 * </pre> 3317 * 3318 * <li>Case is normalized. Language is normalized to lower case, 3319 * script to title case, country to upper case, variant to upper case, 3320 * and extensions to lower case. 3321 * 3322 * <p>This implements the 'Language-Tag' production of BCP47, and 3323 * so supports grandfathered (regular and irregular) as well as 3324 * private use language tags. Stand alone private use tags are 3325 * represented as empty language and extension 'x-whatever', 3326 * and grandfathered tags are converted to their canonical replacements 3327 * where they exist. 3328 * 3329 * <p>Grandfathered tags with canonical replacements are as follows: 3330 * 3331 * <table> 3332 * <tbody align="center"> 3333 * <tr><th>grandfathered tag</th><th> </th><th>modern replacement</th></tr> 3334 * <tr><td>art-lojban</td><td> </td><td>jbo</td></tr> 3335 * <tr><td>i-ami</td><td> </td><td>ami</td></tr> 3336 * <tr><td>i-bnn</td><td> </td><td>bnn</td></tr> 3337 * <tr><td>i-hak</td><td> </td><td>hak</td></tr> 3338 * <tr><td>i-klingon</td><td> </td><td>tlh</td></tr> 3339 * <tr><td>i-lux</td><td> </td><td>lb</td></tr> 3340 * <tr><td>i-navajo</td><td> </td><td>nv</td></tr> 3341 * <tr><td>i-pwn</td><td> </td><td>pwn</td></tr> 3342 * <tr><td>i-tao</td><td> </td><td>tao</td></tr> 3343 * <tr><td>i-tay</td><td> </td><td>tay</td></tr> 3344 * <tr><td>i-tsu</td><td> </td><td>tsu</td></tr> 3345 * <tr><td>no-bok</td><td> </td><td>nb</td></tr> 3346 * <tr><td>no-nyn</td><td> </td><td>nn</td></tr> 3347 * <tr><td>sgn-BE-FR</td><td> </td><td>sfb</td></tr> 3348 * <tr><td>sgn-BE-NL</td><td> </td><td>vgt</td></tr> 3349 * <tr><td>sgn-CH-DE</td><td> </td><td>sgg</td></tr> 3350 * <tr><td>zh-guoyu</td><td> </td><td>cmn</td></tr> 3351 * <tr><td>zh-hakka</td><td> </td><td>hak</td></tr> 3352 * <tr><td>zh-min-nan</td><td> </td><td>nan</td></tr> 3353 * <tr><td>zh-xiang</td><td> </td><td>hsn</td></tr> 3354 * </tbody> 3355 * </table> 3356 * 3357 * <p>Grandfathered tags with no modern replacement will be 3358 * converted as follows: 3359 * 3360 * <table> 3361 * <tbody align="center"> 3362 * <tr><th>grandfathered tag</th><th> </th><th>converts to</th></tr> 3363 * <tr><td>cel-gaulish</td><td> </td><td>xtg-x-cel-gaulish</td></tr> 3364 * <tr><td>en-GB-oed</td><td> </td><td>en-GB-x-oed</td></tr> 3365 * <tr><td>i-default</td><td> </td><td>en-x-i-default</td></tr> 3366 * <tr><td>i-enochian</td><td> </td><td>und-x-i-enochian</td></tr> 3367 * <tr><td>i-mingo</td><td> </td><td>see-x-i-mingo</td></tr> 3368 * <tr><td>zh-min</td><td> </td><td>nan-x-zh-min</td></tr> 3369 * </tbody> 3370 * </table> 3371 * 3372 * <p>For a list of all grandfathered tags, see the 3373 * IANA Language Subtag Registry (search for "Type: grandfathered"). 3374 * 3375 * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code> 3376 * and <code>forLanguageTag</code> will round-trip. 3377 * 3378 * @param languageTag the language tag 3379 * @return The locale that best represents the language tag. 3380 * @throws NullPointerException if <code>languageTag</code> is <code>null</code> 3381 * @see #toLanguageTag() 3382 * @see ULocale.Builder#setLanguageTag(String) 3383 * 3384 * @stable ICU 4.2 3385 */ forLanguageTag(String languageTag)3386 public static ULocale forLanguageTag(String languageTag) { 3387 LanguageTag tag = LanguageTag.parse(languageTag, null); 3388 InternalLocaleBuilder bldr = new InternalLocaleBuilder(); 3389 bldr.setLanguageTag(tag); 3390 return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions()); 3391 } 3392 3393 /** 3394 * {@icu} Converts the specified keyword (legacy key, or BCP 47 Unicode locale 3395 * extension key) to the equivalent BCP 47 Unicode locale extension key. 3396 * For example, BCP 47 Unicode locale extension key "co" is returned for 3397 * the input keyword "collation". 3398 * <p> 3399 * When the specified keyword is unknown, but satisfies the BCP syntax, 3400 * then the lower-case version of the input keyword will be returned. 3401 * For example, 3402 * <code>toUnicodeLocaleKey("ZZ")</code> returns "zz". 3403 * 3404 * @param keyword the input locale keyword (either legacy key 3405 * such as "collation" or BCP 47 Unicode locale extension 3406 * key such as "co"). 3407 * @return the well-formed BCP 47 Unicode locale extension key, 3408 * or null if the specified locale keyword cannot be mapped 3409 * to a well-formed BCP 47 Unicode locale extension key. 3410 * @see #toLegacyKey(String) 3411 * @draft ICU 54 3412 * @provisional This API might change or be removed in a future release. 3413 */ toUnicodeLocaleKey(String keyword)3414 public static String toUnicodeLocaleKey(String keyword) { 3415 String bcpKey = KeyTypeData.toBcpKey(keyword); 3416 if (bcpKey == null && UnicodeLocaleExtension.isKey(keyword)) { 3417 // unknown keyword, but syntax is fine.. 3418 bcpKey = AsciiUtil.toLowerString(keyword); 3419 } 3420 return bcpKey; 3421 } 3422 3423 /** 3424 * {@icu} Converts the specified keyword value (legacy type, or BCP 47 3425 * Unicode locale extension type) to the well-formed BCP 47 Unicode locale 3426 * extension type for the specified keyword (category). For example, BCP 47 3427 * Unicode locale extension type "phonebk" is returned for the input 3428 * keyword value "phonebook", with the keyword "collation" (or "co"). 3429 * <p> 3430 * When the specified keyword is not recognized, but the specified value 3431 * satisfies the syntax of the BCP 47 Unicode locale extension type, 3432 * or when the specified keyword allows 'variable' type and the specified 3433 * value satisfies the syntax, the lower-case version of the input value 3434 * will be returned. For example, 3435 * <code>toUnicodeLocaleType("Foo", "Bar")</code> returns "bar", 3436 * <code>toUnicodeLocaleType("variableTop", "00A4")</code> returns "00a4". 3437 * 3438 * @param keyword the locale keyword (either legacy key such as 3439 * "collation" or BCP 47 Unicode locale extension 3440 * key such as "co"). 3441 * @param value the locale keyword value (either legacy type 3442 * such as "phonebook" or BCP 47 Unicode locale extension 3443 * type such as "phonebk"). 3444 * @return the well-formed BCP47 Unicode locale extension type, 3445 * or null if the locale keyword value cannot be mapped to 3446 * a well-formed BCP 47 Unicode locale extension type. 3447 * @see #toLegacyType(String, String) 3448 * @draft ICU 54 3449 * @provisional This API might change or be removed in a future release. 3450 */ toUnicodeLocaleType(String keyword, String value)3451 public static String toUnicodeLocaleType(String keyword, String value) { 3452 String bcpType = KeyTypeData.toBcpType(keyword, value, null, null); 3453 if (bcpType == null && UnicodeLocaleExtension.isType(value)) { 3454 // unknown keyword, but syntax is fine.. 3455 bcpType = AsciiUtil.toLowerString(value); 3456 } 3457 return bcpType; 3458 } 3459 3460 /** 3461 * {@icu} Converts the specified keyword (BCP 47 Unicode locale extension key, or 3462 * legacy key) to the legacy key. For example, legacy key "collation" is 3463 * returned for the input BCP 47 Unicode locale extension key "co". 3464 * 3465 * @param keyword the input locale keyword (either BCP 47 Unicode locale 3466 * extension key or legacy key). 3467 * @return the well-formed legacy key, or null if the specified 3468 * keyword cannot be mapped to a well-formed legacy key. 3469 * @see #toUnicodeLocaleKey(String) 3470 * @draft ICU 54 3471 * @provisional This API might change or be removed in a future release. 3472 */ toLegacyKey(String keyword)3473 public static String toLegacyKey(String keyword) { 3474 String legacyKey = KeyTypeData.toLegacyKey(keyword); 3475 if (legacyKey == null) { 3476 // Checks if the specified locale key is well-formed with the legacy locale syntax. 3477 // 3478 // Note: 3479 // Neither ICU nor LDML/CLDR provides the definition of keyword syntax. 3480 // However, a key should not contain '=' obviously. For now, all existing 3481 // keys are using ASCII alphabetic letters only. We won't add any new key 3482 // that is not compatible with the BCP 47 syntax. Therefore, we assume 3483 // a valid key consist from [0-9a-zA-Z], no symbols. 3484 if (keyword.matches("[0-9a-zA-Z]+")) { 3485 legacyKey = AsciiUtil.toLowerString(keyword); 3486 } 3487 } 3488 return legacyKey; 3489 } 3490 3491 /** 3492 * {@icu} Converts the specified keyword value (BCP 47 Unicode locale extension type, 3493 * or legacy type or type alias) to the canonical legacy type. For example, 3494 * the legacy type "phonebook" is returned for the input BCP 47 Unicode 3495 * locale extension type "phonebk" with the keyword "collation" (or "co"). 3496 * <p> 3497 * When the specified keyword is not recognized, but the specified value 3498 * satisfies the syntax of legacy key, or when the specified keyword 3499 * allows 'variable' type and the specified value satisfies the syntax, 3500 * the lower-case version of the input value will be returned. 3501 * For example, 3502 * <code>toLegacyType("Foo", "Bar")</code> returns "bar", 3503 * <code>toLegacyType("vt", "00A4")</code> returns "00a4". 3504 * 3505 * @param keyword the locale keyword (either legacy keyword such as 3506 * "collation" or BCP 47 Unicode locale extension 3507 * key such as "co"). 3508 * @param value the locale keyword value (either BCP 47 Unicode locale 3509 * extension type such as "phonebk" or legacy keyword value 3510 * such as "phonebook"). 3511 * @return the well-formed legacy type, or null if the specified 3512 * keyword value cannot be mapped to a well-formed legacy 3513 * type. 3514 * @see #toUnicodeLocaleType(String, String) 3515 * @draft ICU 54 3516 * @provisional This API might change or be removed in a future release. 3517 */ toLegacyType(String keyword, String value)3518 public static String toLegacyType(String keyword, String value) { 3519 String legacyType = KeyTypeData.toLegacyType(keyword, value, null, null); 3520 if (legacyType == null) { 3521 // Checks if the specified locale type is well-formed with the legacy locale syntax. 3522 // 3523 // Note: 3524 // Neither ICU nor LDML/CLDR provides the definition of keyword syntax. 3525 // However, a type should not contain '=' obviously. For now, all existing 3526 // types are using ASCII alphabetic letters with a few symbol letters. We won't 3527 // add any new type that is not compatible with the BCP 47 syntax except timezone 3528 // IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain 3529 // '-' '_' '/' in the middle. 3530 if (value.matches("[0-9a-zA-Z]+([_/\\-][0-9a-zA-Z]+)*")) { 3531 legacyType = AsciiUtil.toLowerString(value); 3532 } 3533 } 3534 return legacyType; 3535 } 3536 3537 /** 3538 * <code>Builder</code> is used to build instances of <code>ULocale</code> 3539 * from values configured by the setters. Unlike the <code>ULocale</code> 3540 * constructors, the <code>Builder</code> checks if a value configured by a 3541 * setter satisfies the syntax requirements defined by the <code>ULocale</code> 3542 * class. A <code>ULocale</code> object created by a <code>Builder</code> is 3543 * well-formed and can be transformed to a well-formed IETF BCP 47 language tag 3544 * without losing information. 3545 * 3546 * <p><b>Note:</b> The <code>ULocale</code> class does not provide any 3547 * syntactic restrictions on variant, while BCP 47 requires each variant 3548 * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3 3549 * alphanumerics. The method <code>setVariant</code> throws 3550 * <code>IllformedLocaleException</code> for a variant that does not satisfy 3551 * this restriction. If it is necessary to support such a variant, use a 3552 * ULocale constructor. However, keep in mind that a <code>ULocale</code> 3553 * object created this way might lose the variant information when 3554 * transformed to a BCP 47 language tag. 3555 * 3556 * <p>The following example shows how to create a <code>Locale</code> object 3557 * with the <code>Builder</code>. 3558 * <blockquote> 3559 * <pre> 3560 * ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build(); 3561 * </pre> 3562 * </blockquote> 3563 * 3564 * <p>Builders can be reused; <code>clear()</code> resets all 3565 * fields to their default values. 3566 * 3567 * @see ULocale#toLanguageTag() 3568 * 3569 * @stable ICU 4.2 3570 */ 3571 public static final class Builder { 3572 3573 private final InternalLocaleBuilder _locbld; 3574 3575 /** 3576 * Constructs an empty Builder. The default value of all 3577 * fields, extensions, and private use information is the 3578 * empty string. 3579 * 3580 * @stable ICU 4.2 3581 */ Builder()3582 public Builder() { 3583 _locbld = new InternalLocaleBuilder(); 3584 } 3585 3586 /** 3587 * Resets the <code>Builder</code> to match the provided 3588 * <code>locale</code>. Existing state is discarded. 3589 * 3590 * <p>All fields of the locale must be well-formed, see {@link Locale}. 3591 * 3592 * <p>Locales with any ill-formed fields cause 3593 * <code>IllformedLocaleException</code> to be thrown. 3594 * 3595 * @param locale the locale 3596 * @return This builder. 3597 * @throws IllformedLocaleException if <code>locale</code> has 3598 * any ill-formed fields. 3599 * @throws NullPointerException if <code>locale</code> is null. 3600 * 3601 * @stable ICU 4.2 3602 */ setLocale(ULocale locale)3603 public Builder setLocale(ULocale locale) { 3604 try { 3605 _locbld.setLocale(locale.base(), locale.extensions()); 3606 } catch (LocaleSyntaxException e) { 3607 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3608 } 3609 return this; 3610 } 3611 3612 /** 3613 * Resets the Builder to match the provided IETF BCP 47 3614 * language tag. Discards the existing state. Null and the 3615 * empty string cause the builder to be reset, like {@link 3616 * #clear}. Grandfathered tags (see {@link 3617 * ULocale#forLanguageTag}) are converted to their canonical 3618 * form before being processed. Otherwise, the language tag 3619 * must be well-formed (see {@link ULocale}) or an exception is 3620 * thrown (unlike <code>ULocale.forLanguageTag</code>, which 3621 * just discards ill-formed and following portions of the 3622 * tag). 3623 * 3624 * @param languageTag the language tag 3625 * @return This builder. 3626 * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed 3627 * @see ULocale#forLanguageTag(String) 3628 * 3629 * @stable ICU 4.2 3630 */ setLanguageTag(String languageTag)3631 public Builder setLanguageTag(String languageTag) { 3632 ParseStatus sts = new ParseStatus(); 3633 LanguageTag tag = LanguageTag.parse(languageTag, sts); 3634 if (sts.isError()) { 3635 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex()); 3636 } 3637 _locbld.setLanguageTag(tag); 3638 3639 return this; 3640 } 3641 3642 /** 3643 * Sets the language. If <code>language</code> is the empty string or 3644 * null, the language in this <code>Builder</code> is removed. Otherwise, 3645 * the language must be <a href="./Locale.html#def_language">well-formed</a> 3646 * or an exception is thrown. 3647 * 3648 * <p>The typical language value is a two or three-letter language 3649 * code as defined in ISO639. 3650 * 3651 * @param language the language 3652 * @return This builder. 3653 * @throws IllformedLocaleException if <code>language</code> is ill-formed 3654 * 3655 * @stable ICU 4.2 3656 */ setLanguage(String language)3657 public Builder setLanguage(String language) { 3658 try { 3659 _locbld.setLanguage(language); 3660 } catch (LocaleSyntaxException e) { 3661 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3662 } 3663 return this; 3664 } 3665 3666 /** 3667 * Sets the script. If <code>script</code> is null or the empty string, 3668 * the script in this <code>Builder</code> is removed. 3669 * Otherwise, the script must be well-formed or an exception is thrown. 3670 * 3671 * <p>The typical script value is a four-letter script code as defined by ISO 15924. 3672 * 3673 * @param script the script 3674 * @return This builder. 3675 * @throws IllformedLocaleException if <code>script</code> is ill-formed 3676 * 3677 * @stable ICU 4.2 3678 */ setScript(String script)3679 public Builder setScript(String script) { 3680 try { 3681 _locbld.setScript(script); 3682 } catch (LocaleSyntaxException e) { 3683 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3684 } 3685 return this; 3686 } 3687 3688 /** 3689 * Sets the region. If region is null or the empty string, the region 3690 * in this <code>Builder</code> is removed. Otherwise, 3691 * the region must be well-formed or an exception is thrown. 3692 * 3693 * <p>The typical region value is a two-letter ISO 3166 code or a 3694 * three-digit UN M.49 area code. 3695 * 3696 * <p>The country value in the <code>Locale</code> created by the 3697 * <code>Builder</code> is always normalized to upper case. 3698 * 3699 * @param region the region 3700 * @return This builder. 3701 * @throws IllformedLocaleException if <code>region</code> is ill-formed 3702 * 3703 * @stable ICU 4.2 3704 */ setRegion(String region)3705 public Builder setRegion(String region) { 3706 try { 3707 _locbld.setRegion(region); 3708 } catch (LocaleSyntaxException e) { 3709 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3710 } 3711 return this; 3712 } 3713 3714 /** 3715 * Sets the variant. If variant is null or the empty string, the 3716 * variant in this <code>Builder</code> is removed. Otherwise, it 3717 * must consist of one or more well-formed subtags, or an exception is thrown. 3718 * 3719 * <p><b>Note:</b> This method checks if <code>variant</code> 3720 * satisfies the IETF BCP 47 variant subtag's syntax requirements, 3721 * and normalizes the value to lowercase letters. However, 3722 * the <code>ULocale</code> class does not impose any syntactic 3723 * restriction on variant. To set such a variant, 3724 * use a ULocale constructor. 3725 * 3726 * @param variant the variant 3727 * @return This builder. 3728 * @throws IllformedLocaleException if <code>variant</code> is ill-formed 3729 * 3730 * @stable ICU 4.2 3731 */ setVariant(String variant)3732 public Builder setVariant(String variant) { 3733 try { 3734 _locbld.setVariant(variant); 3735 } catch (LocaleSyntaxException e) { 3736 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3737 } 3738 return this; 3739 } 3740 3741 /** 3742 * Sets the extension for the given key. If the value is null or the 3743 * empty string, the extension is removed. Otherwise, the extension 3744 * must be well-formed or an exception is thrown. 3745 * 3746 * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION 3747 * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension. 3748 * Setting a value for this key replaces any existing Unicode locale key/type 3749 * pairs with those defined in the extension. 3750 * 3751 * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION 3752 * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be 3753 * well-formed, the value for this key needs only to have subtags of one to 3754 * eight alphanumeric characters, not two to eight as in the general case. 3755 * 3756 * @param key the extension key 3757 * @param value the extension value 3758 * @return This builder. 3759 * @throws IllformedLocaleException if <code>key</code> is illegal 3760 * or <code>value</code> is ill-formed 3761 * @see #setUnicodeLocaleKeyword(String, String) 3762 * 3763 * @stable ICU 4.2 3764 */ setExtension(char key, String value)3765 public Builder setExtension(char key, String value) { 3766 try { 3767 _locbld.setExtension(key, value); 3768 } catch (LocaleSyntaxException e) { 3769 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3770 } 3771 return this; 3772 } 3773 3774 /** 3775 * Sets the Unicode locale keyword type for the given key. If the type 3776 * is null, the Unicode keyword is removed. Otherwise, the key must be 3777 * non-null and both key and type must be well-formed or an exception 3778 * is thrown. 3779 * 3780 * <p>Keys and types are converted to lower case. 3781 * 3782 * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension} 3783 * replaces all Unicode locale keywords with those defined in the 3784 * extension. 3785 * 3786 * @param key the Unicode locale key 3787 * @param type the Unicode locale type 3788 * @return This builder. 3789 * @throws IllformedLocaleException if <code>key</code> or <code>type</code> 3790 * is ill-formed 3791 * @throws NullPointerException if <code>key</code> is null 3792 * @see #setExtension(char, String) 3793 * 3794 * @stable ICU 4.4 3795 */ setUnicodeLocaleKeyword(String key, String type)3796 public Builder setUnicodeLocaleKeyword(String key, String type) { 3797 try { 3798 _locbld.setUnicodeLocaleKeyword(key, type); 3799 } catch (LocaleSyntaxException e) { 3800 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3801 } 3802 return this; 3803 } 3804 3805 /** 3806 * Adds a unicode locale attribute, if not already present, otherwise 3807 * has no effect. The attribute must not be null and must be well-formed 3808 * or an exception is thrown. 3809 * 3810 * @param attribute the attribute 3811 * @return This builder. 3812 * @throws NullPointerException if <code>attribute</code> is null 3813 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 3814 * @see #setExtension(char, String) 3815 * 3816 * @stable ICU 4.6 3817 */ addUnicodeLocaleAttribute(String attribute)3818 public Builder addUnicodeLocaleAttribute(String attribute) { 3819 try { 3820 _locbld.addUnicodeLocaleAttribute(attribute); 3821 } catch (LocaleSyntaxException e) { 3822 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3823 } 3824 return this; 3825 } 3826 3827 /** 3828 * Removes a unicode locale attribute, if present, otherwise has no 3829 * effect. The attribute must not be null and must be well-formed 3830 * or an exception is thrown. 3831 * 3832 * <p>Attribute comparision for removal is case-insensitive. 3833 * 3834 * @param attribute the attribute 3835 * @return This builder. 3836 * @throws NullPointerException if <code>attribute</code> is null 3837 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 3838 * @see #setExtension(char, String) 3839 * 3840 * @stable ICU 4.6 3841 */ removeUnicodeLocaleAttribute(String attribute)3842 public Builder removeUnicodeLocaleAttribute(String attribute) { 3843 try { 3844 _locbld.removeUnicodeLocaleAttribute(attribute); 3845 } catch (LocaleSyntaxException e) { 3846 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3847 } 3848 return this; 3849 } 3850 3851 /** 3852 * Resets the builder to its initial, empty state. 3853 * 3854 * @return this builder 3855 * 3856 * @stable ICU 4.2 3857 */ clear()3858 public Builder clear() { 3859 _locbld.clear(); 3860 return this; 3861 } 3862 3863 /** 3864 * Resets the extensions to their initial, empty state. 3865 * Language, script, region and variant are unchanged. 3866 * 3867 * @return this builder 3868 * @see #setExtension(char, String) 3869 * 3870 * @stable ICU 4.2 3871 */ clearExtensions()3872 public Builder clearExtensions() { 3873 _locbld.clearExtensions(); 3874 return this; 3875 } 3876 3877 /** 3878 * Returns an instance of <code>ULocale</code> created from the fields set 3879 * on this builder. 3880 * 3881 * @return a new Locale 3882 * 3883 * @stable ICU 4.4 3884 */ build()3885 public ULocale build() { 3886 return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions()); 3887 } 3888 } 3889 getInstance(BaseLocale base, LocaleExtensions exts)3890 private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) { 3891 String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(), 3892 base.getVariant()); 3893 3894 Set<Character> extKeys = exts.getKeys(); 3895 if (!extKeys.isEmpty()) { 3896 // legacy locale ID assume Unicode locale keywords and 3897 // other extensions are at the same level. 3898 // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use 3899 3900 TreeMap<String, String> kwds = new TreeMap<String, String>(); 3901 for (Character key : extKeys) { 3902 Extension ext = exts.getExtension(key); 3903 if (ext instanceof UnicodeLocaleExtension) { 3904 UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext; 3905 Set<String> ukeys = uext.getUnicodeLocaleKeys(); 3906 for (String bcpKey : ukeys) { 3907 String bcpType = uext.getUnicodeLocaleType(bcpKey); 3908 // convert to legacy key/type 3909 String lkey = toLegacyKey(bcpKey); 3910 String ltype = toLegacyType(bcpKey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords 3911 // special handling for u-va-posix, since this is a variant, not a keyword 3912 if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) { 3913 id = id + "_POSIX"; 3914 } else { 3915 kwds.put(lkey, ltype); 3916 } 3917 } 3918 // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy 3919 Set<String> uattributes = uext.getUnicodeLocaleAttributes(); 3920 if (uattributes.size() > 0) { 3921 StringBuilder attrbuf = new StringBuilder(); 3922 for (String attr : uattributes) { 3923 if (attrbuf.length() > 0) { 3924 attrbuf.append('-'); 3925 } 3926 attrbuf.append(attr); 3927 } 3928 kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString()); 3929 } 3930 } else { 3931 kwds.put(String.valueOf(key), ext.getValue()); 3932 } 3933 } 3934 3935 if (!kwds.isEmpty()) { 3936 StringBuilder buf = new StringBuilder(id); 3937 buf.append("@"); 3938 Set<Map.Entry<String, String>> kset = kwds.entrySet(); 3939 boolean insertSep = false; 3940 for (Map.Entry<String, String> kwd : kset) { 3941 if (insertSep) { 3942 buf.append(";"); 3943 } else { 3944 insertSep = true; 3945 } 3946 buf.append(kwd.getKey()); 3947 buf.append("="); 3948 buf.append(kwd.getValue()); 3949 } 3950 3951 id = buf.toString(); 3952 } 3953 } 3954 return new ULocale(id); 3955 } 3956 base()3957 private BaseLocale base() { 3958 if (baseLocale == null) { 3959 String language, script, region, variant; 3960 language = script = region = variant = ""; 3961 if (!equals(ULocale.ROOT)) { 3962 LocaleIDParser lp = new LocaleIDParser(localeID); 3963 language = lp.getLanguage(); 3964 script = lp.getScript(); 3965 region = lp.getCountry(); 3966 variant = lp.getVariant(); 3967 } 3968 baseLocale = BaseLocale.getInstance(language, script, region, variant); 3969 } 3970 return baseLocale; 3971 } 3972 extensions()3973 private LocaleExtensions extensions() { 3974 if (extensions == null) { 3975 Iterator<String> kwitr = getKeywords(); 3976 if (kwitr == null) { 3977 extensions = LocaleExtensions.EMPTY_EXTENSIONS; 3978 } else { 3979 InternalLocaleBuilder intbld = new InternalLocaleBuilder(); 3980 while (kwitr.hasNext()) { 3981 String key = kwitr.next(); 3982 if (key.equals(LOCALE_ATTRIBUTE_KEY)) { 3983 // special keyword used for representing Unicode locale attributes 3984 String[] uattributes = getKeywordValue(key).split("[-_]"); 3985 for (String uattr : uattributes) { 3986 try { 3987 intbld.addUnicodeLocaleAttribute(uattr); 3988 } catch (LocaleSyntaxException e) { 3989 // ignore and fall through 3990 } 3991 } 3992 } else if (key.length() >= 2) { 3993 String bcpKey = toUnicodeLocaleKey(key); 3994 String bcpType = toUnicodeLocaleType(key, getKeywordValue(key)); 3995 if (bcpKey != null && bcpType != null) { 3996 try { 3997 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType); 3998 } catch (LocaleSyntaxException e) { 3999 // ignore and fall through 4000 } 4001 } 4002 } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) { 4003 try { 4004 intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_", 4005 LanguageTag.SEP)); 4006 } catch (LocaleSyntaxException e) { 4007 // ignore and fall through 4008 } 4009 } 4010 } 4011 extensions = intbld.getLocaleExtensions(); 4012 } 4013 } 4014 return extensions; 4015 } 4016 4017 /* 4018 * JDK Locale Helper 4019 */ 4020 private static final class JDKLocaleHelper { 4021 private static boolean hasScriptsAndUnicodeExtensions = false; 4022 private static boolean hasLocaleCategories = false; 4023 4024 /* 4025 * New methods in Java 7 Locale class 4026 */ 4027 private static Method mGetScript; 4028 private static Method mGetExtensionKeys; 4029 private static Method mGetExtension; 4030 private static Method mGetUnicodeLocaleKeys; 4031 private static Method mGetUnicodeLocaleAttributes; 4032 private static Method mGetUnicodeLocaleType; 4033 private static Method mForLanguageTag; 4034 4035 private static Method mGetDefault; 4036 private static Method mSetDefault; 4037 private static Object eDISPLAY; 4038 private static Object eFORMAT; 4039 4040 /* 4041 * This table is used for mapping between ICU and special Java 4042 * 6 locales. When an ICU locale matches <minumum base> with 4043 * <keyword>/<value>, the ICU locale is mapped to <Java> locale. 4044 * For example, both ja_JP@calendar=japanese and ja@calendar=japanese 4045 * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped 4046 * to Java locale "no_NO_NY". 4047 */ 4048 private static final String[][] JAVA6_MAPDATA = { 4049 // { <Java>, <ICU base>, <keyword>, <value>, <minimum base> 4050 { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"}, 4051 { "no_NO_NY", "nn_NO", null, null, "nn"}, 4052 { "th_TH_TH", "th_TH", "numbers", "thai", "th"}, 4053 }; 4054 4055 static { 4056 do { 4057 try { 4058 mGetScript = Locale.class.getMethod("getScript", (Class[]) null); 4059 mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null); 4060 mGetExtension = Locale.class.getMethod("getExtension", char.class); 4061 mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null); 4062 mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null); 4063 mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class); 4064 mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class); 4065 4066 hasScriptsAndUnicodeExtensions = true; 4067 } catch (NoSuchMethodException e) { 4068 } catch (IllegalArgumentException e) { 4069 } catch (SecurityException e) { 4070 // TODO : report? 4071 } 4072 4073 try { 4074 Class<?> cCategory = null; 4075 Class<?>[] classes = Locale.class.getDeclaredClasses(); 4076 for (Class<?> c : classes) { 4077 if (c.getName().equals("java.util.Locale$Category")) { 4078 cCategory = c; 4079 break; 4080 } 4081 } 4082 if (cCategory == null) { 4083 break; 4084 } 4085 mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory); 4086 mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class); 4087 4088 Method mName = cCategory.getMethod("name", (Class[]) null); 4089 Object[] enumConstants = cCategory.getEnumConstants(); 4090 for (Object e : enumConstants) { 4091 String catVal = (String)mName.invoke(e, (Object[])null); 4092 if (catVal.equals("DISPLAY")) { 4093 eDISPLAY = e; 4094 } else if (catVal.equals("FORMAT")) { 4095 eFORMAT = e; 4096 } 4097 } 4098 if (eDISPLAY == null || eFORMAT == null) { 4099 break; 4100 } 4101 4102 hasLocaleCategories = true; 4103 } catch (NoSuchMethodException e) { 4104 } catch (IllegalArgumentException e) { 4105 } catch (IllegalAccessException e) { 4106 } catch (InvocationTargetException e) { 4107 } catch (SecurityException e) { 4108 // TODO : report? 4109 } 4110 } while (false); 4111 } 4112 JDKLocaleHelper()4113 private JDKLocaleHelper() { 4114 } 4115 hasLocaleCategories()4116 public static boolean hasLocaleCategories() { 4117 return hasLocaleCategories; 4118 } 4119 toULocale(Locale loc)4120 public static ULocale toULocale(Locale loc) { 4121 return hasScriptsAndUnicodeExtensions ? toULocale7(loc) : toULocale6(loc); 4122 } 4123 toLocale(ULocale uloc)4124 public static Locale toLocale(ULocale uloc) { 4125 return hasScriptsAndUnicodeExtensions ? toLocale7(uloc) : toLocale6(uloc); 4126 } 4127 toULocale7(Locale loc)4128 private static ULocale toULocale7(Locale loc) { 4129 String language = loc.getLanguage(); 4130 String script = ""; 4131 String country = loc.getCountry(); 4132 String variant = loc.getVariant(); 4133 4134 Set<String> attributes = null; 4135 Map<String, String> keywords = null; 4136 4137 try { 4138 script = (String) mGetScript.invoke(loc, (Object[]) null); 4139 @SuppressWarnings("unchecked") 4140 Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null); 4141 if (!extKeys.isEmpty()) { 4142 for (Character extKey : extKeys) { 4143 if (extKey.charValue() == 'u') { 4144 // Found Unicode locale extension 4145 4146 // attributes 4147 @SuppressWarnings("unchecked") 4148 Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null); 4149 if (!uAttributes.isEmpty()) { 4150 attributes = new TreeSet<String>(); 4151 for (String attr : uAttributes) { 4152 attributes.add(attr); 4153 } 4154 } 4155 4156 // keywords 4157 @SuppressWarnings("unchecked") 4158 Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null); 4159 for (String kwKey : uKeys) { 4160 String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey); 4161 if (kwVal != null) { 4162 if (kwKey.equals("va")) { 4163 // va-* is interpreted as a variant 4164 variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant; 4165 } else { 4166 if (keywords == null) { 4167 keywords = new TreeMap<String, String>(); 4168 } 4169 keywords.put(kwKey, kwVal); 4170 } 4171 } 4172 } 4173 } else { 4174 String extVal = (String) mGetExtension.invoke(loc, extKey); 4175 if (extVal != null) { 4176 if (keywords == null) { 4177 keywords = new TreeMap<String, String>(); 4178 } 4179 keywords.put(String.valueOf(extKey), extVal); 4180 } 4181 } 4182 } 4183 } 4184 } catch (IllegalAccessException e) { 4185 throw new RuntimeException(e); 4186 } catch (InvocationTargetException e) { 4187 throw new RuntimeException(e); 4188 } 4189 4190 // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU, 4191 // and it should be transformed to nn_NO. 4192 4193 // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert 4194 // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY. 4195 4196 if (language.equals("no") && country.equals("NO") && variant.equals("NY")) { 4197 language = "nn"; 4198 variant = ""; 4199 } 4200 4201 // Constructing ID 4202 StringBuilder buf = new StringBuilder(language); 4203 4204 if (script.length() > 0) { 4205 buf.append('_'); 4206 buf.append(script); 4207 } 4208 4209 if (country.length() > 0) { 4210 buf.append('_'); 4211 buf.append(country); 4212 } 4213 4214 if (variant.length() > 0) { 4215 if (country.length() == 0) { 4216 buf.append('_'); 4217 } 4218 buf.append('_'); 4219 buf.append(variant); 4220 } 4221 4222 if (attributes != null) { 4223 // transform Unicode attributes into a keyword 4224 StringBuilder attrBuf = new StringBuilder(); 4225 for (String attr : attributes) { 4226 if (attrBuf.length() != 0) { 4227 attrBuf.append('-'); 4228 } 4229 attrBuf.append(attr); 4230 } 4231 if (keywords == null) { 4232 keywords = new TreeMap<String, String>(); 4233 } 4234 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString()); 4235 } 4236 4237 if (keywords != null) { 4238 buf.append('@'); 4239 boolean addSep = false; 4240 for (Entry<String, String> kwEntry : keywords.entrySet()) { 4241 String kwKey = kwEntry.getKey(); 4242 String kwVal = kwEntry.getValue(); 4243 4244 if (kwKey.length() != 1) { 4245 // Unicode locale key 4246 kwKey = toLegacyKey(kwKey); 4247 // use "yes" as the value of typeless keywords 4248 kwVal = toLegacyType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal)); 4249 } 4250 4251 if (addSep) { 4252 buf.append(';'); 4253 } else { 4254 addSep = true; 4255 } 4256 buf.append(kwKey); 4257 buf.append('='); 4258 buf.append(kwVal); 4259 } 4260 } 4261 4262 return new ULocale(getName(buf.toString()), loc); 4263 } 4264 toULocale6(Locale loc)4265 private static ULocale toULocale6(Locale loc) { 4266 ULocale uloc = null; 4267 String locStr = loc.toString(); 4268 if (locStr.length() == 0) { 4269 uloc = ULocale.ROOT; 4270 } else { 4271 for (int i = 0; i < JAVA6_MAPDATA.length; i++) { 4272 if (JAVA6_MAPDATA[i][0].equals(locStr)) { 4273 LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]); 4274 p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]); 4275 locStr = p.getName(); 4276 break; 4277 } 4278 } 4279 uloc = new ULocale(getName(locStr), loc); 4280 } 4281 return uloc; 4282 } 4283 toLocale7(ULocale uloc)4284 private static Locale toLocale7(ULocale uloc) { 4285 Locale loc = null; 4286 String ulocStr = uloc.getName(); 4287 if (uloc.getScript().length() > 0 || ulocStr.contains("@")) { 4288 // With script or keywords available, the best way 4289 // to get a mapped Locale is to go through a language tag. 4290 // A Locale with script or keywords can only have variants 4291 // that is 1 to 8 alphanum. If this ULocale has a variant 4292 // subtag not satisfying the criteria, the variant subtag 4293 // will be lost. 4294 String tag = uloc.toLanguageTag(); 4295 4296 // Workaround for variant casing problem: 4297 // 4298 // The variant field in ICU is case insensitive and normalized 4299 // to upper case letters by getVariant(), while 4300 // the variant field in JDK Locale is case sensitive. 4301 // ULocale#toLanguageTag use lower case characters for 4302 // BCP 47 variant and private use x-lvariant. 4303 // 4304 // Locale#forLanguageTag in JDK preserves character casing 4305 // for variant. Because ICU always normalizes variant to 4306 // upper case, we convert language tag to upper case here. 4307 tag = AsciiUtil.toUpperString(tag); 4308 4309 try { 4310 loc = (Locale)mForLanguageTag.invoke(null, tag); 4311 } catch (IllegalAccessException e) { 4312 throw new RuntimeException(e); 4313 } catch (InvocationTargetException e) { 4314 throw new RuntimeException(e); 4315 } 4316 } 4317 if (loc == null) { 4318 // Without script or keywords, use a Locale constructor, 4319 // so we can preserve any ill-formed variants. 4320 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant()); 4321 } 4322 return loc; 4323 } 4324 toLocale6(ULocale uloc)4325 private static Locale toLocale6(ULocale uloc) { 4326 String locstr = uloc.getBaseName(); 4327 for (int i = 0; i < JAVA6_MAPDATA.length; i++) { 4328 if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) { 4329 if (JAVA6_MAPDATA[i][2] != null) { 4330 String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]); 4331 if (val != null && val.equals(JAVA6_MAPDATA[i][3])) { 4332 locstr = JAVA6_MAPDATA[i][0]; 4333 break; 4334 } 4335 } else { 4336 locstr = JAVA6_MAPDATA[i][0]; 4337 break; 4338 } 4339 } 4340 } 4341 LocaleIDParser p = new LocaleIDParser(locstr); 4342 String[] names = p.getLanguageScriptCountryVariant(); 4343 return new Locale(names[0], names[2], names[3]); 4344 } 4345 getDefault(Category category)4346 public static Locale getDefault(Category category) { 4347 Locale loc = Locale.getDefault(); 4348 if (hasLocaleCategories) { 4349 Object cat = null; 4350 switch (category) { 4351 case DISPLAY: 4352 cat = eDISPLAY; 4353 break; 4354 case FORMAT: 4355 cat = eFORMAT; 4356 break; 4357 } 4358 if (cat != null) { 4359 try { 4360 loc = (Locale)mGetDefault.invoke(null, cat); 4361 } catch (InvocationTargetException e) { 4362 // fall through - use the base default 4363 } catch (IllegalArgumentException e) { 4364 // fall through - use the base default 4365 } catch (IllegalAccessException e) { 4366 // fall through - use the base default 4367 } 4368 } 4369 } 4370 return loc; 4371 } 4372 setDefault(Category category, Locale newLocale)4373 public static void setDefault(Category category, Locale newLocale) { 4374 if (hasLocaleCategories) { 4375 Object cat = null; 4376 switch (category) { 4377 case DISPLAY: 4378 cat = eDISPLAY; 4379 break; 4380 case FORMAT: 4381 cat = eFORMAT; 4382 break; 4383 } 4384 if (cat != null) { 4385 try { 4386 mSetDefault.invoke(null, cat, newLocale); 4387 } catch (InvocationTargetException e) { 4388 // fall through - no effects 4389 } catch (IllegalArgumentException e) { 4390 // fall through - no effects 4391 } catch (IllegalAccessException e) { 4392 // fall through - no effects 4393 } 4394 } 4395 } 4396 } 4397 4398 // Returns true if the given Locale matches the original 4399 // default locale initialized by JVM by checking user.XXX 4400 // system properties. When the system properties are not accessible, 4401 // this method returns false. isOriginalDefaultLocale(Locale loc)4402 public static boolean isOriginalDefaultLocale(Locale loc) { 4403 if (hasScriptsAndUnicodeExtensions) { 4404 String script = ""; 4405 try { 4406 script = (String) mGetScript.invoke(loc, (Object[]) null); 4407 } catch (Exception e) { 4408 return false; 4409 } 4410 4411 return loc.getLanguage().equals(getSystemProperty("user.language")) 4412 && loc.getCountry().equals(getSystemProperty("user.country")) 4413 && loc.getVariant().equals(getSystemProperty("user.variant")) 4414 && script.equals(getSystemProperty("user.script")); 4415 } 4416 return loc.getLanguage().equals(getSystemProperty("user.language")) 4417 && loc.getCountry().equals(getSystemProperty("user.country")) 4418 && loc.getVariant().equals(getSystemProperty("user.variant")); 4419 } 4420 getSystemProperty(String key)4421 public static String getSystemProperty(String key) { 4422 String val = null; 4423 final String fkey = key; 4424 if (System.getSecurityManager() != null) { 4425 try { 4426 val = AccessController.doPrivileged(new PrivilegedAction<String>() { 4427 public String run() { 4428 return System.getProperty(fkey); 4429 } 4430 }); 4431 } catch (AccessControlException e) { 4432 // ignore 4433 } 4434 } else { 4435 val = System.getProperty(fkey); 4436 } 4437 return val; 4438 } 4439 } 4440 } 4441