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