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