ULocale.java revision 2ae130017183d2f66d55bf0ca51f8da3294644fd
1/* GENERATED SOURCE. DO NOT MODIFY. */ 2/* 3 ****************************************************************************** 4 * Copyright (C) 2003-2015, International Business Machines Corporation and 5 * others. All Rights Reserved. 6 ****************************************************************************** 7 */ 8 9package android.icu.util; 10 11import java.io.Serializable; 12import java.lang.reflect.InvocationTargetException; 13import java.lang.reflect.Method; 14import java.security.AccessControlException; 15import java.security.AccessController; 16import java.security.PrivilegedAction; 17import java.text.ParseException; 18import java.util.Iterator; 19import java.util.List; 20import java.util.Locale; 21import java.util.Map; 22import java.util.Map.Entry; 23import java.util.MissingResourceException; 24import java.util.Set; 25import java.util.TreeMap; 26import java.util.TreeSet; 27 28import android.icu.impl.ICUCache; 29import android.icu.impl.ICUResourceBundle; 30import android.icu.impl.ICUResourceTableAccess; 31import android.icu.impl.LocaleIDParser; 32import android.icu.impl.LocaleIDs; 33import android.icu.impl.LocaleUtility; 34import android.icu.impl.SimpleCache; 35import android.icu.impl.locale.AsciiUtil; 36import android.icu.impl.locale.BaseLocale; 37import android.icu.impl.locale.Extension; 38import android.icu.impl.locale.InternalLocaleBuilder; 39import android.icu.impl.locale.KeyTypeData; 40import android.icu.impl.locale.LanguageTag; 41import android.icu.impl.locale.LocaleExtensions; 42import android.icu.impl.locale.LocaleSyntaxException; 43import android.icu.impl.locale.ParseStatus; 44import android.icu.impl.locale.UnicodeLocaleExtension; 45import android.icu.lang.UScript; 46import android.icu.text.LocaleDisplayNames; 47import android.icu.text.LocaleDisplayNames.DialectHandling; 48 49/** 50 * {@icuenhanced java.util.Locale}.{@icu _usage_} 51 * 52 * A class analogous to {@link java.util.Locale} that provides additional 53 * support for ICU protocol. In ICU 3.0 this class is enhanced to support 54 * RFC 3066 language identifiers. 55 * 56 * <p>Many classes and services in ICU follow a factory idiom, in 57 * which a factory method or object responds to a client request with 58 * an object. The request includes a locale (the <i>requested</i> 59 * locale), and the returned object is constructed using data for that 60 * locale. The system may lack data for the requested locale, in 61 * which case the locale fallback mechanism will be invoked until a 62 * populated locale is found (the <i>valid</i> locale). Furthermore, 63 * even when a populated locale is found (the <i>valid</i> locale), 64 * further fallback may be required to reach a locale containing the 65 * specific data required by the service (the <i>actual</i> locale). 66 * 67 * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids. 68 * Normalization 'cleans up' ICU locale ids as follows: 69 * <ul> 70 * <li>language, script, country, variant, and keywords are properly cased<br> 71 * (lower, title, upper, upper, and lower case respectively)</li> 72 * <li>hyphens used as separators are converted to underscores</li> 73 * <li>three-letter language and country ids are converted to two-letter 74 * equivalents where available</li> 75 * <li>surrounding spaces are removed from keywords and values</li> 76 * <li>if there are multiple keywords, they are put in sorted order</li> 77 * </ul> 78 * Canonicalization additionally performs the following: 79 * <ul> 80 * <li>POSIX ids are converted to ICU format IDs</li> 81 * <li>'grandfathered' 3066 ids are converted to ICU standard form</li> 82 * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form, 83 * with the currency 84 * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO). 85 * </ul> 86 * All ULocale constructors automatically normalize the locale id. To handle 87 * POSIX ids, <code>canonicalize</code> can be called to convert the id 88 * to canonical form, or the <code>canonicalInstance</code> factory method 89 * can be called.</p> 90 * 91 * <p>This class provides selectors {@link #VALID_LOCALE} and {@link 92 * #ACTUAL_LOCALE} intended for use in methods named 93 * <tt>getLocale()</tt>. These methods exist in several ICU classes, 94 * including {@link android.icu.util.Calendar}, {@link 95 * android.icu.util.Currency}, {@link android.icu.text.UFormat}, 96 * {@link android.icu.text.BreakIterator}, 97 * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>, 98 * {@link android.icu.text.DateFormatSymbols}, and {@link 99 * android.icu.text.DecimalFormatSymbols} and their subclasses, if 100 * any. Once an object of one of these classes has been created, 101 * <tt>getLocale()</tt> may be called on it to determine the valid and 102 * actual locale arrived at during the object's construction. 103 * 104 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i> 105 * locale is not, in most cases. 106 * 107 * @see java.util.Locale 108 * @author weiv 109 * @author Alan Liu 110 * @author Ram Viswanadha 111 * @stable ICU 2.8 112 */ 113public final class ULocale implements Serializable, Comparable<ULocale> { 114 // using serialver from jdk1.4.2_05 115 private static final long serialVersionUID = 3715177670352309217L; 116 117 private static ICUCache<String, String> nameCache = new SimpleCache<String, String>(); 118 119 /** 120 * Useful constant for language. 121 * @stable ICU 3.0 122 */ 123 public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH); 124 125 /** 126 * Useful constant for language. 127 * @stable ICU 3.0 128 */ 129 public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH); 130 131 /** 132 * Useful constant for language. 133 * @stable ICU 3.0 134 */ 135 public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN); 136 137 /** 138 * Useful constant for language. 139 * @stable ICU 3.0 140 */ 141 public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN); 142 143 /** 144 * Useful constant for language. 145 * @stable ICU 3.0 146 */ 147 public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE); 148 149 /** 150 * Useful constant for language. 151 * @stable ICU 3.0 152 */ 153 public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN); 154 155 /** 156 * Useful constant for language. 157 * @stable ICU 3.0 158 */ 159 public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE); 160 161 162 // Special note about static initializer for 163 // - SIMPLIFIED_CHINESE 164 // - TRADTIONAL_CHINESE 165 // - CHINA 166 // - TAIWAN 167 // 168 // Equivalent JDK Locale for ULocale.SIMPLIFIED_CHINESE is different 169 // by JRE version. JRE 7 or later supports a script tag "Hans", while 170 // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually 171 // zh_CN, not zh_Hans. This is same in Java 7 or later versions. 172 // 173 // ULocale#toLocale() implementation uses Java reflection to create a Locale 174 // with a script tag. When a new ULocale is constructed with the single arg 175 // constructor, the volatile field 'Locale locale' is initialized by 176 // #toLocale() method. 177 // 178 // Because we cannot hardcode corresponding JDK Locale representation below, 179 // SIMPLIFIED_CHINESE is constructed without JDK Locale argument, and 180 // #toLocale() is used for resolving the best matching JDK Locale at runtime. 181 // 182 // The same thing applies to TRADITIONAL_CHINESE. 183 184 /** 185 * Useful constant for language. 186 * @stable ICU 3.0 187 */ 188 public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans"); 189 190 191 /** 192 * Useful constant for language. 193 * @stable ICU 3.0 194 */ 195 public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant"); 196 197 /** 198 * Useful constant for country/region. 199 * @stable ICU 3.0 200 */ 201 public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE); 202 203 /** 204 * Useful constant for country/region. 205 * @stable ICU 3.0 206 */ 207 public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY); 208 209 /** 210 * Useful constant for country/region. 211 * @stable ICU 3.0 212 */ 213 public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY); 214 215 /** 216 * Useful constant for country/region. 217 * @stable ICU 3.0 218 */ 219 public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN); 220 221 /** 222 * Useful constant for country/region. 223 * @stable ICU 3.0 224 */ 225 public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA); 226 227 /** 228 * Useful constant for country/region. 229 * @stable ICU 3.0 230 */ 231 public static final ULocale CHINA = new ULocale("zh_Hans_CN"); 232 233 /** 234 * Useful constant for country/region. 235 * @stable ICU 3.0 236 */ 237 public static final ULocale PRC = CHINA; 238 239 /** 240 * Useful constant for country/region. 241 * @stable ICU 3.0 242 */ 243 public static final ULocale TAIWAN = new ULocale("zh_Hant_TW"); 244 245 /** 246 * Useful constant for country/region. 247 * @stable ICU 3.0 248 */ 249 public static final ULocale UK = new ULocale("en_GB", Locale.UK); 250 251 /** 252 * Useful constant for country/region. 253 * @stable ICU 3.0 254 */ 255 public static final ULocale US = new ULocale("en_US", Locale.US); 256 257 /** 258 * Useful constant for country/region. 259 * @stable ICU 3.0 260 */ 261 public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA); 262 263 /** 264 * Useful constant for country/region. 265 * @stable ICU 3.0 266 */ 267 public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH); 268 269 /** 270 * Handy constant. 271 */ 272 private static final String EMPTY_STRING = ""; 273 274 // Used in both ULocale and LocaleIDParser, so moved up here. 275 private static final char UNDERSCORE = '_'; 276 277 // default empty locale 278 private static final Locale EMPTY_LOCALE = new Locale("", ""); 279 280 // special keyword key for Unicode locale attributes 281 private static final String LOCALE_ATTRIBUTE_KEY = "attribute"; 282 283 /** 284 * The root ULocale. 285 * @stable ICU 2.8 286 */ 287 public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE); 288 289 /** 290 * Enum for locale categories. These locale categories are used to get/set the default locale for 291 * the specific functionality represented by the category. 292 * @stable ICU 49 293 */ 294 public enum Category { 295 /** 296 * Category used to represent the default locale for displaying user interfaces. 297 * @stable ICU 49 298 */ 299 DISPLAY, 300 /** 301 * Category used to represent the default locale for formatting date, number and/or currency. 302 * @stable ICU 49 303 */ 304 FORMAT 305 } 306 307 private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>(); 308 309 /** 310 * Cache the locale. 311 */ 312 private transient volatile Locale locale; 313 314 /** 315 * The raw localeID that we were passed in. 316 */ 317 private String localeID; 318 319 /** 320 * Cache the locale data container fields. 321 * In future, we want to use them as the primary locale identifier storage. 322 */ 323 private transient volatile BaseLocale baseLocale; 324 private transient volatile LocaleExtensions extensions; 325 326 327 private static String[][] CANONICALIZE_MAP; 328 private static String[][] variantsToKeywords; 329 330 private static void initCANONICALIZE_MAP() { 331 if (CANONICALIZE_MAP == null) { 332 /** 333 * This table lists pairs of locale ids for canonicalization. The 334 * The 1st item is the normalized id. The 2nd item is the 335 * canonicalized id. The 3rd is the keyword. The 4th is the keyword value. 336 */ 337 String[][] tempCANONICALIZE_MAP = { 338 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */ 339 { "C", "en_US_POSIX", null, null }, /* POSIX name */ 340 { "art_LOJBAN", "jbo", null, null }, /* registered name */ 341 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */ 342 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */ 343 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" }, 344 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */ 345 { "de_1901", "de__1901", null, null }, /* registered name */ 346 { "de_1906", "de__1906", null, null }, /* registered name */ 347 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */ 348 { "de_AT_PREEURO", "de_AT", "currency", "ATS" }, 349 { "de_DE_PREEURO", "de_DE", "currency", "DEM" }, 350 { "de_LU_PREEURO", "de_LU", "currency", "EUR" }, 351 { "el_GR_PREEURO", "el_GR", "currency", "GRD" }, 352 { "en_BOONT", "en__BOONT", null, null }, /* registered name */ 353 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */ 354 { "en_BE_PREEURO", "en_BE", "currency", "BEF" }, 355 { "en_IE_PREEURO", "en_IE", "currency", "IEP" }, 356 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */ 357 { "es_ES_PREEURO", "es_ES", "currency", "ESP" }, 358 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" }, 359 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" }, 360 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" }, 361 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" }, 362 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" }, 363 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" }, 364 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" }, 365 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */ 366 { "it_IT_PREEURO", "it_IT", "currency", "ITL" }, 367 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, 368 // { "nb_NO_NY", "nn_NO", null, null }, 369 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" }, 370 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" }, 371 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" }, 372 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */ 373 { "sr_SP_CYRL", "sr_Cyrl_RS", null, null }, /* .NET name */ 374 { "sr_SP_LATN", "sr_Latn_RS", null, null }, /* .NET name */ 375 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */ 376 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */ 377 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */ 378 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */ 379 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */ 380 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */ 381 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */ 382 { "zh_GAN", "zh__GAN", null, null }, /* registered name */ 383 { "zh_GUOYU", "zh", null, null }, /* registered name */ 384 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */ 385 { "zh_MIN", "zh__MIN", null, null }, /* registered name */ 386 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */ 387 { "zh_WUU", "zh__WUU", null, null }, /* registered name */ 388 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */ 389 { "zh_YUE", "zh__YUE", null, null } /* registered name */ 390 }; 391 392 synchronized (ULocale.class) { 393 if (CANONICALIZE_MAP == null) { 394 CANONICALIZE_MAP = tempCANONICALIZE_MAP; 395 } 396 } 397 } 398 if (variantsToKeywords == null) { 399 /** 400 * This table lists pairs of locale ids for canonicalization. The 401 * The first item is the normalized variant id. 402 */ 403 String[][] tempVariantsToKeywords = { 404 { "EURO", "currency", "EUR" }, 405 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */ 406 { "STROKE", "collation", "stroke" } /* Solaris variant */ 407 }; 408 409 synchronized (ULocale.class) { 410 if (variantsToKeywords == null) { 411 variantsToKeywords = tempVariantsToKeywords; 412 } 413 } 414 } 415 } 416 417 /** 418 * Private constructor used by static initializers. 419 */ 420 private ULocale(String localeID, Locale locale) { 421 this.localeID = localeID; 422 this.locale = locale; 423 } 424 425 /** 426 * Construct a ULocale object from a {@link java.util.Locale}. 427 * @param loc a JDK locale 428 */ 429 private ULocale(Locale loc) { 430 this.localeID = getName(forLocale(loc).toString()); 431 this.locale = loc; 432 } 433 434 /** 435 * {@icu} Returns a ULocale object for a {@link java.util.Locale}. 436 * The ULocale is canonicalized. 437 * @param loc a JDK locale 438 * @stable ICU 3.2 439 */ 440 public static ULocale forLocale(Locale loc) { 441 if (loc == null) { 442 return null; 443 } 444 ULocale result = CACHE.get(loc); 445 if (result == null) { 446 result = JDKLocaleHelper.toULocale(loc); 447 CACHE.put(loc, result); 448 } 449 return result; 450 } 451 452 /** 453 * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists 454 * of optional language, script, country, and variant fields in that order, 455 * separated by underscores, followed by an optional keyword list. The 456 * script, if present, is four characters long-- this distinguishes it 457 * from a country code, which is two characters long. Other fields 458 * are distinguished by position as indicated by the underscores. The 459 * start of the keyword list is indicated by '@', and consists of two 460 * or more keyword/value pairs separated by semicolons(';'). 461 * 462 * <p>This constructor does not canonicalize the localeID. So, for 463 * example, "zh__pinyin" remains unchanged instead of converting 464 * to "zh@collation=pinyin". By default ICU only recognizes the 465 * latter as specifying pinyin collation. Use {@link #createCanonical} 466 * or {@link #canonicalize} if you need to canonicalize the localeID. 467 * 468 * @param localeID string representation of the locale, e.g: 469 * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional" 470 * @stable ICU 2.8 471 */ 472 public ULocale(String localeID) { 473 this.localeID = getName(localeID); 474 } 475 476 /** 477 * Convenience overload of ULocale(String, String, String) for 478 * compatibility with java.util.Locale. 479 * @see #ULocale(String, String, String) 480 * @stable ICU 3.4 481 */ 482 public ULocale(String a, String b) { 483 this(a, b, null); 484 } 485 486 /** 487 * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and 488 * c. These fields are concatenated using underscores to form a localeID of the form 489 * a_b_c, which is then handled like the localeID passed to <code>ULocale(String 490 * localeID)</code>. 491 * 492 * <p>Java locale strings consisting of language, country, and 493 * variant will be handled by this form, since the country code 494 * (being shorter than four letters long) will not be interpreted 495 * as a script code. If a script code is present, the final 496 * argument ('c') will be interpreted as the country code. It is 497 * recommended that this constructor only be used to ease porting, 498 * and that clients instead use the single-argument constructor 499 * when constructing a ULocale from a localeID. 500 * @param a first component of the locale id 501 * @param b second component of the locale id 502 * @param c third component of the locale id 503 * @see #ULocale(String) 504 * @stable ICU 3.0 505 */ 506 public ULocale(String a, String b, String c) { 507 localeID = getName(lscvToID(a, b, c, EMPTY_STRING)); 508 } 509 510 /** 511 * {@icu} Creates a ULocale from the id by first canonicalizing the id. 512 * @param nonCanonicalID the locale id to canonicalize 513 * @return the locale created from the canonical version of the ID. 514 * @stable ICU 3.0 515 */ 516 public static ULocale createCanonical(String nonCanonicalID) { 517 return new ULocale(canonicalize(nonCanonicalID), (Locale)null); 518 } 519 520 private static String lscvToID(String lang, String script, String country, String variant) { 521 StringBuilder buf = new StringBuilder(); 522 523 if (lang != null && lang.length() > 0) { 524 buf.append(lang); 525 } 526 if (script != null && script.length() > 0) { 527 buf.append(UNDERSCORE); 528 buf.append(script); 529 } 530 if (country != null && country.length() > 0) { 531 buf.append(UNDERSCORE); 532 buf.append(country); 533 } 534 if (variant != null && variant.length() > 0) { 535 if (country == null || country.length() == 0) { 536 buf.append(UNDERSCORE); 537 } 538 buf.append(UNDERSCORE); 539 buf.append(variant); 540 } 541 return buf.toString(); 542 } 543 544 /** 545 * {@icu} Converts this ULocale object to a {@link java.util.Locale}. 546 * @return a JDK locale that either exactly represents this object 547 * or is the closest approximation. 548 * @stable ICU 2.8 549 */ 550 public Locale toLocale() { 551 if (locale == null) { 552 locale = JDKLocaleHelper.toLocale(this); 553 } 554 return locale; 555 } 556 557 /** 558 * Keep our own default ULocale. 559 */ 560 private static Locale defaultLocale = Locale.getDefault(); 561 private static ULocale defaultULocale; 562 563 private static Locale[] defaultCategoryLocales = new Locale[Category.values().length]; 564 private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length]; 565 566 static { 567 defaultULocale = forLocale(defaultLocale); 568 569 // For Java 6 or older JRE, ICU initializes the default script from 570 // "user.script" system property. The system property was added 571 // in Java 7. On JRE 7, Locale.getDefault() should reflect the 572 // property value to the Locale's default. So ICU just relies on 573 // Locale.getDefault(). 574 575 // Note: The "user.script" property is only used by initialization. 576 // 577 if (JDKLocaleHelper.hasLocaleCategories()) { 578 for (Category cat: Category.values()) { 579 int idx = cat.ordinal(); 580 defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat); 581 defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]); 582 } 583 } else { 584 // Make sure the current default Locale is original. 585 // If not, it means that someone updated the default Locale. 586 // In this case, user.XXX properties are already out of date 587 // and we should not use user.script. 588 if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) { 589 // Use "user.script" if available 590 String userScript = JDKLocaleHelper.getSystemProperty("user.script"); 591 if (userScript != null && LanguageTag.isScript(userScript)) { 592 // Note: Builder or forLanguageTag cannot be used here 593 // when one of Locale fields is not well-formed. 594 BaseLocale base = defaultULocale.base(); 595 BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript, 596 base.getRegion(), base.getVariant()); 597 defaultULocale = getInstance(newBase, defaultULocale.extensions()); 598 } 599 } 600 601 // Java 6 or older does not have separated category locales, 602 // use the non-category default for all 603 for (Category cat: Category.values()) { 604 int idx = cat.ordinal(); 605 defaultCategoryLocales[idx] = defaultLocale; 606 defaultCategoryULocales[idx] = defaultULocale; 607 } 608 } 609 } 610 611 /** 612 * Returns the current default ULocale. 613 * <p> 614 * The default ULocale is synchronized to the default Java Locale. This method checks 615 * the current default Java Locale and returns an equivalent ULocale. 616 * <p> 617 * <b>Note:</b> Before Java 7, the JDK Locale was not able to represent a locale's script. 618 * Therefore, the script field in the default ULocale is always empty unless 619 * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)} 620 * on Java 6 or older systems. 621 * <p> 622 * <b>Note for ICU 49 or later:</b> Some JRE implementations allow users to override the default 623 * JDK Locale using system properties - <code>user.language</code>, <code>user.country</code> 624 * and <code>user.variant</code>. In addition to these system properties, some Java 7 625 * implementations support <code>user.script</code> for overriding the default Locale's script. 626 * ICU 49 and later versions use the <code>user.script</code> system property on Java 6 627 * or older systems supporting other <code>user.*</code> system properties to initialize 628 * the default ULocale. The <code>user.script</code> override for default ULocale is not 629 * used on Java 7, or if the current Java default Locale is changed after start up. 630 * 631 * @return the default ULocale. 632 * @stable ICU 2.8 633 */ 634 public static ULocale getDefault() { 635 synchronized (ULocale.class) { 636 if (defaultULocale == null) { 637 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese), 638 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently 639 // stored in a resource bundle. However, UResourceBundle currently requires 640 // non-null default ULocale. For now, this implementation returns ULocale.ROOT 641 // to avoid the problem. 642 643 // TODO: Consider moving BCP47 mapping data out of resource bundle later. 644 645 return ULocale.ROOT; 646 } 647 Locale currentDefault = Locale.getDefault(); 648 if (!defaultLocale.equals(currentDefault)) { 649 defaultLocale = currentDefault; 650 defaultULocale = forLocale(currentDefault); 651 652 if (!JDKLocaleHelper.hasLocaleCategories()) { 653 // Detected Java default Locale change. 654 // We need to update category defaults to match the 655 // Java 7's behavior on Java 6 or older environment. 656 for (Category cat : Category.values()) { 657 int idx = cat.ordinal(); 658 defaultCategoryLocales[idx] = currentDefault; 659 defaultCategoryULocales[idx] = forLocale(currentDefault); 660 } 661 } 662 } 663 return defaultULocale; 664 } 665 } 666 667 /** 668 * Sets the default ULocale. This also sets the default Locale. 669 * If the caller does not have write permission to the 670 * user.language property, a security exception will be thrown, 671 * and the default ULocale will remain unchanged. 672 * <p> 673 * By setting the default ULocale with this method, all of the default categoy locales 674 * are also set to the specified default ULocale. 675 * @param newLocale the new default locale 676 * @throws SecurityException if a security manager exists and its 677 * <code>checkPermission</code> method doesn't allow the operation. 678 * @throws NullPointerException if <code>newLocale</code> is null 679 * @see SecurityManager#checkPermission(java.security.Permission) 680 * @see java.util.PropertyPermission 681 * @see ULocale#setDefault(Category, ULocale) 682 * @stable ICU 3.0 683 */ 684 public static synchronized void setDefault(ULocale newLocale){ 685 defaultLocale = newLocale.toLocale(); 686 Locale.setDefault(defaultLocale); 687 defaultULocale = newLocale; 688 // This method also updates all category default locales 689 for (Category cat : Category.values()) { 690 setDefault(cat, newLocale); 691 } 692 } 693 694 /** 695 * Returns the current default ULocale for the specified category. 696 * 697 * @param category the category 698 * @return the default ULocale for the specified category. 699 * @stable ICU 49 700 */ 701 public static ULocale getDefault(Category category) { 702 synchronized (ULocale.class) { 703 int idx = category.ordinal(); 704 if (defaultCategoryULocales[idx] == null) { 705 // Just in case this method is called during ULocale class 706 // initialization. Unlike getDefault(), we do not have 707 // cyclic dependency for category default. 708 return ULocale.ROOT; 709 } 710 if (JDKLocaleHelper.hasLocaleCategories()) { 711 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category); 712 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) { 713 defaultCategoryLocales[idx] = currentCategoryDefault; 714 defaultCategoryULocales[idx] = forLocale(currentCategoryDefault); 715 } 716 } else { 717 // java.util.Locale.setDefault(Locale) in Java 7 updates 718 // category locale defaults. On Java 6 or older environment, 719 // ICU4J checks if the default locale has changed and update 720 // category ULocales here if necessary. 721 722 // Note: When java.util.Locale.setDefault(Locale) is called 723 // with a Locale same with the previous one, Java 7 still 724 // updates category locale defaults. On Java 6 or older env, 725 // there is no good way to detect the event, ICU4J simply 726 // check if the default Java Locale has changed since last 727 // time. 728 729 Locale currentDefault = Locale.getDefault(); 730 if (!defaultLocale.equals(currentDefault)) { 731 defaultLocale = currentDefault; 732 defaultULocale = forLocale(currentDefault); 733 734 for (Category cat : Category.values()) { 735 int tmpIdx = cat.ordinal(); 736 defaultCategoryLocales[tmpIdx] = currentDefault; 737 defaultCategoryULocales[tmpIdx] = forLocale(currentDefault); 738 } 739 } 740 741 // No synchronization with JDK Locale, because category default 742 // is not supported in Java 6 or older versions 743 } 744 return defaultCategoryULocales[idx]; 745 } 746 } 747 748 /** 749 * Sets the default <code>ULocale</code> for the specified <code>Category</code>. 750 * This also sets the default <code>Locale</code> for the specified <code>Category</code> 751 * of the JVM. If the caller does not have write permission to the 752 * user.language property, a security exception will be thrown, 753 * and the default ULocale for the specified Category will remain unchanged. 754 * 755 * @param category the specified category to set the default locale 756 * @param newLocale the new default locale 757 * @see SecurityManager#checkPermission(java.security.Permission) 758 * @see java.util.PropertyPermission 759 * @stable ICU 49 760 */ 761 public static synchronized void setDefault(Category category, ULocale newLocale) { 762 Locale newJavaDefault = newLocale.toLocale(); 763 int idx = category.ordinal(); 764 defaultCategoryULocales[idx] = newLocale; 765 defaultCategoryLocales[idx] = newJavaDefault; 766 JDKLocaleHelper.setDefault(category, newJavaDefault); 767 } 768 769 /** 770 * This is for compatibility with Locale-- in actuality, since ULocale is 771 * immutable, there is no reason to clone it, so this API returns 'this'. 772 * @stable ICU 3.0 773 */ 774 public Object clone() { 775 return this; 776 } 777 778 /** 779 * Returns the hashCode. 780 * @stable ICU 3.0 781 */ 782 public int hashCode() { 783 return localeID.hashCode(); 784 } 785 786 /** 787 * Returns true if the other object is another ULocale with the 788 * same full name. 789 * Note that since names are not canonicalized, two ULocales that 790 * function identically might not compare equal. 791 * 792 * @return true if this Locale is equal to the specified object. 793 * @stable ICU 3.0 794 */ 795 public boolean equals(Object obj) { 796 if (this == obj) { 797 return true; 798 } 799 if (obj instanceof ULocale) { 800 return localeID.equals(((ULocale)obj).localeID); 801 } 802 return false; 803 } 804 805 /** 806 * Compares two ULocale for ordering. 807 * <p><b>Note:</b> The order might change in future.</p> 808 * 809 * @param other the ULocale to be compared. 810 * @return a negative integer, zero, or a positive integer as this ULocale is less than, equal to, or greater 811 * than the specified ULocale. 812 * @throws NullPointerException if <code>other</code> is null. 813 * 814 * @stable ICU 53 815 */ 816 public int compareTo(ULocale other) { 817 if (this == other) { 818 return 0; 819 } 820 821 int cmp = 0; 822 823 // Language 824 cmp = getLanguage().compareTo(other.getLanguage()); 825 if (cmp == 0) { 826 // Script 827 cmp = getScript().compareTo(other.getScript()); 828 if (cmp == 0) { 829 // Region 830 cmp = getCountry().compareTo(other.getCountry()); 831 if (cmp == 0) { 832 // Variant 833 cmp = getVariant().compareTo(other.getVariant()); 834 if (cmp == 0) { 835 // Keywords 836 Iterator<String> thisKwdItr = getKeywords(); 837 Iterator<String> otherKwdItr = other.getKeywords(); 838 839 if (thisKwdItr == null) { 840 cmp = otherKwdItr == null ? 0 : -1; 841 } else if (otherKwdItr == null) { 842 cmp = 1; 843 } else { 844 // Both have keywords 845 while (cmp == 0 && thisKwdItr.hasNext()) { 846 if (!otherKwdItr.hasNext()) { 847 cmp = 1; 848 break; 849 } 850 // Compare keyword keys 851 String thisKey = thisKwdItr.next(); 852 String otherKey = otherKwdItr.next(); 853 cmp = thisKey.compareTo(otherKey); 854 if (cmp == 0) { 855 // Compare keyword values 856 String thisVal = getKeywordValue(thisKey); 857 String otherVal = other.getKeywordValue(otherKey); 858 if (thisVal == null) { 859 cmp = otherVal == null ? 0 : -1; 860 } else if (otherVal == null) { 861 cmp = 1; 862 } else { 863 cmp = thisVal.compareTo(otherVal); 864 } 865 } 866 } 867 if (cmp == 0 && otherKwdItr.hasNext()) { 868 cmp = -1; 869 } 870 } 871 } 872 } 873 } 874 } 875 876 // Normalize the result value: 877 // Note: String.compareTo() may return value other than -1, 0, 1. 878 // A value other than those are OK by the definition, but we don't want 879 // associate any semantics other than negative/zero/positive. 880 return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0); 881 } 882 883 /** 884 * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>, 885 * not <code>Locale</code>. Returns a list of all installed locales. 886 * @stable ICU 3.0 887 */ 888 public static ULocale[] getAvailableLocales() { 889 return ICUResourceBundle.getAvailableULocales(); 890 } 891 892 /** 893 * Returns a list of all 2-letter country codes defined in ISO 3166. 894 * Can be used to create Locales. 895 * @stable ICU 3.0 896 */ 897 public static String[] getISOCountries() { 898 return LocaleIDs.getISOCountries(); 899 } 900 901 /** 902 * Returns a list of all 2-letter language codes defined in ISO 639. 903 * Can be used to create Locales. 904 * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed. 905 * The list this function returns includes both the new and the old codes for the 906 * languages whose codes have changed.] 907 * @stable ICU 3.0 908 */ 909 public static String[] getISOLanguages() { 910 return LocaleIDs.getISOLanguages(); 911 } 912 913 /** 914 * Returns the language code for this locale, which will either be the empty string 915 * or a lowercase ISO 639 code. 916 * @see #getDisplayLanguage() 917 * @see #getDisplayLanguage(ULocale) 918 * @stable ICU 3.0 919 */ 920 public String getLanguage() { 921 return base().getLanguage(); 922 } 923 924 /** 925 * Returns the language code for the locale ID, 926 * which will either be the empty string 927 * or a lowercase ISO 639 code. 928 * @see #getDisplayLanguage() 929 * @see #getDisplayLanguage(ULocale) 930 * @stable ICU 3.0 931 */ 932 public static String getLanguage(String localeID) { 933 return new LocaleIDParser(localeID).getLanguage(); 934 } 935 936 /** 937 * Returns the script code for this locale, which might be the empty string. 938 * @see #getDisplayScript() 939 * @see #getDisplayScript(ULocale) 940 * @stable ICU 3.0 941 */ 942 public String getScript() { 943 return base().getScript(); 944 } 945 946 /** 947 * {@icu} Returns the script code for the specified locale, which might be the empty 948 * string. 949 * @see #getDisplayScript() 950 * @see #getDisplayScript(ULocale) 951 * @stable ICU 3.0 952 */ 953 public static String getScript(String localeID) { 954 return new LocaleIDParser(localeID).getScript(); 955 } 956 957 /** 958 * Returns the country/region code for this locale, which will either be the empty string 959 * or an uppercase ISO 3166 2-letter code. 960 * @see #getDisplayCountry() 961 * @see #getDisplayCountry(ULocale) 962 * @stable ICU 3.0 963 */ 964 public String getCountry() { 965 return base().getRegion(); 966 } 967 968 /** 969 * {@icu} Returns the country/region code for this locale, which will either be the empty string 970 * or an uppercase ISO 3166 2-letter code. 971 * @param localeID The locale identification string. 972 * @see #getDisplayCountry() 973 * @see #getDisplayCountry(ULocale) 974 * @stable ICU 3.0 975 */ 976 public static String getCountry(String localeID) { 977 return new LocaleIDParser(localeID).getCountry(); 978 } 979 980 /** 981 * Returns the variant code for this locale, which might be the empty string. 982 * @see #getDisplayVariant() 983 * @see #getDisplayVariant(ULocale) 984 * @stable ICU 3.0 985 */ 986 public String getVariant() { 987 return base().getVariant(); 988 } 989 990 /** 991 * {@icu} Returns the variant code for the specified locale, which might be the empty string. 992 * @see #getDisplayVariant() 993 * @see #getDisplayVariant(ULocale) 994 * @stable ICU 3.0 995 */ 996 public static String getVariant(String localeID) { 997 return new LocaleIDParser(localeID).getVariant(); 998 } 999 1000 /** 1001 * {@icu} Returns the fallback locale for the specified locale, which might be the 1002 * empty string. 1003 * @stable ICU 3.2 1004 */ 1005 public static String getFallback(String localeID) { 1006 return getFallbackString(getName(localeID)); 1007 } 1008 1009 /** 1010 * {@icu} Returns the fallback locale for this locale. If this locale is root, 1011 * returns null. 1012 * @stable ICU 3.2 1013 */ 1014 public ULocale getFallback() { 1015 if (localeID.length() == 0 || localeID.charAt(0) == '@') { 1016 return null; 1017 } 1018 return new ULocale(getFallbackString(localeID), (Locale)null); 1019 } 1020 1021 /** 1022 * Returns the given (canonical) locale id minus the last part before the tags. 1023 */ 1024 private static String getFallbackString(String fallback) { 1025 int extStart = fallback.indexOf('@'); 1026 if (extStart == -1) { 1027 extStart = fallback.length(); 1028 } 1029 int last = fallback.lastIndexOf('_', extStart); 1030 if (last == -1) { 1031 last = 0; 1032 } else { 1033 // truncate empty segment 1034 while (last > 0) { 1035 if (fallback.charAt(last - 1) != '_') { 1036 break; 1037 } 1038 last--; 1039 } 1040 } 1041 return fallback.substring(0, last) + fallback.substring(extStart); 1042 } 1043 1044 /** 1045 * {@icu} Returns the (normalized) base name for this locale, 1046 * like {@link #getName()}, but without keywords. 1047 * 1048 * @return the base name as a String. 1049 * @stable ICU 3.0 1050 */ 1051 public String getBaseName() { 1052 return getBaseName(localeID); 1053 } 1054 1055 /** 1056 * {@icu} Returns the (normalized) base name for the specified locale, 1057 * like {@link #getName(String)}, but without keywords. 1058 * 1059 * @param localeID the locale ID as a string 1060 * @return the base name as a String. 1061 * @stable ICU 3.0 1062 */ 1063 public static String getBaseName(String localeID){ 1064 if (localeID.indexOf('@') == -1) { 1065 return localeID; 1066 } 1067 return new LocaleIDParser(localeID).getBaseName(); 1068 } 1069 1070 /** 1071 * {@icu} Returns the (normalized) full name for this locale. 1072 * 1073 * @return String the full name of the localeID 1074 * @stable ICU 3.0 1075 */ 1076 public String getName() { 1077 return localeID; // always normalized 1078 } 1079 1080 /** 1081 * Gets the shortest length subtag's size. 1082 * 1083 * @param localeID 1084 * @return The size of the shortest length subtag 1085 **/ 1086 private static int getShortestSubtagLength(String localeID) { 1087 int localeIDLength = localeID.length(); 1088 int length = localeIDLength; 1089 boolean reset = true; 1090 int tmpLength = 0; 1091 1092 for (int i = 0; i < localeIDLength; i++) { 1093 if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') { 1094 if (reset) { 1095 reset = false; 1096 tmpLength = 0; 1097 } 1098 tmpLength++; 1099 } else { 1100 if (tmpLength != 0 && tmpLength < length) { 1101 length = tmpLength; 1102 } 1103 reset = true; 1104 } 1105 } 1106 1107 return length; 1108 } 1109 1110 /** 1111 * {@icu} Returns the (normalized) full name for the specified locale. 1112 * 1113 * @param localeID the localeID as a string 1114 * @return String the full name of the localeID 1115 * @stable ICU 3.0 1116 */ 1117 public static String getName(String localeID){ 1118 String tmpLocaleID; 1119 // Convert BCP47 id if necessary 1120 if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) { 1121 tmpLocaleID = forLanguageTag(localeID).getName(); 1122 if (tmpLocaleID.length() == 0) { 1123 tmpLocaleID = localeID; 1124 } 1125 } else { 1126 tmpLocaleID = localeID; 1127 } 1128 String name = nameCache.get(tmpLocaleID); 1129 if (name == null) { 1130 name = new LocaleIDParser(tmpLocaleID).getName(); 1131 nameCache.put(tmpLocaleID, name); 1132 } 1133 return name; 1134 } 1135 1136 /** 1137 * Returns a string representation of this object. 1138 * @stable ICU 3.0 1139 */ 1140 public String toString() { 1141 return localeID; 1142 } 1143 1144 /** 1145 * {@icu} Returns an iterator over keywords for this locale. If there 1146 * are no keywords, returns null. 1147 * @return iterator over keywords, or null if there are no keywords. 1148 * @stable ICU 3.0 1149 */ 1150 public Iterator<String> getKeywords() { 1151 return getKeywords(localeID); 1152 } 1153 1154 /** 1155 * {@icu} Returns an iterator over keywords for the specified locale. If there 1156 * are no keywords, returns null. 1157 * @return an iterator over the keywords in the specified locale, or null 1158 * if there are no keywords. 1159 * @stable ICU 3.0 1160 */ 1161 public static Iterator<String> getKeywords(String localeID){ 1162 return new LocaleIDParser(localeID).getKeywords(); 1163 } 1164 1165 /** 1166 * {@icu} Returns the value for a keyword in this locale. If the keyword is not 1167 * defined, returns null. 1168 * @param keywordName name of the keyword whose value is desired. Case insensitive. 1169 * @return the value of the keyword, or null. 1170 * @stable ICU 3.0 1171 */ 1172 public String getKeywordValue(String keywordName){ 1173 return getKeywordValue(localeID, keywordName); 1174 } 1175 1176 /** 1177 * {@icu} Returns the value for a keyword in the specified locale. If the keyword is 1178 * not defined, returns null. The locale name does not need to be normalized. 1179 * @param keywordName name of the keyword whose value is desired. Case insensitive. 1180 * @return String the value of the keyword as a string 1181 * @stable ICU 3.0 1182 */ 1183 public static String getKeywordValue(String localeID, String keywordName) { 1184 return new LocaleIDParser(localeID).getKeywordValue(keywordName); 1185 } 1186 1187 /** 1188 * {@icu} Returns the canonical name for the specified locale ID. This is used to 1189 * convert POSIX and other grandfathered IDs to standard ICU form. 1190 * @param localeID the locale id 1191 * @return the canonicalized id 1192 * @stable ICU 3.0 1193 */ 1194 public static String canonicalize(String localeID){ 1195 LocaleIDParser parser = new LocaleIDParser(localeID, true); 1196 String baseName = parser.getBaseName(); 1197 boolean foundVariant = false; 1198 1199 // formerly, we always set to en_US_POSIX if the basename was empty, but 1200 // now we require that the entire id be empty, so that "@foo=bar" 1201 // will pass through unchanged. 1202 // {dlf} I'd rather keep "" unchanged. 1203 if (localeID.equals("")) { 1204 return ""; 1205 // return "en_US_POSIX"; 1206 } 1207 1208 // we have an ID in the form xx_Yyyy_ZZ_KKKKK 1209 1210 initCANONICALIZE_MAP(); 1211 1212 /* convert the variants to appropriate ID */ 1213 for (int i = 0; i < variantsToKeywords.length; i++) { 1214 String[] vals = variantsToKeywords[i]; 1215 int idx = baseName.lastIndexOf("_" + vals[0]); 1216 if (idx > -1) { 1217 foundVariant = true; 1218 1219 baseName = baseName.substring(0, idx); 1220 if (baseName.endsWith("_")) { 1221 baseName = baseName.substring(0, --idx); 1222 } 1223 parser.setBaseName(baseName); 1224 parser.defaultKeywordValue(vals[1], vals[2]); 1225 break; 1226 } 1227 } 1228 1229 /* See if this is an already known locale */ 1230 for (int i = 0; i < CANONICALIZE_MAP.length; i++) { 1231 if (CANONICALIZE_MAP[i][0].equals(baseName)) { 1232 foundVariant = true; 1233 1234 String[] vals = CANONICALIZE_MAP[i]; 1235 parser.setBaseName(vals[1]); 1236 if (vals[2] != null) { 1237 parser.defaultKeywordValue(vals[2], vals[3]); 1238 } 1239 break; 1240 } 1241 } 1242 1243 /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */ 1244 if (!foundVariant) { 1245 if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) { 1246 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null)); 1247 } 1248 } 1249 1250 return parser.getName(); 1251 } 1252 1253 /** 1254 * {@icu} Given a keyword and a value, return a new locale with an updated 1255 * keyword and value. If the keyword is null, this removes all keywords from the locale id. 1256 * Otherwise, if the value is null, this removes the value for this keyword from the 1257 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id. 1258 * The keyword and value must not be empty. 1259 * 1260 * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed. 1261 * 1262 * @param keyword the keyword to add/remove, or null to remove all keywords. 1263 * @param value the value to add/set, or null to remove this particular keyword. 1264 * @return the updated locale 1265 * @stable ICU 3.2 1266 */ 1267 public ULocale setKeywordValue(String keyword, String value) { 1268 return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null); 1269 } 1270 1271 /** 1272 * Given a locale id, a keyword, and a value, return a new locale id with an updated 1273 * keyword and value. If the keyword is null, this removes all keywords from the locale id. 1274 * Otherwise, if the value is null, this removes the value for this keyword from the 1275 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id. 1276 * The keyword and value must not be empty. 1277 * 1278 * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed. 1279 * 1280 * @param localeID the locale id to modify 1281 * @param keyword the keyword to add/remove, or null to remove all keywords. 1282 * @param value the value to add/set, or null to remove this particular keyword. 1283 * @return the updated locale id 1284 * @stable ICU 3.2 1285 */ 1286 public static String setKeywordValue(String localeID, String keyword, String value) { 1287 LocaleIDParser parser = new LocaleIDParser(localeID); 1288 parser.setKeywordValue(keyword, value); 1289 return parser.getName(); 1290 } 1291 1292 /* 1293 * Given a locale id, a keyword, and a value, return a new locale id with an updated 1294 * keyword and value, if the keyword does not already have a value. The keyword and 1295 * value must not be null or empty. 1296 * @param localeID the locale id to modify 1297 * @param keyword the keyword to add, if not already present 1298 * @param value the value to add, if not already present 1299 * @return the updated locale id 1300 */ 1301 /* private static String defaultKeywordValue(String localeID, String keyword, String value) { 1302 LocaleIDParser parser = new LocaleIDParser(localeID); 1303 parser.defaultKeywordValue(keyword, value); 1304 return parser.getName(); 1305 }*/ 1306 1307 /** 1308 * Returns a three-letter abbreviation for this locale's language. If the locale 1309 * doesn't specify a language, returns the empty string. Otherwise, returns 1310 * a lowercase ISO 639-2/T language code. 1311 * The ISO 639-2 language codes can be found on-line at 1312 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a> 1313 * @exception MissingResourceException Throws MissingResourceException if the 1314 * three-letter language abbreviation is not available for this locale. 1315 * @stable ICU 3.0 1316 */ 1317 public String getISO3Language(){ 1318 return getISO3Language(localeID); 1319 } 1320 1321 /** 1322 * {@icu} Returns a three-letter abbreviation for this locale's language. If the locale 1323 * doesn't specify a language, returns the empty string. Otherwise, returns 1324 * a lowercase ISO 639-2/T language code. 1325 * The ISO 639-2 language codes can be found on-line at 1326 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a> 1327 * @exception MissingResourceException Throws MissingResourceException if the 1328 * three-letter language abbreviation is not available for this locale. 1329 * @stable ICU 3.0 1330 */ 1331 public static String getISO3Language(String localeID) { 1332 return LocaleIDs.getISO3Language(getLanguage(localeID)); 1333 } 1334 1335 /** 1336 * Returns a three-letter abbreviation for this locale's country/region. If the locale 1337 * doesn't specify a country, returns the empty string. Otherwise, returns 1338 * an uppercase ISO 3166 3-letter country code. 1339 * @exception MissingResourceException Throws MissingResourceException if the 1340 * three-letter country abbreviation is not available for this locale. 1341 * @stable ICU 3.0 1342 */ 1343 public String getISO3Country() { 1344 return getISO3Country(localeID); 1345 } 1346 1347 /** 1348 * {@icu} Returns a three-letter abbreviation for this locale's country/region. If the locale 1349 * doesn't specify a country, returns the empty string. Otherwise, returns 1350 * an uppercase ISO 3166 3-letter country code. 1351 * @exception MissingResourceException Throws MissingResourceException if the 1352 * three-letter country abbreviation is not available for this locale. 1353 * @stable ICU 3.0 1354 */ 1355 public static String getISO3Country(String localeID) { 1356 return LocaleIDs.getISO3Country(getCountry(localeID)); 1357 } 1358 1359 /** 1360 * Pairs of (language subtag, + or -) for finding out fast if common languages 1361 * are LTR (minus) or RTL (plus). 1362 */ 1363 private static final String LANG_DIR_STRING = 1364 "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-"; 1365 1366 /** 1367 * {@icu} Returns whether this locale's script is written right-to-left. 1368 * If there is no script subtag, then the likely script is used, 1369 * see {@link #addLikelySubtags(ULocale)}. 1370 * If no likely script is known, then false is returned. 1371 * 1372 * <p>A script is right-to-left according to the CLDR script metadata 1373 * which corresponds to whether the script's letters have Bidi_Class=R or AL. 1374 * 1375 * <p>Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". 1376 * 1377 * @return true if the locale's script is written right-to-left 1378 * @stable ICU 54 1379 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1499 public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) { 1500 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true); 1501 } 1502 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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. 1602 private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) { 1603 return LocaleDisplayNames.getInstance(displayLocale) 1604 .scriptDisplayName(locale.getScript()); 1605 } 1606 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 */ 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 */ 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 */ 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 */ 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. 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 */ 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 */ 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 */ 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 */ 1710 public static String getDisplayVariant(String localeID, ULocale displayLocale) { 1711 return getDisplayVariantInternal(new ULocale(localeID), displayLocale); 1712 } 1713 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 */ 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 */ 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 */ 1751 public static String getDisplayKeyword(String keyword, ULocale displayLocale) { 1752 return getDisplayKeywordInternal(keyword, displayLocale); 1753 } 1754 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 */ 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 */ 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 */ 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 */ 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. 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 */ 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 */ 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 */ 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 */ 1858 public static String getDisplayName(String localeID, ULocale displayLocale) { 1859 return getDisplayNameInternal(new ULocale(localeID), displayLocale); 1860 } 1861 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 */ 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 */ 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 */ 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 */ 1911 public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) { 1912 return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale); 1913 } 1914 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 */ 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 */ 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 android.icu.util.ULocale 1976 * @see android.icu.util.ULocale#ACTUAL_LOCALE 1977 * @see android.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 { 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 **/ 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 **/ 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 **/ 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 **/ 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 * @stable ICU 54 3412 */ 3413 public static String toUnicodeLocaleKey(String keyword) { 3414 String bcpKey = KeyTypeData.toBcpKey(keyword); 3415 if (bcpKey == null && UnicodeLocaleExtension.isKey(keyword)) { 3416 // unknown keyword, but syntax is fine.. 3417 bcpKey = AsciiUtil.toLowerString(keyword); 3418 } 3419 return bcpKey; 3420 } 3421 3422 /** 3423 * {@icu} Converts the specified keyword value (legacy type, or BCP 47 3424 * Unicode locale extension type) to the well-formed BCP 47 Unicode locale 3425 * extension type for the specified keyword (category). For example, BCP 47 3426 * Unicode locale extension type "phonebk" is returned for the input 3427 * keyword value "phonebook", with the keyword "collation" (or "co"). 3428 * <p> 3429 * When the specified keyword is not recognized, but the specified value 3430 * satisfies the syntax of the BCP 47 Unicode locale extension type, 3431 * or when the specified keyword allows 'variable' type and the specified 3432 * value satisfies the syntax, the lower-case version of the input value 3433 * will be returned. For example, 3434 * <code>toUnicodeLocaleType("Foo", "Bar")</code> returns "bar", 3435 * <code>toUnicodeLocaleType("variableTop", "00A4")</code> returns "00a4". 3436 * 3437 * @param keyword the locale keyword (either legacy key such as 3438 * "collation" or BCP 47 Unicode locale extension 3439 * key such as "co"). 3440 * @param value the locale keyword value (either legacy type 3441 * such as "phonebook" or BCP 47 Unicode locale extension 3442 * type such as "phonebk"). 3443 * @return the well-formed BCP47 Unicode locale extension type, 3444 * or null if the locale keyword value cannot be mapped to 3445 * a well-formed BCP 47 Unicode locale extension type. 3446 * @see #toLegacyType(String, String) 3447 * @stable ICU 54 3448 */ 3449 public static String toUnicodeLocaleType(String keyword, String value) { 3450 String bcpType = KeyTypeData.toBcpType(keyword, value, null, null); 3451 if (bcpType == null && UnicodeLocaleExtension.isType(value)) { 3452 // unknown keyword, but syntax is fine.. 3453 bcpType = AsciiUtil.toLowerString(value); 3454 } 3455 return bcpType; 3456 } 3457 3458 /** 3459 * {@icu} Converts the specified keyword (BCP 47 Unicode locale extension key, or 3460 * legacy key) to the legacy key. For example, legacy key "collation" is 3461 * returned for the input BCP 47 Unicode locale extension key "co". 3462 * 3463 * @param keyword the input locale keyword (either BCP 47 Unicode locale 3464 * extension key or legacy key). 3465 * @return the well-formed legacy key, or null if the specified 3466 * keyword cannot be mapped to a well-formed legacy key. 3467 * @see #toUnicodeLocaleKey(String) 3468 * @stable ICU 54 3469 */ 3470 public static String toLegacyKey(String keyword) { 3471 String legacyKey = KeyTypeData.toLegacyKey(keyword); 3472 if (legacyKey == null) { 3473 // Checks if the specified locale key is well-formed with the legacy locale syntax. 3474 // 3475 // Note: 3476 // Neither ICU nor LDML/CLDR provides the definition of keyword syntax. 3477 // However, a key should not contain '=' obviously. For now, all existing 3478 // keys are using ASCII alphabetic letters only. We won't add any new key 3479 // that is not compatible with the BCP 47 syntax. Therefore, we assume 3480 // a valid key consist from [0-9a-zA-Z], no symbols. 3481 if (keyword.matches("[0-9a-zA-Z]+")) { 3482 legacyKey = AsciiUtil.toLowerString(keyword); 3483 } 3484 } 3485 return legacyKey; 3486 } 3487 3488 /** 3489 * {@icu} Converts the specified keyword value (BCP 47 Unicode locale extension type, 3490 * or legacy type or type alias) to the canonical legacy type. For example, 3491 * the legacy type "phonebook" is returned for the input BCP 47 Unicode 3492 * locale extension type "phonebk" with the keyword "collation" (or "co"). 3493 * <p> 3494 * When the specified keyword is not recognized, but the specified value 3495 * satisfies the syntax of legacy key, or when the specified keyword 3496 * allows 'variable' type and the specified value satisfies the syntax, 3497 * the lower-case version of the input value will be returned. 3498 * For example, 3499 * <code>toLegacyType("Foo", "Bar")</code> returns "bar", 3500 * <code>toLegacyType("vt", "00A4")</code> returns "00a4". 3501 * 3502 * @param keyword the locale keyword (either legacy keyword such as 3503 * "collation" or BCP 47 Unicode locale extension 3504 * key such as "co"). 3505 * @param value the locale keyword value (either BCP 47 Unicode locale 3506 * extension type such as "phonebk" or legacy keyword value 3507 * such as "phonebook"). 3508 * @return the well-formed legacy type, or null if the specified 3509 * keyword value cannot be mapped to a well-formed legacy 3510 * type. 3511 * @see #toUnicodeLocaleType(String, String) 3512 * @stable ICU 54 3513 */ 3514 public static String toLegacyType(String keyword, String value) { 3515 String legacyType = KeyTypeData.toLegacyType(keyword, value, null, null); 3516 if (legacyType == null) { 3517 // Checks if the specified locale type is well-formed with the legacy locale syntax. 3518 // 3519 // Note: 3520 // Neither ICU nor LDML/CLDR provides the definition of keyword syntax. 3521 // However, a type should not contain '=' obviously. For now, all existing 3522 // types are using ASCII alphabetic letters with a few symbol letters. We won't 3523 // add any new type that is not compatible with the BCP 47 syntax except timezone 3524 // IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain 3525 // '-' '_' '/' in the middle. 3526 if (value.matches("[0-9a-zA-Z]+([_/\\-][0-9a-zA-Z]+)*")) { 3527 legacyType = AsciiUtil.toLowerString(value); 3528 } 3529 } 3530 return legacyType; 3531 } 3532 3533 /** 3534 * <code>Builder</code> is used to build instances of <code>ULocale</code> 3535 * from values configured by the setters. Unlike the <code>ULocale</code> 3536 * constructors, the <code>Builder</code> checks if a value configured by a 3537 * setter satisfies the syntax requirements defined by the <code>ULocale</code> 3538 * class. A <code>ULocale</code> object created by a <code>Builder</code> is 3539 * well-formed and can be transformed to a well-formed IETF BCP 47 language tag 3540 * without losing information. 3541 * 3542 * <p><b>Note:</b> The <code>ULocale</code> class does not provide any 3543 * syntactic restrictions on variant, while BCP 47 requires each variant 3544 * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3 3545 * alphanumerics. The method <code>setVariant</code> throws 3546 * <code>IllformedLocaleException</code> for a variant that does not satisfy 3547 * this restriction. If it is necessary to support such a variant, use a 3548 * ULocale constructor. However, keep in mind that a <code>ULocale</code> 3549 * object created this way might lose the variant information when 3550 * transformed to a BCP 47 language tag. 3551 * 3552 * <p>The following example shows how to create a <code>Locale</code> object 3553 * with the <code>Builder</code>. 3554 * <blockquote> 3555 * <pre> 3556 * ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build(); 3557 * </pre> 3558 * </blockquote> 3559 * 3560 * <p>Builders can be reused; <code>clear()</code> resets all 3561 * fields to their default values. 3562 * 3563 * @see ULocale#toLanguageTag() 3564 * 3565 * @stable ICU 4.2 3566 */ 3567 public static final class Builder { 3568 3569 private final InternalLocaleBuilder _locbld; 3570 3571 /** 3572 * Constructs an empty Builder. The default value of all 3573 * fields, extensions, and private use information is the 3574 * empty string. 3575 * 3576 * @stable ICU 4.2 3577 */ 3578 public Builder() { 3579 _locbld = new InternalLocaleBuilder(); 3580 } 3581 3582 /** 3583 * Resets the <code>Builder</code> to match the provided 3584 * <code>locale</code>. Existing state is discarded. 3585 * 3586 * <p>All fields of the locale must be well-formed, see {@link Locale}. 3587 * 3588 * <p>Locales with any ill-formed fields cause 3589 * <code>IllformedLocaleException</code> to be thrown. 3590 * 3591 * @param locale the locale 3592 * @return This builder. 3593 * @throws IllformedLocaleException if <code>locale</code> has 3594 * any ill-formed fields. 3595 * @throws NullPointerException if <code>locale</code> is null. 3596 * 3597 * @stable ICU 4.2 3598 */ 3599 public Builder setLocale(ULocale locale) { 3600 try { 3601 _locbld.setLocale(locale.base(), locale.extensions()); 3602 } catch (LocaleSyntaxException e) { 3603 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3604 } 3605 return this; 3606 } 3607 3608 /** 3609 * Resets the Builder to match the provided IETF BCP 47 3610 * language tag. Discards the existing state. Null and the 3611 * empty string cause the builder to be reset, like {@link 3612 * #clear}. Grandfathered tags (see {@link 3613 * ULocale#forLanguageTag}) are converted to their canonical 3614 * form before being processed. Otherwise, the language tag 3615 * must be well-formed (see {@link ULocale}) or an exception is 3616 * thrown (unlike <code>ULocale.forLanguageTag</code>, which 3617 * just discards ill-formed and following portions of the 3618 * tag). 3619 * 3620 * @param languageTag the language tag 3621 * @return This builder. 3622 * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed 3623 * @see ULocale#forLanguageTag(String) 3624 * 3625 * @stable ICU 4.2 3626 */ 3627 public Builder setLanguageTag(String languageTag) { 3628 ParseStatus sts = new ParseStatus(); 3629 LanguageTag tag = LanguageTag.parse(languageTag, sts); 3630 if (sts.isError()) { 3631 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex()); 3632 } 3633 _locbld.setLanguageTag(tag); 3634 3635 return this; 3636 } 3637 3638 /** 3639 * Sets the language. If <code>language</code> is the empty string or 3640 * null, the language in this <code>Builder</code> is removed. Otherwise, 3641 * the language must be <a href="./Locale.html#def_language">well-formed</a> 3642 * or an exception is thrown. 3643 * 3644 * <p>The typical language value is a two or three-letter language 3645 * code as defined in ISO639. 3646 * 3647 * @param language the language 3648 * @return This builder. 3649 * @throws IllformedLocaleException if <code>language</code> is ill-formed 3650 * 3651 * @stable ICU 4.2 3652 */ 3653 public Builder setLanguage(String language) { 3654 try { 3655 _locbld.setLanguage(language); 3656 } catch (LocaleSyntaxException e) { 3657 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3658 } 3659 return this; 3660 } 3661 3662 /** 3663 * Sets the script. If <code>script</code> is null or the empty string, 3664 * the script in this <code>Builder</code> is removed. 3665 * Otherwise, the script must be well-formed or an exception is thrown. 3666 * 3667 * <p>The typical script value is a four-letter script code as defined by ISO 15924. 3668 * 3669 * @param script the script 3670 * @return This builder. 3671 * @throws IllformedLocaleException if <code>script</code> is ill-formed 3672 * 3673 * @stable ICU 4.2 3674 */ 3675 public Builder setScript(String script) { 3676 try { 3677 _locbld.setScript(script); 3678 } catch (LocaleSyntaxException e) { 3679 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3680 } 3681 return this; 3682 } 3683 3684 /** 3685 * Sets the region. If region is null or the empty string, the region 3686 * in this <code>Builder</code> is removed. Otherwise, 3687 * the region must be well-formed or an exception is thrown. 3688 * 3689 * <p>The typical region value is a two-letter ISO 3166 code or a 3690 * three-digit UN M.49 area code. 3691 * 3692 * <p>The country value in the <code>Locale</code> created by the 3693 * <code>Builder</code> is always normalized to upper case. 3694 * 3695 * @param region the region 3696 * @return This builder. 3697 * @throws IllformedLocaleException if <code>region</code> is ill-formed 3698 * 3699 * @stable ICU 4.2 3700 */ 3701 public Builder setRegion(String region) { 3702 try { 3703 _locbld.setRegion(region); 3704 } catch (LocaleSyntaxException e) { 3705 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3706 } 3707 return this; 3708 } 3709 3710 /** 3711 * Sets the variant. If variant is null or the empty string, the 3712 * variant in this <code>Builder</code> is removed. Otherwise, it 3713 * must consist of one or more well-formed subtags, or an exception is thrown. 3714 * 3715 * <p><b>Note:</b> This method checks if <code>variant</code> 3716 * satisfies the IETF BCP 47 variant subtag's syntax requirements, 3717 * and normalizes the value to lowercase letters. However, 3718 * the <code>ULocale</code> class does not impose any syntactic 3719 * restriction on variant. To set such a variant, 3720 * use a ULocale constructor. 3721 * 3722 * @param variant the variant 3723 * @return This builder. 3724 * @throws IllformedLocaleException if <code>variant</code> is ill-formed 3725 * 3726 * @stable ICU 4.2 3727 */ 3728 public Builder setVariant(String variant) { 3729 try { 3730 _locbld.setVariant(variant); 3731 } catch (LocaleSyntaxException e) { 3732 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3733 } 3734 return this; 3735 } 3736 3737 /** 3738 * Sets the extension for the given key. If the value is null or the 3739 * empty string, the extension is removed. Otherwise, the extension 3740 * must be well-formed or an exception is thrown. 3741 * 3742 * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION 3743 * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension. 3744 * Setting a value for this key replaces any existing Unicode locale key/type 3745 * pairs with those defined in the extension. 3746 * 3747 * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION 3748 * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be 3749 * well-formed, the value for this key needs only to have subtags of one to 3750 * eight alphanumeric characters, not two to eight as in the general case. 3751 * 3752 * @param key the extension key 3753 * @param value the extension value 3754 * @return This builder. 3755 * @throws IllformedLocaleException if <code>key</code> is illegal 3756 * or <code>value</code> is ill-formed 3757 * @see #setUnicodeLocaleKeyword(String, String) 3758 * 3759 * @stable ICU 4.2 3760 */ 3761 public Builder setExtension(char key, String value) { 3762 try { 3763 _locbld.setExtension(key, value); 3764 } catch (LocaleSyntaxException e) { 3765 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3766 } 3767 return this; 3768 } 3769 3770 /** 3771 * Sets the Unicode locale keyword type for the given key. If the type 3772 * is null, the Unicode keyword is removed. Otherwise, the key must be 3773 * non-null and both key and type must be well-formed or an exception 3774 * is thrown. 3775 * 3776 * <p>Keys and types are converted to lower case. 3777 * 3778 * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension} 3779 * replaces all Unicode locale keywords with those defined in the 3780 * extension. 3781 * 3782 * @param key the Unicode locale key 3783 * @param type the Unicode locale type 3784 * @return This builder. 3785 * @throws IllformedLocaleException if <code>key</code> or <code>type</code> 3786 * is ill-formed 3787 * @throws NullPointerException if <code>key</code> is null 3788 * @see #setExtension(char, String) 3789 * 3790 * @stable ICU 4.4 3791 */ 3792 public Builder setUnicodeLocaleKeyword(String key, String type) { 3793 try { 3794 _locbld.setUnicodeLocaleKeyword(key, type); 3795 } catch (LocaleSyntaxException e) { 3796 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3797 } 3798 return this; 3799 } 3800 3801 /** 3802 * Adds a unicode locale attribute, if not already present, otherwise 3803 * has no effect. The attribute must not be null and must be well-formed 3804 * or an exception is thrown. 3805 * 3806 * @param attribute the attribute 3807 * @return This builder. 3808 * @throws NullPointerException if <code>attribute</code> is null 3809 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 3810 * @see #setExtension(char, String) 3811 * 3812 * @stable ICU 4.6 3813 */ 3814 public Builder addUnicodeLocaleAttribute(String attribute) { 3815 try { 3816 _locbld.addUnicodeLocaleAttribute(attribute); 3817 } catch (LocaleSyntaxException e) { 3818 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3819 } 3820 return this; 3821 } 3822 3823 /** 3824 * Removes a unicode locale attribute, if present, otherwise has no 3825 * effect. The attribute must not be null and must be well-formed 3826 * or an exception is thrown. 3827 * 3828 * <p>Attribute comparision for removal is case-insensitive. 3829 * 3830 * @param attribute the attribute 3831 * @return This builder. 3832 * @throws NullPointerException if <code>attribute</code> is null 3833 * @throws IllformedLocaleException if <code>attribute</code> is ill-formed 3834 * @see #setExtension(char, String) 3835 * 3836 * @stable ICU 4.6 3837 */ 3838 public Builder removeUnicodeLocaleAttribute(String attribute) { 3839 try { 3840 _locbld.removeUnicodeLocaleAttribute(attribute); 3841 } catch (LocaleSyntaxException e) { 3842 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); 3843 } 3844 return this; 3845 } 3846 3847 /** 3848 * Resets the builder to its initial, empty state. 3849 * 3850 * @return this builder 3851 * 3852 * @stable ICU 4.2 3853 */ 3854 public Builder clear() { 3855 _locbld.clear(); 3856 return this; 3857 } 3858 3859 /** 3860 * Resets the extensions to their initial, empty state. 3861 * Language, script, region and variant are unchanged. 3862 * 3863 * @return this builder 3864 * @see #setExtension(char, String) 3865 * 3866 * @stable ICU 4.2 3867 */ 3868 public Builder clearExtensions() { 3869 _locbld.clearExtensions(); 3870 return this; 3871 } 3872 3873 /** 3874 * Returns an instance of <code>ULocale</code> created from the fields set 3875 * on this builder. 3876 * 3877 * @return a new Locale 3878 * 3879 * @stable ICU 4.4 3880 */ 3881 public ULocale build() { 3882 return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions()); 3883 } 3884 } 3885 3886 private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) { 3887 String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(), 3888 base.getVariant()); 3889 3890 Set<Character> extKeys = exts.getKeys(); 3891 if (!extKeys.isEmpty()) { 3892 // legacy locale ID assume Unicode locale keywords and 3893 // other extensions are at the same level. 3894 // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use 3895 3896 TreeMap<String, String> kwds = new TreeMap<String, String>(); 3897 for (Character key : extKeys) { 3898 Extension ext = exts.getExtension(key); 3899 if (ext instanceof UnicodeLocaleExtension) { 3900 UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext; 3901 Set<String> ukeys = uext.getUnicodeLocaleKeys(); 3902 for (String bcpKey : ukeys) { 3903 String bcpType = uext.getUnicodeLocaleType(bcpKey); 3904 // convert to legacy key/type 3905 String lkey = toLegacyKey(bcpKey); 3906 String ltype = toLegacyType(bcpKey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords 3907 // special handling for u-va-posix, since this is a variant, not a keyword 3908 if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) { 3909 id = id + "_POSIX"; 3910 } else { 3911 kwds.put(lkey, ltype); 3912 } 3913 } 3914 // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy 3915 Set<String> uattributes = uext.getUnicodeLocaleAttributes(); 3916 if (uattributes.size() > 0) { 3917 StringBuilder attrbuf = new StringBuilder(); 3918 for (String attr : uattributes) { 3919 if (attrbuf.length() > 0) { 3920 attrbuf.append('-'); 3921 } 3922 attrbuf.append(attr); 3923 } 3924 kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString()); 3925 } 3926 } else { 3927 kwds.put(String.valueOf(key), ext.getValue()); 3928 } 3929 } 3930 3931 if (!kwds.isEmpty()) { 3932 StringBuilder buf = new StringBuilder(id); 3933 buf.append("@"); 3934 Set<Map.Entry<String, String>> kset = kwds.entrySet(); 3935 boolean insertSep = false; 3936 for (Map.Entry<String, String> kwd : kset) { 3937 if (insertSep) { 3938 buf.append(";"); 3939 } else { 3940 insertSep = true; 3941 } 3942 buf.append(kwd.getKey()); 3943 buf.append("="); 3944 buf.append(kwd.getValue()); 3945 } 3946 3947 id = buf.toString(); 3948 } 3949 } 3950 return new ULocale(id); 3951 } 3952 3953 private BaseLocale base() { 3954 if (baseLocale == null) { 3955 String language, script, region, variant; 3956 language = script = region = variant = ""; 3957 if (!equals(ULocale.ROOT)) { 3958 LocaleIDParser lp = new LocaleIDParser(localeID); 3959 language = lp.getLanguage(); 3960 script = lp.getScript(); 3961 region = lp.getCountry(); 3962 variant = lp.getVariant(); 3963 } 3964 baseLocale = BaseLocale.getInstance(language, script, region, variant); 3965 } 3966 return baseLocale; 3967 } 3968 3969 private LocaleExtensions extensions() { 3970 if (extensions == null) { 3971 Iterator<String> kwitr = getKeywords(); 3972 if (kwitr == null) { 3973 extensions = LocaleExtensions.EMPTY_EXTENSIONS; 3974 } else { 3975 InternalLocaleBuilder intbld = new InternalLocaleBuilder(); 3976 while (kwitr.hasNext()) { 3977 String key = kwitr.next(); 3978 if (key.equals(LOCALE_ATTRIBUTE_KEY)) { 3979 // special keyword used for representing Unicode locale attributes 3980 String[] uattributes = getKeywordValue(key).split("[-_]"); 3981 for (String uattr : uattributes) { 3982 try { 3983 intbld.addUnicodeLocaleAttribute(uattr); 3984 } catch (LocaleSyntaxException e) { 3985 // ignore and fall through 3986 } 3987 } 3988 } else if (key.length() >= 2) { 3989 String bcpKey = toUnicodeLocaleKey(key); 3990 String bcpType = toUnicodeLocaleType(key, getKeywordValue(key)); 3991 if (bcpKey != null && bcpType != null) { 3992 try { 3993 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType); 3994 } catch (LocaleSyntaxException e) { 3995 // ignore and fall through 3996 } 3997 } 3998 } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) { 3999 try { 4000 intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_", 4001 LanguageTag.SEP)); 4002 } catch (LocaleSyntaxException e) { 4003 // ignore and fall through 4004 } 4005 } 4006 } 4007 extensions = intbld.getLocaleExtensions(); 4008 } 4009 } 4010 return extensions; 4011 } 4012 4013 /* 4014 * JDK Locale Helper 4015 */ 4016 private static final class JDKLocaleHelper { 4017 private static boolean hasScriptsAndUnicodeExtensions = false; 4018 private static boolean hasLocaleCategories = false; 4019 4020 /* 4021 * New methods in Java 7 Locale class 4022 */ 4023 private static Method mGetScript; 4024 private static Method mGetExtensionKeys; 4025 private static Method mGetExtension; 4026 private static Method mGetUnicodeLocaleKeys; 4027 private static Method mGetUnicodeLocaleAttributes; 4028 private static Method mGetUnicodeLocaleType; 4029 private static Method mForLanguageTag; 4030 4031 private static Method mGetDefault; 4032 private static Method mSetDefault; 4033 private static Object eDISPLAY; 4034 private static Object eFORMAT; 4035 4036 /* 4037 * This table is used for mapping between ICU and special Java 4038 * 6 locales. When an ICU locale matches <minumum base> with 4039 * <keyword>/<value>, the ICU locale is mapped to <Java> locale. 4040 * For example, both ja_JP@calendar=japanese and ja@calendar=japanese 4041 * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped 4042 * to Java locale "no_NO_NY". 4043 */ 4044 private static final String[][] JAVA6_MAPDATA = { 4045 // { <Java>, <ICU base>, <keyword>, <value>, <minimum base> 4046 { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"}, 4047 { "no_NO_NY", "nn_NO", null, null, "nn"}, 4048 { "th_TH_TH", "th_TH", "numbers", "thai", "th"}, 4049 }; 4050 4051 static { 4052 do { 4053 try { 4054 mGetScript = Locale.class.getMethod("getScript", (Class[]) null); 4055 mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null); 4056 mGetExtension = Locale.class.getMethod("getExtension", char.class); 4057 mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null); 4058 mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null); 4059 mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class); 4060 mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class); 4061 4062 hasScriptsAndUnicodeExtensions = true; 4063 } catch (NoSuchMethodException e) { 4064 } catch (IllegalArgumentException e) { 4065 } catch (SecurityException e) { 4066 // TODO : report? 4067 } 4068 4069 try { 4070 Class<?> cCategory = null; 4071 Class<?>[] classes = Locale.class.getDeclaredClasses(); 4072 for (Class<?> c : classes) { 4073 if (c.getName().equals("java.util.Locale$Category")) { 4074 cCategory = c; 4075 break; 4076 } 4077 } 4078 if (cCategory == null) { 4079 break; 4080 } 4081 mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory); 4082 mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class); 4083 4084 Method mName = cCategory.getMethod("name", (Class[]) null); 4085 Object[] enumConstants = cCategory.getEnumConstants(); 4086 for (Object e : enumConstants) { 4087 String catVal = (String)mName.invoke(e, (Object[])null); 4088 if (catVal.equals("DISPLAY")) { 4089 eDISPLAY = e; 4090 } else if (catVal.equals("FORMAT")) { 4091 eFORMAT = e; 4092 } 4093 } 4094 if (eDISPLAY == null || eFORMAT == null) { 4095 break; 4096 } 4097 4098 hasLocaleCategories = true; 4099 } catch (NoSuchMethodException e) { 4100 } catch (IllegalArgumentException e) { 4101 } catch (IllegalAccessException e) { 4102 } catch (InvocationTargetException e) { 4103 } catch (SecurityException e) { 4104 // TODO : report? 4105 } 4106 } while (false); 4107 } 4108 4109 private JDKLocaleHelper() { 4110 } 4111 4112 public static boolean hasLocaleCategories() { 4113 return hasLocaleCategories; 4114 } 4115 4116 public static ULocale toULocale(Locale loc) { 4117 return hasScriptsAndUnicodeExtensions ? toULocale7(loc) : toULocale6(loc); 4118 } 4119 4120 public static Locale toLocale(ULocale uloc) { 4121 return hasScriptsAndUnicodeExtensions ? toLocale7(uloc) : toLocale6(uloc); 4122 } 4123 4124 private static ULocale toULocale7(Locale loc) { 4125 String language = loc.getLanguage(); 4126 String script = ""; 4127 String country = loc.getCountry(); 4128 String variant = loc.getVariant(); 4129 4130 Set<String> attributes = null; 4131 Map<String, String> keywords = null; 4132 4133 try { 4134 script = (String) mGetScript.invoke(loc, (Object[]) null); 4135 @SuppressWarnings("unchecked") 4136 Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null); 4137 if (!extKeys.isEmpty()) { 4138 for (Character extKey : extKeys) { 4139 if (extKey.charValue() == 'u') { 4140 // Found Unicode locale extension 4141 4142 // attributes 4143 @SuppressWarnings("unchecked") 4144 Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null); 4145 if (!uAttributes.isEmpty()) { 4146 attributes = new TreeSet<String>(); 4147 for (String attr : uAttributes) { 4148 attributes.add(attr); 4149 } 4150 } 4151 4152 // keywords 4153 @SuppressWarnings("unchecked") 4154 Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null); 4155 for (String kwKey : uKeys) { 4156 String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey); 4157 if (kwVal != null) { 4158 if (kwKey.equals("va")) { 4159 // va-* is interpreted as a variant 4160 variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant; 4161 } else { 4162 if (keywords == null) { 4163 keywords = new TreeMap<String, String>(); 4164 } 4165 keywords.put(kwKey, kwVal); 4166 } 4167 } 4168 } 4169 } else { 4170 String extVal = (String) mGetExtension.invoke(loc, extKey); 4171 if (extVal != null) { 4172 if (keywords == null) { 4173 keywords = new TreeMap<String, String>(); 4174 } 4175 keywords.put(String.valueOf(extKey), extVal); 4176 } 4177 } 4178 } 4179 } 4180 } catch (IllegalAccessException e) { 4181 throw new RuntimeException(e); 4182 } catch (InvocationTargetException e) { 4183 throw new RuntimeException(e); 4184 } 4185 4186 // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU, 4187 // and it should be transformed to nn_NO. 4188 4189 // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert 4190 // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY. 4191 4192 if (language.equals("no") && country.equals("NO") && variant.equals("NY")) { 4193 language = "nn"; 4194 variant = ""; 4195 } 4196 4197 // Constructing ID 4198 StringBuilder buf = new StringBuilder(language); 4199 4200 if (script.length() > 0) { 4201 buf.append('_'); 4202 buf.append(script); 4203 } 4204 4205 if (country.length() > 0) { 4206 buf.append('_'); 4207 buf.append(country); 4208 } 4209 4210 if (variant.length() > 0) { 4211 if (country.length() == 0) { 4212 buf.append('_'); 4213 } 4214 buf.append('_'); 4215 buf.append(variant); 4216 } 4217 4218 if (attributes != null) { 4219 // transform Unicode attributes into a keyword 4220 StringBuilder attrBuf = new StringBuilder(); 4221 for (String attr : attributes) { 4222 if (attrBuf.length() != 0) { 4223 attrBuf.append('-'); 4224 } 4225 attrBuf.append(attr); 4226 } 4227 if (keywords == null) { 4228 keywords = new TreeMap<String, String>(); 4229 } 4230 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString()); 4231 } 4232 4233 if (keywords != null) { 4234 buf.append('@'); 4235 boolean addSep = false; 4236 for (Entry<String, String> kwEntry : keywords.entrySet()) { 4237 String kwKey = kwEntry.getKey(); 4238 String kwVal = kwEntry.getValue(); 4239 4240 if (kwKey.length() != 1) { 4241 // Unicode locale key 4242 kwKey = toLegacyKey(kwKey); 4243 // use "yes" as the value of typeless keywords 4244 kwVal = toLegacyType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal)); 4245 } 4246 4247 if (addSep) { 4248 buf.append(';'); 4249 } else { 4250 addSep = true; 4251 } 4252 buf.append(kwKey); 4253 buf.append('='); 4254 buf.append(kwVal); 4255 } 4256 } 4257 4258 return new ULocale(getName(buf.toString()), loc); 4259 } 4260 4261 private static ULocale toULocale6(Locale loc) { 4262 ULocale uloc = null; 4263 String locStr = loc.toString(); 4264 if (locStr.length() == 0) { 4265 uloc = ULocale.ROOT; 4266 } else { 4267 for (int i = 0; i < JAVA6_MAPDATA.length; i++) { 4268 if (JAVA6_MAPDATA[i][0].equals(locStr)) { 4269 LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]); 4270 p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]); 4271 locStr = p.getName(); 4272 break; 4273 } 4274 } 4275 uloc = new ULocale(getName(locStr), loc); 4276 } 4277 return uloc; 4278 } 4279 4280 private static Locale toLocale7(ULocale uloc) { 4281 Locale loc = null; 4282 String ulocStr = uloc.getName(); 4283 if (uloc.getScript().length() > 0 || ulocStr.contains("@")) { 4284 // With script or keywords available, the best way 4285 // to get a mapped Locale is to go through a language tag. 4286 // A Locale with script or keywords can only have variants 4287 // that is 1 to 8 alphanum. If this ULocale has a variant 4288 // subtag not satisfying the criteria, the variant subtag 4289 // will be lost. 4290 String tag = uloc.toLanguageTag(); 4291 4292 // Workaround for variant casing problem: 4293 // 4294 // The variant field in ICU is case insensitive and normalized 4295 // to upper case letters by getVariant(), while 4296 // the variant field in JDK Locale is case sensitive. 4297 // ULocale#toLanguageTag use lower case characters for 4298 // BCP 47 variant and private use x-lvariant. 4299 // 4300 // Locale#forLanguageTag in JDK preserves character casing 4301 // for variant. Because ICU always normalizes variant to 4302 // upper case, we convert language tag to upper case here. 4303 tag = AsciiUtil.toUpperString(tag); 4304 4305 try { 4306 loc = (Locale)mForLanguageTag.invoke(null, tag); 4307 } catch (IllegalAccessException e) { 4308 throw new RuntimeException(e); 4309 } catch (InvocationTargetException e) { 4310 throw new RuntimeException(e); 4311 } 4312 } 4313 if (loc == null) { 4314 // Without script or keywords, use a Locale constructor, 4315 // so we can preserve any ill-formed variants. 4316 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant()); 4317 } 4318 return loc; 4319 } 4320 4321 private static Locale toLocale6(ULocale uloc) { 4322 String locstr = uloc.getBaseName(); 4323 for (int i = 0; i < JAVA6_MAPDATA.length; i++) { 4324 if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) { 4325 if (JAVA6_MAPDATA[i][2] != null) { 4326 String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]); 4327 if (val != null && val.equals(JAVA6_MAPDATA[i][3])) { 4328 locstr = JAVA6_MAPDATA[i][0]; 4329 break; 4330 } 4331 } else { 4332 locstr = JAVA6_MAPDATA[i][0]; 4333 break; 4334 } 4335 } 4336 } 4337 LocaleIDParser p = new LocaleIDParser(locstr); 4338 String[] names = p.getLanguageScriptCountryVariant(); 4339 return new Locale(names[0], names[2], names[3]); 4340 } 4341 4342 public static Locale getDefault(Category category) { 4343 Locale loc = Locale.getDefault(); 4344 if (hasLocaleCategories) { 4345 Object cat = null; 4346 switch (category) { 4347 case DISPLAY: 4348 cat = eDISPLAY; 4349 break; 4350 case FORMAT: 4351 cat = eFORMAT; 4352 break; 4353 } 4354 if (cat != null) { 4355 try { 4356 loc = (Locale)mGetDefault.invoke(null, cat); 4357 } catch (InvocationTargetException e) { 4358 // fall through - use the base default 4359 } catch (IllegalArgumentException e) { 4360 // fall through - use the base default 4361 } catch (IllegalAccessException e) { 4362 // fall through - use the base default 4363 } 4364 } 4365 } 4366 return loc; 4367 } 4368 4369 public static void setDefault(Category category, Locale newLocale) { 4370 if (hasLocaleCategories) { 4371 Object cat = null; 4372 switch (category) { 4373 case DISPLAY: 4374 cat = eDISPLAY; 4375 break; 4376 case FORMAT: 4377 cat = eFORMAT; 4378 break; 4379 } 4380 if (cat != null) { 4381 try { 4382 mSetDefault.invoke(null, cat, newLocale); 4383 } catch (InvocationTargetException e) { 4384 // fall through - no effects 4385 } catch (IllegalArgumentException e) { 4386 // fall through - no effects 4387 } catch (IllegalAccessException e) { 4388 // fall through - no effects 4389 } 4390 } 4391 } 4392 } 4393 4394 // Returns true if the given Locale matches the original 4395 // default locale initialized by JVM by checking user.XXX 4396 // system properties. When the system properties are not accessible, 4397 // this method returns false. 4398 public static boolean isOriginalDefaultLocale(Locale loc) { 4399 if (hasScriptsAndUnicodeExtensions) { 4400 String script = ""; 4401 try { 4402 script = (String) mGetScript.invoke(loc, (Object[]) null); 4403 } catch (Exception e) { 4404 return false; 4405 } 4406 4407 return loc.getLanguage().equals(getSystemProperty("user.language")) 4408 && loc.getCountry().equals(getSystemProperty("user.country")) 4409 && loc.getVariant().equals(getSystemProperty("user.variant")) 4410 && script.equals(getSystemProperty("user.script")); 4411 } 4412 return loc.getLanguage().equals(getSystemProperty("user.language")) 4413 && loc.getCountry().equals(getSystemProperty("user.country")) 4414 && loc.getVariant().equals(getSystemProperty("user.variant")); 4415 } 4416 4417 public static String getSystemProperty(String key) { 4418 String val = null; 4419 final String fkey = key; 4420 if (System.getSecurityManager() != null) { 4421 try { 4422 val = AccessController.doPrivileged(new PrivilegedAction<String>() { 4423 public String run() { 4424 return System.getProperty(fkey); 4425 } 4426 }); 4427 } catch (AccessControlException e) { 4428 // ignore 4429 } 4430 } else { 4431 val = System.getProperty(fkey); 4432 } 4433 return val; 4434 } 4435 } 4436} 4437