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