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