2 ******************************************************************************
\r
3 * Copyright (C) 2003-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 ******************************************************************************
\r
8 package com.ibm.icu.util;
\r
10 import java.io.Serializable;
\r
11 import java.text.ParseException;
\r
12 import java.util.Iterator;
\r
13 import java.util.Locale;
\r
14 import java.util.Map;
\r
15 import java.util.MissingResourceException;
\r
16 import java.util.Set;
\r
17 import java.util.TreeMap;
\r
19 import com.ibm.icu.impl.ICUCache;
\r
20 import com.ibm.icu.impl.ICUResourceBundle;
\r
21 import com.ibm.icu.impl.ICUResourceTableAccess;
\r
22 import com.ibm.icu.impl.LocaleIDParser;
\r
23 import com.ibm.icu.impl.LocaleIDs;
\r
24 import com.ibm.icu.impl.LocaleUtility;
\r
25 import com.ibm.icu.impl.SimpleCache;
\r
26 import com.ibm.icu.impl.locale.AsciiUtil;
\r
27 import com.ibm.icu.impl.locale.BaseLocale;
\r
28 import com.ibm.icu.impl.locale.Extension;
\r
29 import com.ibm.icu.impl.locale.InternalLocaleBuilder;
\r
30 import com.ibm.icu.impl.locale.LanguageTag;
\r
31 import com.ibm.icu.impl.locale.LocaleExtensions;
\r
32 import com.ibm.icu.impl.locale.LocaleSyntaxException;
\r
33 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
\r
34 import com.ibm.icu.text.LocaleDisplayNames;
\r
35 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
\r
38 * {@icuenhanced java.util.Locale}.{@icu _usage_}
\r
40 * A class analogous to {@link java.util.Locale} that provides additional
\r
41 * support for ICU protocol. In ICU 3.0 this class is enhanced to support
\r
42 * RFC 3066 language identifiers.
\r
44 * <p>Many classes and services in ICU follow a factory idiom, in
\r
45 * which a factory method or object responds to a client request with
\r
46 * an object. The request includes a locale (the <i>requested</i>
\r
47 * locale), and the returned object is constructed using data for that
\r
48 * locale. The system may lack data for the requested locale, in
\r
49 * which case the locale fallback mechanism will be invoked until a
\r
50 * populated locale is found (the <i>valid</i> locale). Furthermore,
\r
51 * even when a populated locale is found (the <i>valid</i> locale),
\r
52 * further fallback may be required to reach a locale containing the
\r
53 * specific data required by the service (the <i>actual</i> locale).
\r
55 * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
\r
56 * Normalization 'cleans up' ICU locale ids as follows:
\r
58 * <li>language, script, country, variant, and keywords are properly cased<br>
\r
59 * (lower, title, upper, upper, and lower case respectively)</li>
\r
60 * <li>hyphens used as separators are converted to underscores</li>
\r
61 * <li>three-letter language and country ids are converted to two-letter
\r
62 * equivalents where available</li>
\r
63 * <li>surrounding spaces are removed from keywords and values</li>
\r
64 * <li>if there are multiple keywords, they are put in sorted order</li>
\r
66 * Canonicalization additionally performs the following:
\r
68 * <li>POSIX ids are converted to ICU format IDs</li>
\r
69 * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
\r
70 * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
\r
72 * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
\r
74 * All ULocale constructors automatically normalize the locale id. To handle
\r
75 * POSIX ids, <code>canonicalize</code> can be called to convert the id
\r
76 * to canonical form, or the <code>canonicalInstance</code> factory method
\r
77 * can be called.</p>
\r
79 * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
\r
80 * #ACTUAL_LOCALE} intended for use in methods named
\r
81 * <tt>getLocale()</tt>. These methods exist in several ICU classes,
\r
82 * including {@link com.ibm.icu.util.Calendar}, {@link
\r
83 * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
\r
84 * {@link com.ibm.icu.text.BreakIterator},
\r
85 * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>,
\r
86 * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
\r
87 * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
\r
88 * any. Once an object of one of these classes has been created,
\r
89 * <tt>getLocale()</tt> may be called on it to determine the valid and
\r
90 * actual locale arrived at during the object's construction.
\r
92 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
\r
93 * locale is not, in most cases.
\r
95 * @see java.util.Locale
\r
98 * @author Ram Viswanadha
\r
101 public final class ULocale implements Serializable {
\r
102 // using serialver from jdk1.4.2_05
\r
103 private static final long serialVersionUID = 3715177670352309217L;
\r
106 * Useful constant for language.
\r
109 public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
\r
112 * Useful constant for language.
\r
115 public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
\r
118 * Useful constant for language.
\r
121 public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
\r
124 * Useful constant for language.
\r
127 public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
\r
130 * Useful constant for language.
\r
133 public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
\r
136 * Useful constant for language.
\r
139 public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
\r
142 * Useful constant for language.
\r
145 public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
\r
148 * Useful constant for language.
\r
151 public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans", Locale.CHINESE);
\r
154 * Useful constant for language.
\r
157 public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant", Locale.CHINESE);
\r
160 * Useful constant for country/region.
\r
163 public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
\r
166 * Useful constant for country/region.
\r
169 public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
\r
172 * Useful constant for country/region.
\r
175 public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
\r
178 * Useful constant for country/region.
\r
181 public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
\r
184 * Useful constant for country/region.
\r
187 public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
\r
190 * Useful constant for country/region.
\r
193 public static final ULocale CHINA = new ULocale("zh_Hans_CN", Locale.CHINA);
\r
196 * Useful constant for country/region.
\r
199 public static final ULocale PRC = CHINA;
\r
202 * Useful constant for country/region.
\r
205 public static final ULocale TAIWAN = new ULocale("zh_Hant_TW", Locale.TAIWAN);
\r
208 * Useful constant for country/region.
\r
211 public static final ULocale UK = new ULocale("en_GB", Locale.UK);
\r
214 * Useful constant for country/region.
\r
217 public static final ULocale US = new ULocale("en_US", Locale.US);
\r
220 * Useful constant for country/region.
\r
223 public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
\r
226 * Useful constant for country/region.
\r
229 public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
\r
234 private static final String EMPTY_STRING = "";
\r
236 // Used in both ULocale and LocaleIDParser, so moved up here.
\r
237 private static final char UNDERSCORE = '_';
\r
239 // default empty locale
\r
240 private static final Locale EMPTY_LOCALE = new Locale("", "");
\r
243 * The root ULocale.
\r
246 public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
\r
248 private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>();
\r
251 * Cache the locale.
\r
253 private transient volatile Locale locale;
\r
256 * The raw localeID that we were passed in.
\r
258 private String localeID;
\r
261 * Cache the locale data container fields.
\r
262 * In future, we want to use them as the primary locale identifier storage.
\r
264 private transient volatile BaseLocale baseLocale;
\r
265 private transient volatile LocaleExtensions extensions;
\r
268 private static String[][] CANONICALIZE_MAP;
\r
269 private static String[][] variantsToKeywords;
\r
271 private static void initCANONICALIZE_MAP() {
\r
272 if (CANONICALIZE_MAP == null) {
\r
274 * This table lists pairs of locale ids for canonicalization. The
\r
275 * The 1st item is the normalized id. The 2nd item is the
\r
276 * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
\r
278 String[][] tempCANONICALIZE_MAP = {
\r
279 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */
\r
280 { "C", "en_US_POSIX", null, null }, /* POSIX name */
\r
281 { "art_LOJBAN", "jbo", null, null }, /* registered name */
\r
282 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */
\r
283 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */
\r
284 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
\r
285 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */
\r
286 { "de_1901", "de__1901", null, null }, /* registered name */
\r
287 { "de_1906", "de__1906", null, null }, /* registered name */
\r
288 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
\r
289 { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
\r
290 { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
\r
291 { "de_LU_PREEURO", "de_LU", "currency", "EUR" },
\r
292 { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
\r
293 { "en_BOONT", "en__BOONT", null, null }, /* registered name */
\r
294 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */
\r
295 { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
\r
296 { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
\r
297 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
\r
298 { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
\r
299 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
\r
300 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
\r
301 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
\r
302 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
\r
303 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
\r
304 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
\r
305 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
\r
306 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
\r
307 { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
\r
308 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
\r
309 // { "nb_NO_NY", "nn_NO", null, null },
\r
310 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
\r
311 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
\r
312 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
\r
313 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */
\r
314 { "sr_SP_CYRL", "sr_Cyrl_RS", null, null }, /* .NET name */
\r
315 { "sr_SP_LATN", "sr_Latn_RS", null, null }, /* .NET name */
\r
316 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
\r
317 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
\r
318 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
\r
319 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */
\r
320 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */
\r
321 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */
\r
322 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */
\r
323 { "zh_GAN", "zh__GAN", null, null }, /* registered name */
\r
324 { "zh_GUOYU", "zh", null, null }, /* registered name */
\r
325 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */
\r
326 { "zh_MIN", "zh__MIN", null, null }, /* registered name */
\r
327 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */
\r
328 { "zh_WUU", "zh__WUU", null, null }, /* registered name */
\r
329 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */
\r
330 { "zh_YUE", "zh__YUE", null, null } /* registered name */
\r
333 synchronized (ULocale.class) {
\r
334 if (CANONICALIZE_MAP == null) {
\r
335 CANONICALIZE_MAP = tempCANONICALIZE_MAP;
\r
339 if (variantsToKeywords == null) {
\r
341 * This table lists pairs of locale ids for canonicalization. The
\r
342 * The first item is the normalized variant id.
\r
344 String[][] tempVariantsToKeywords = {
\r
345 { "EURO", "currency", "EUR" },
\r
346 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
\r
347 { "STROKE", "collation", "stroke" } /* Solaris variant */
\r
350 synchronized (ULocale.class) {
\r
351 if (variantsToKeywords == null) {
\r
352 variantsToKeywords = tempVariantsToKeywords;
\r
359 * This table is used for mapping between ICU and special Java
\r
360 * locales. When an ICU locale matches <minumum base> with
\r
361 * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
\r
362 * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
\r
363 * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped
\r
364 * to Java locale "no_NO_NY".
\r
366 private static final String[][] _javaLocaleMap = {
\r
367 // { <Java>, <ICU base>, <keyword>, <value>, <minimum base>
\r
368 { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"},
\r
369 { "no_NO_NY", "nn_NO", null, null, "nn"},
\r
370 { "th_TH_TH", "th_TH", "numbers", "thai", "th"},
\r
374 * Private constructor used by static initializers.
\r
376 private ULocale(String localeID, Locale locale) {
\r
377 this.localeID = localeID;
\r
378 this.locale = locale;
\r
382 * Construct a ULocale object from a {@link java.util.Locale}.
\r
383 * @param loc a JDK locale
\r
385 private ULocale(Locale loc) {
\r
386 this.localeID = getName(forLocale(loc).toString());
\r
391 * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
\r
392 * The ULocale is canonicalized.
\r
393 * @param loc a JDK locale
\r
396 public static ULocale forLocale(Locale loc) {
\r
400 ULocale result = CACHE.get(loc);
\r
401 if (result == null) {
\r
402 if (defaultULocale != null && loc == defaultULocale.locale) {
\r
403 result = defaultULocale;
\r
405 String locStr = loc.toString();
\r
406 if (locStr.length() == 0) {
\r
409 for (int i = 0; i < _javaLocaleMap.length; i++) {
\r
410 if (_javaLocaleMap[i][0].equals(locStr)) {
\r
411 LocaleIDParser p = new LocaleIDParser(_javaLocaleMap[i][1]);
\r
412 p.setKeywordValue(_javaLocaleMap[i][2], _javaLocaleMap[i][3]);
\r
413 locStr = p.getName();
\r
417 result = new ULocale(locStr, loc);
\r
420 CACHE.put(loc, result);
\r
426 * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
\r
427 * of optional language, script, country, and variant fields in that order,
\r
428 * separated by underscores, followed by an optional keyword list. The
\r
429 * script, if present, is four characters long-- this distinguishes it
\r
430 * from a country code, which is two characters long. Other fields
\r
431 * are distinguished by position as indicated by the underscores. The
\r
432 * start of the keyword list is indicated by '@', and consists of two
\r
433 * or more keyword/value pairs separated by semicolons(';').
\r
435 * <p>This constructor does not canonicalize the localeID. So, for
\r
436 * example, "zh__pinyin" remains unchanged instead of converting
\r
437 * to "zh@collation=pinyin". By default ICU only recognizes the
\r
438 * latter as specifying pinyin collation. Use {@link #createCanonical}
\r
439 * or {@link #canonicalize} if you need to canonicalize the localeID.
\r
441 * @param localeID string representation of the locale, e.g:
\r
442 * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
\r
445 public ULocale(String localeID) {
\r
446 this.localeID = getName(localeID);
\r
450 * Convenience overload of ULocale(String, String, String) for
\r
451 * compatibility with java.util.Locale.
\r
452 * @see #ULocale(String, String, String)
\r
455 public ULocale(String a, String b) {
\r
460 * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
\r
461 * c. These fields are concatenated using underscores to form a localeID of the form
\r
462 * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
\r
463 * localeID)</code>.
\r
465 * <p>Java locale strings consisting of language, country, and
\r
466 * variant will be handled by this form, since the country code
\r
467 * (being shorter than four letters long) will not be interpreted
\r
468 * as a script code. If a script code is present, the final
\r
469 * argument ('c') will be interpreted as the country code. It is
\r
470 * recommended that this constructor only be used to ease porting,
\r
471 * and that clients instead use the single-argument constructor
\r
472 * when constructing a ULocale from a localeID.
\r
473 * @param a first component of the locale id
\r
474 * @param b second component of the locale id
\r
475 * @param c third component of the locale id
\r
476 * @see #ULocale(String)
\r
479 public ULocale(String a, String b, String c) {
\r
480 localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
\r
484 * {@icu} Creates a ULocale from the id by first canonicalizing the id.
\r
485 * @param nonCanonicalID the locale id to canonicalize
\r
486 * @return the locale created from the canonical version of the ID.
\r
489 public static ULocale createCanonical(String nonCanonicalID) {
\r
490 return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
\r
493 private static String lscvToID(String lang, String script, String country, String variant) {
\r
494 StringBuilder buf = new StringBuilder();
\r
496 if (lang != null && lang.length() > 0) {
\r
499 if (script != null && script.length() > 0) {
\r
500 buf.append(UNDERSCORE);
\r
501 buf.append(script);
\r
503 if (country != null && country.length() > 0) {
\r
504 buf.append(UNDERSCORE);
\r
505 buf.append(country);
\r
507 if (variant != null && variant.length() > 0) {
\r
508 if (country == null || country.length() == 0) {
\r
509 buf.append(UNDERSCORE);
\r
511 buf.append(UNDERSCORE);
\r
512 buf.append(variant);
\r
514 return buf.toString();
\r
518 * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
\r
519 * @return a JDK locale that either exactly represents this object
\r
520 * or is the closest approximation.
\r
523 public Locale toLocale() {
\r
524 if (locale == null) {
\r
525 LocaleIDParser p = new LocaleIDParser(localeID);
\r
526 String base = p.getBaseName();
\r
527 for (int i = 0; i < _javaLocaleMap.length; i++) {
\r
528 if (base.equals(_javaLocaleMap[i][1]) || base.equals(_javaLocaleMap[i][4])) {
\r
529 if (_javaLocaleMap[i][2] != null) {
\r
530 String val = p.getKeywordValue(_javaLocaleMap[i][2]);
\r
531 if (val != null && val.equals(_javaLocaleMap[i][3])) {
\r
532 p = new LocaleIDParser(_javaLocaleMap[i][0]);
\r
536 p = new LocaleIDParser(_javaLocaleMap[i][0]);
\r
541 String[] names = p.getLanguageScriptCountryVariant();
\r
542 locale = new Locale(names[0], names[2], names[3]);
\r
547 private static ICUCache<String, String> nameCache = new SimpleCache<String, String>();
\r
549 * Keep our own default ULocale.
\r
551 private static Locale defaultLocale = Locale.getDefault();
\r
552 private static ULocale defaultULocale = new ULocale(defaultLocale);
\r
555 * Returns the current default ULocale.
\r
558 public static ULocale getDefault() {
\r
559 synchronized (ULocale.class) {
\r
560 Locale currentDefault = Locale.getDefault();
\r
561 if (!defaultLocale.equals(currentDefault)) {
\r
562 defaultLocale = currentDefault;
\r
563 defaultULocale = new ULocale(defaultLocale);
\r
565 return defaultULocale;
\r
570 * {@icu} Sets the default ULocale. This also sets the default Locale.
\r
571 * If the caller does not have write permission to the
\r
572 * user.language property, a security exception will be thrown,
\r
573 * and the default ULocale will remain unchanged.
\r
574 * @param newLocale the new default locale
\r
575 * @throws SecurityException if a security manager exists and its
\r
576 * <code>checkPermission</code> method doesn't allow the operation.
\r
577 * @throws NullPointerException if <code>newLocale</code> is null
\r
578 * @see SecurityManager#checkPermission(java.security.Permission)
\r
579 * @see java.util.PropertyPermission
\r
582 public static synchronized void setDefault(ULocale newLocale){
\r
583 Locale.setDefault(newLocale.toLocale());
\r
584 defaultULocale = newLocale;
\r
588 * This is for compatibility with Locale-- in actuality, since ULocale is
\r
589 * immutable, there is no reason to clone it, so this API returns 'this'.
\r
592 public Object clone() {
\r
597 * Returns the hashCode.
\r
600 public int hashCode() {
\r
601 return localeID.hashCode();
\r
605 * Returns true if the other object is another ULocale with the
\r
606 * same full name, or is a String localeID that matches the full name.
\r
607 * Note that since names are not canonicalized, two ULocales that
\r
608 * function identically might not compare equal.
\r
610 * @return true if this Locale is equal to the specified object.
\r
613 public boolean equals(Object obj) {
\r
617 if (obj instanceof String) {
\r
618 return localeID.equals((String)obj);
\r
620 if (obj instanceof ULocale) {
\r
621 return localeID.equals(((ULocale)obj).localeID);
\r
627 * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
\r
628 * not <code>Locale</code>. Returns a list of all installed locales.
\r
631 public static ULocale[] getAvailableLocales() {
\r
632 return ICUResourceBundle.getAvailableULocales();
\r
636 * Returns a list of all 2-letter country codes defined in ISO 3166.
\r
637 * Can be used to create Locales.
\r
640 public static String[] getISOCountries() {
\r
641 return LocaleIDs.getISOCountries();
\r
645 * Returns a list of all 2-letter language codes defined in ISO 639.
\r
646 * Can be used to create Locales.
\r
647 * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
\r
648 * The list this function returns includes both the new and the old codes for the
\r
649 * languages whose codes have changed.]
\r
652 public static String[] getISOLanguages() {
\r
653 return LocaleIDs.getISOLanguages();
\r
657 * Returns the language code for this locale, which will either be the empty string
\r
658 * or a lowercase ISO 639 code.
\r
659 * @see #getDisplayLanguage()
\r
660 * @see #getDisplayLanguage(ULocale)
\r
663 public String getLanguage() {
\r
664 return getLanguage(localeID);
\r
668 * Returns the language code for the locale ID,
\r
669 * which will either be the empty string
\r
670 * or a lowercase ISO 639 code.
\r
671 * @see #getDisplayLanguage()
\r
672 * @see #getDisplayLanguage(ULocale)
\r
675 public static String getLanguage(String localeID) {
\r
676 return new LocaleIDParser(localeID).getLanguage();
\r
680 * {@icu} Returns the script code for this locale, which might be the empty string.
\r
681 * @see #getDisplayScript()
\r
682 * @see #getDisplayScript(ULocale)
\r
685 public String getScript() {
\r
686 return getScript(localeID);
\r
690 * {@icu} Returns the script code for the specified locale, which might be the empty
\r
692 * @see #getDisplayScript()
\r
693 * @see #getDisplayScript(ULocale)
\r
696 public static String getScript(String localeID) {
\r
697 return new LocaleIDParser(localeID).getScript();
\r
701 * Returns the country/region code for this locale, which will either be the empty string
\r
702 * or an uppercase ISO 3166 2-letter code.
\r
703 * @see #getDisplayCountry()
\r
704 * @see #getDisplayCountry(ULocale)
\r
707 public String getCountry() {
\r
708 return getCountry(localeID);
\r
712 * Returns the country/region code for this locale, which will either be the empty string
\r
713 * or an uppercase ISO 3166 2-letter code.
\r
714 * @param localeID The locale identification string.
\r
715 * @see #getDisplayCountry()
\r
716 * @see #getDisplayCountry(ULocale)
\r
719 public static String getCountry(String localeID) {
\r
720 return new LocaleIDParser(localeID).getCountry();
\r
724 * Returns the variant code for this locale, which might be the empty string.
\r
725 * @see #getDisplayVariant()
\r
726 * @see #getDisplayVariant(ULocale)
\r
729 public String getVariant() {
\r
730 return getVariant(localeID);
\r
734 * Returns the variant code for the specified locale, which might be the empty string.
\r
735 * @see #getDisplayVariant()
\r
736 * @see #getDisplayVariant(ULocale)
\r
739 public static String getVariant(String localeID) {
\r
740 return new LocaleIDParser(localeID).getVariant();
\r
744 * {@icu} Returns the fallback locale for the specified locale, which might be the
\r
748 public static String getFallback(String localeID) {
\r
749 return getFallbackString(getName(localeID));
\r
753 * {@icu} Returns the fallback locale for this locale. If this locale is root,
\r
757 public ULocale getFallback() {
\r
758 if (localeID.length() == 0 || localeID.charAt(0) == '@') {
\r
761 return new ULocale(getFallbackString(localeID), (Locale)null);
\r
765 * Returns the given (canonical) locale id minus the last part before the tags.
\r
767 private static String getFallbackString(String fallback) {
\r
768 int extStart = fallback.indexOf('@');
\r
769 if (extStart == -1) {
\r
770 extStart = fallback.length();
\r
772 int last = fallback.lastIndexOf('_', extStart);
\r
776 // truncate empty segment
\r
778 if (fallback.charAt(last - 1) != '_') {
\r
784 return fallback.substring(0, last) + fallback.substring(extStart);
\r
788 * {@icu} Returns the (normalized) base name for this locale.
\r
789 * @return the base name as a String.
\r
792 public String getBaseName() {
\r
793 return getBaseName(localeID);
\r
797 * {@icu} Returns the (normalized) base name for the specified locale.
\r
798 * @param localeID the locale ID as a string
\r
799 * @return the base name as a String.
\r
802 public static String getBaseName(String localeID){
\r
803 if (localeID.indexOf('@') == -1) {
\r
806 return new LocaleIDParser(localeID).getBaseName();
\r
810 * {@icu} Returns the (normalized) full name for this locale.
\r
812 * @return String the full name of the localeID
\r
815 public String getName() {
\r
816 return localeID; // always normalized
\r
820 * {@icu} Returns the (normalized) full name for the specified locale.
\r
822 * @param localeID the localeID as a string
\r
823 * @return String the full name of the localeID
\r
826 public static String getName(String localeID){
\r
827 String name = nameCache.get(localeID);
\r
828 if (name == null) {
\r
829 name = new LocaleIDParser(localeID).getName();
\r
830 nameCache.put(localeID, name);
\r
836 * Returns a string representation of this object.
\r
839 public String toString() {
\r
844 * {@icu} Returns an iterator over keywords for this locale. If there
\r
845 * are no keywords, returns null.
\r
846 * @return iterator over keywords, or null if there are no keywords.
\r
849 public Iterator<String> getKeywords() {
\r
850 return getKeywords(localeID);
\r
854 * {@icu} Returns an iterator over keywords for the specified locale. If there
\r
855 * are no keywords, returns null.
\r
856 * @return an iterator over the keywords in the specified locale, or null
\r
857 * if there are no keywords.
\r
860 public static Iterator<String> getKeywords(String localeID){
\r
861 return new LocaleIDParser(localeID).getKeywords();
\r
865 * {@icu} Returns the value for a keyword in this locale. If the keyword is not
\r
866 * defined, returns null.
\r
867 * @param keywordName name of the keyword whose value is desired. Case insensitive.
\r
868 * @return the value of the keyword, or null.
\r
871 public String getKeywordValue(String keywordName){
\r
872 return getKeywordValue(localeID, keywordName);
\r
876 * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
\r
877 * not defined, returns null. The locale name does not need to be normalized.
\r
878 * @param keywordName name of the keyword whose value is desired. Case insensitive.
\r
879 * @return String the value of the keyword as a string
\r
882 public static String getKeywordValue(String localeID, String keywordName) {
\r
883 return new LocaleIDParser(localeID).getKeywordValue(keywordName);
\r
887 * {@icu} Returns the canonical name for the specified locale ID. This is used to
\r
888 * convert POSIX and other grandfathered IDs to standard ICU form.
\r
889 * @param localeID the locale id
\r
890 * @return the canonicalized id
\r
893 public static String canonicalize(String localeID){
\r
894 LocaleIDParser parser = new LocaleIDParser(localeID, true);
\r
895 String baseName = parser.getBaseName();
\r
896 boolean foundVariant = false;
\r
898 // formerly, we always set to en_US_POSIX if the basename was empty, but
\r
899 // now we require that the entire id be empty, so that "@foo=bar"
\r
900 // will pass through unchanged.
\r
901 // {dlf} I'd rather keep "" unchanged.
\r
902 if (localeID.equals("")) {
\r
904 // return "en_US_POSIX";
\r
907 // we have an ID in the form xx_Yyyy_ZZ_KKKKK
\r
909 initCANONICALIZE_MAP();
\r
911 /* convert the variants to appropriate ID */
\r
912 for (int i = 0; i < variantsToKeywords.length; i++) {
\r
913 String[] vals = variantsToKeywords[i];
\r
914 int idx = baseName.lastIndexOf("_" + vals[0]);
\r
916 foundVariant = true;
\r
918 baseName = baseName.substring(0, idx);
\r
919 if (baseName.endsWith("_")) {
\r
920 baseName = baseName.substring(0, --idx);
\r
922 parser.setBaseName(baseName);
\r
923 parser.defaultKeywordValue(vals[1], vals[2]);
\r
928 /* See if this is an already known locale */
\r
929 for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
\r
930 if (CANONICALIZE_MAP[i][0].equals(baseName)) {
\r
931 foundVariant = true;
\r
933 String[] vals = CANONICALIZE_MAP[i];
\r
934 parser.setBaseName(vals[1]);
\r
935 if (vals[2] != null) {
\r
936 parser.defaultKeywordValue(vals[2], vals[3]);
\r
942 /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
\r
943 if (!foundVariant) {
\r
944 if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
\r
945 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
\r
949 return parser.getName();
\r
953 * Given a keyword and a value, return a new locale with an updated
\r
954 * keyword and value. If keyword is null, this removes all keywords from the locale id.
\r
955 * Otherwise, if the value is null, this removes the value for this keyword from the
\r
956 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
\r
957 * The keyword and value must not be empty.
\r
958 * @param keyword the keyword to add/remove, or null to remove all keywords.
\r
959 * @param value the value to add/set, or null to remove this particular keyword.
\r
960 * @return the updated locale
\r
963 public ULocale setKeywordValue(String keyword, String value) {
\r
964 return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
\r
968 * Given a locale id, a keyword, and a value, return a new locale id with an updated
\r
969 * keyword and value. If keyword is null, this removes all keywords from the locale id.
\r
970 * Otherwise, if the value is null, this removes the value for this keyword from the
\r
971 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
\r
972 * The keyword and value must not be empty.
\r
973 * @param localeID the locale id to modify
\r
974 * @param keyword the keyword to add/remove, or null to remove all keywords.
\r
975 * @param value the value to add/set, or null to remove this particular keyword.
\r
976 * @return the updated locale id
\r
979 public static String setKeywordValue(String localeID, String keyword, String value) {
\r
980 LocaleIDParser parser = new LocaleIDParser(localeID);
\r
981 parser.setKeywordValue(keyword, value);
\r
982 return parser.getName();
\r
986 * Given a locale id, a keyword, and a value, return a new locale id with an updated
\r
987 * keyword and value, if the keyword does not already have a value. The keyword and
\r
988 * value must not be null or empty.
\r
989 * @param localeID the locale id to modify
\r
990 * @param keyword the keyword to add, if not already present
\r
991 * @param value the value to add, if not already present
\r
992 * @return the updated locale id
\r
994 /* private static String defaultKeywordValue(String localeID, String keyword, String value) {
\r
995 LocaleIDParser parser = new LocaleIDParser(localeID);
\r
996 parser.defaultKeywordValue(keyword, value);
\r
997 return parser.getName();
\r
1001 * Returns a three-letter abbreviation for this locale's language. If the locale
\r
1002 * doesn't specify a language, returns the empty string. Otherwise, returns
\r
1003 * a lowercase ISO 639-2/T language code.
\r
1004 * The ISO 639-2 language codes can be found on-line at
\r
1005 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
\r
1006 * @exception MissingResourceException Throws MissingResourceException if the
\r
1007 * three-letter language abbreviation is not available for this locale.
\r
1010 public String getISO3Language(){
\r
1011 return getISO3Language(localeID);
\r
1015 * Returns a three-letter abbreviation for this locale's language. If the locale
\r
1016 * doesn't specify a language, returns the empty string. Otherwise, returns
\r
1017 * a lowercase ISO 639-2/T language code.
\r
1018 * The ISO 639-2 language codes can be found on-line at
\r
1019 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
\r
1020 * @exception MissingResourceException Throws MissingResourceException if the
\r
1021 * three-letter language abbreviation is not available for this locale.
\r
1024 public static String getISO3Language(String localeID) {
\r
1025 return LocaleIDs.getISO3Language(getLanguage(localeID));
\r
1029 * Returns a three-letter abbreviation for this locale's country/region. If the locale
\r
1030 * doesn't specify a country, returns the empty string. Otherwise, returns
\r
1031 * an uppercase ISO 3166 3-letter country code.
\r
1032 * @exception MissingResourceException Throws MissingResourceException if the
\r
1033 * three-letter country abbreviation is not available for this locale.
\r
1036 public String getISO3Country() {
\r
1037 return getISO3Country(localeID);
\r
1041 * Returns a three-letter abbreviation for this locale's country/region. If the locale
\r
1042 * doesn't specify a country, returns the empty string. Otherwise, returns
\r
1043 * an uppercase ISO 3166 3-letter country code.
\r
1044 * @exception MissingResourceException Throws MissingResourceException if the
\r
1045 * three-letter country abbreviation is not available for this locale.
\r
1048 public static String getISO3Country(String localeID) {
\r
1049 return LocaleIDs.getISO3Country(getCountry(localeID));
\r
1055 * Returns this locale's language localized for display in the default locale.
\r
1056 * @return the localized language name.
\r
1059 public String getDisplayLanguage() {
\r
1060 return getDisplayLanguageInternal(this, getDefault(), false);
\r
1064 * {@icu} Returns this locale's language localized for display in the provided locale.
\r
1065 * @param displayLocale the locale in which to display the name.
\r
1066 * @return the localized language name.
\r
1069 public String getDisplayLanguage(ULocale displayLocale) {
\r
1070 return getDisplayLanguageInternal(this, displayLocale, false);
\r
1074 * Returns a locale's language localized for display in the provided locale.
\r
1075 * This is a cover for the ICU4C API.
\r
1076 * @param localeID the id of the locale whose language will be displayed
\r
1077 * @param displayLocaleID the id of the locale in which to display the name.
\r
1078 * @return the localized language name.
\r
1081 public static String getDisplayLanguage(String localeID, String displayLocaleID) {
\r
1082 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
\r
1087 * Returns a locale's language localized for display in the provided locale.
\r
1088 * This is a cover for the ICU4C API.
\r
1089 * @param localeID the id of the locale whose language will be displayed.
\r
1090 * @param displayLocale the locale in which to display the name.
\r
1091 * @return the localized language name.
\r
1094 public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
\r
1095 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
\r
1098 * {@icu} Returns this locale's language localized for display in the default locale.
\r
1099 * If a dialect name is present in the data, then it is returned.
\r
1100 * @return the localized language name.
\r
1102 * @provisional This API might change or be removed in a future release.
\r
1104 public String getDisplayLanguageWithDialect() {
\r
1105 return getDisplayLanguageInternal(this, getDefault(), true);
\r
1109 * {@icu} Returns this locale's language localized for display in the provided locale.
\r
1110 * If a dialect name is present in the data, then it is returned.
\r
1111 * @param displayLocale the locale in which to display the name.
\r
1112 * @return the localized language name.
\r
1114 * @provisional This API might change or be removed in a future release.
\r
1116 public String getDisplayLanguageWithDialect(ULocale displayLocale) {
\r
1117 return getDisplayLanguageInternal(this, displayLocale, true);
\r
1121 * {@icu} Returns a locale's language localized for display in the provided locale.
\r
1122 * If a dialect name is present in the data, then it is returned.
\r
1123 * This is a cover for the ICU4C API.
\r
1124 * @param localeID the id of the locale whose language will be displayed
\r
1125 * @param displayLocaleID the id of the locale in which to display the name.
\r
1126 * @return the localized language name.
\r
1128 * @provisional This API might change or be removed in a future release.
\r
1130 public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
\r
1131 return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
\r
1136 * {@icu} Returns a locale's language localized for display in the provided locale.
\r
1137 * If a dialect name is present in the data, then it is returned.
\r
1138 * This is a cover for the ICU4C API.
\r
1139 * @param localeID the id of the locale whose language will be displayed.
\r
1140 * @param displayLocale the locale in which to display the name.
\r
1141 * @return the localized language name.
\r
1143 * @provisional This API might change or be removed in a future release.
\r
1145 public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
\r
1146 return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
\r
1149 private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
\r
1150 boolean useDialect) {
\r
1151 String lang = useDialect ? locale.getBaseName() : locale.getLanguage();
\r
1152 return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang);
\r
1156 * {@icu} Returns this locale's script localized for display in the default locale.
\r
1157 * @return the localized script name.
\r
1160 public String getDisplayScript() {
\r
1161 return getDisplayScriptInternal(this, getDefault());
\r
1165 * {@icu} Returns this locale's script localized for display in the provided locale.
\r
1166 * @param displayLocale the locale in which to display the name.
\r
1167 * @return the localized script name.
\r
1170 public String getDisplayScript(ULocale displayLocale) {
\r
1171 return getDisplayScriptInternal(this, displayLocale);
\r
1175 * {@icu} Returns a locale's script localized for display in the provided locale.
\r
1176 * This is a cover for the ICU4C API.
\r
1177 * @param localeID the id of the locale whose script will be displayed
\r
1178 * @param displayLocaleID the id of the locale in which to display the name.
\r
1179 * @return the localized script name.
\r
1182 public static String getDisplayScript(String localeID, String displayLocaleID) {
\r
1183 return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
\r
1187 * {@icu} Returns a locale's script localized for display in the provided locale.
\r
1188 * @param localeID the id of the locale whose script will be displayed.
\r
1189 * @param displayLocale the locale in which to display the name.
\r
1190 * @return the localized script name.
\r
1193 public static String getDisplayScript(String localeID, ULocale displayLocale) {
\r
1194 return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
\r
1197 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
1198 private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
\r
1199 return LocaleDisplayNames.getInstance(displayLocale)
\r
1200 .scriptDisplayName(locale.getScript());
\r
1204 * Returns this locale's country localized for display in the default locale.
\r
1205 * @return the localized country name.
\r
1208 public String getDisplayCountry() {
\r
1209 return getDisplayCountryInternal(this, getDefault());
\r
1213 * Returns this locale's country localized for display in the provided locale.
\r
1214 * @param displayLocale the locale in which to display the name.
\r
1215 * @return the localized country name.
\r
1218 public String getDisplayCountry(ULocale displayLocale){
\r
1219 return getDisplayCountryInternal(this, displayLocale);
\r
1223 * Returns a locale's country localized for display in the provided locale.
\r
1224 * This is a cover for the ICU4C API.
\r
1225 * @param localeID the id of the locale whose country will be displayed
\r
1226 * @param displayLocaleID the id of the locale in which to display the name.
\r
1227 * @return the localized country name.
\r
1230 public static String getDisplayCountry(String localeID, String displayLocaleID) {
\r
1231 return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
\r
1235 * Returns a locale's country localized for display in the provided locale.
\r
1236 * This is a cover for the ICU4C API.
\r
1237 * @param localeID the id of the locale whose country will be displayed.
\r
1238 * @param displayLocale the locale in which to display the name.
\r
1239 * @return the localized country name.
\r
1242 public static String getDisplayCountry(String localeID, ULocale displayLocale) {
\r
1243 return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
\r
1246 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
1247 private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
\r
1248 return LocaleDisplayNames.getInstance(displayLocale)
\r
1249 .regionDisplayName(locale.getCountry());
\r
1253 * Returns this locale's variant localized for display in the default locale.
\r
1254 * @return the localized variant name.
\r
1257 public String getDisplayVariant() {
\r
1258 return getDisplayVariantInternal(this, getDefault());
\r
1262 * Returns this locale's variant localized for display in the provided locale.
\r
1263 * @param displayLocale the locale in which to display the name.
\r
1264 * @return the localized variant name.
\r
1267 public String getDisplayVariant(ULocale displayLocale) {
\r
1268 return getDisplayVariantInternal(this, displayLocale);
\r
1272 * Returns a locale's variant localized for display in the provided locale.
\r
1273 * This is a cover for the ICU4C API.
\r
1274 * @param localeID the id of the locale whose variant will be displayed
\r
1275 * @param displayLocaleID the id of the locale in which to display the name.
\r
1276 * @return the localized variant name.
\r
1279 public static String getDisplayVariant(String localeID, String displayLocaleID){
\r
1280 return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
\r
1284 * Returns a locale's variant localized for display in the provided locale.
\r
1285 * This is a cover for the ICU4C API.
\r
1286 * @param localeID the id of the locale whose variant will be displayed.
\r
1287 * @param displayLocale the locale in which to display the name.
\r
1288 * @return the localized variant name.
\r
1291 public static String getDisplayVariant(String localeID, ULocale displayLocale) {
\r
1292 return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
\r
1295 private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
\r
1296 return LocaleDisplayNames.getInstance(displayLocale)
\r
1297 .variantDisplayName(locale.getVariant());
\r
1301 * {@icu} Returns a keyword localized for display in the default locale.
\r
1302 * @param keyword the keyword to be displayed.
\r
1303 * @return the localized keyword name.
\r
1304 * @see #getKeywords()
\r
1307 public static String getDisplayKeyword(String keyword) {
\r
1308 return getDisplayKeywordInternal(keyword, getDefault());
\r
1312 * {@icu} Returns a keyword localized for display in the specified locale.
\r
1313 * @param keyword the keyword to be displayed.
\r
1314 * @param displayLocaleID the id of the locale in which to display the keyword.
\r
1315 * @return the localized keyword name.
\r
1316 * @see #getKeywords(String)
\r
1319 public static String getDisplayKeyword(String keyword, String displayLocaleID) {
\r
1320 return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
\r
1324 * {@icu} Returns a keyword localized for display in the specified locale.
\r
1325 * @param keyword the keyword to be displayed.
\r
1326 * @param displayLocale the locale in which to display the keyword.
\r
1327 * @return the localized keyword name.
\r
1328 * @see #getKeywords(String)
\r
1331 public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
\r
1332 return getDisplayKeywordInternal(keyword, displayLocale);
\r
1335 private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
\r
1336 return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword);
\r
1340 * {@icu} Returns a keyword value localized for display in the default locale.
\r
1341 * @param keyword the keyword whose value is to be displayed.
\r
1342 * @return the localized value name.
\r
1345 public String getDisplayKeywordValue(String keyword) {
\r
1346 return getDisplayKeywordValueInternal(this, keyword, getDefault());
\r
1350 * {@icu} Returns a keyword value localized for display in the specified locale.
\r
1351 * @param keyword the keyword whose value is to be displayed.
\r
1352 * @param displayLocale the locale in which to display the value.
\r
1353 * @return the localized value name.
\r
1356 public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
\r
1357 return getDisplayKeywordValueInternal(this, keyword, displayLocale);
\r
1361 * {@icu} Returns a keyword value localized for display in the specified locale.
\r
1362 * This is a cover for the ICU4C API.
\r
1363 * @param localeID the id of the locale whose keyword value is to be displayed.
\r
1364 * @param keyword the keyword whose value is to be displayed.
\r
1365 * @param displayLocaleID the id of the locale in which to display the value.
\r
1366 * @return the localized value name.
\r
1369 public static String getDisplayKeywordValue(String localeID, String keyword,
\r
1370 String displayLocaleID) {
\r
1371 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
\r
1372 new ULocale(displayLocaleID));
\r
1376 * {@icu} Returns a keyword value localized for display in the specified locale.
\r
1377 * This is a cover for the ICU4C API.
\r
1378 * @param localeID the id of the locale whose keyword value is to be displayed.
\r
1379 * @param keyword the keyword whose value is to be displayed.
\r
1380 * @param displayLocale the id of the locale in which to display the value.
\r
1381 * @return the localized value name.
\r
1384 public static String getDisplayKeywordValue(String localeID, String keyword,
\r
1385 ULocale displayLocale) {
\r
1386 return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
\r
1389 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
1390 private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
\r
1391 ULocale displayLocale) {
\r
1392 keyword = AsciiUtil.toLowerString(keyword.trim());
\r
1393 String value = locale.getKeywordValue(keyword);
\r
1394 return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value);
\r
1398 * Returns this locale name localized for display in the default locale.
\r
1399 * @return the localized locale name.
\r
1402 public String getDisplayName() {
\r
1403 return getDisplayNameInternal(this, getDefault());
\r
1407 * Returns this locale name localized for display in the provided locale.
\r
1408 * @param displayLocale the locale in which to display the locale name.
\r
1409 * @return the localized locale name.
\r
1412 public String getDisplayName(ULocale displayLocale) {
\r
1413 return getDisplayNameInternal(this, displayLocale);
\r
1417 * Returns the locale ID localized for display in the provided locale.
\r
1418 * This is a cover for the ICU4C API.
\r
1419 * @param localeID the locale whose name is to be displayed.
\r
1420 * @param displayLocaleID the id of the locale in which to display the locale name.
\r
1421 * @return the localized locale name.
\r
1424 public static String getDisplayName(String localeID, String displayLocaleID) {
\r
1425 return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
\r
1429 * Returns the locale ID localized for display in the provided locale.
\r
1430 * This is a cover for the ICU4C API.
\r
1431 * @param localeID the locale whose name is to be displayed.
\r
1432 * @param displayLocale the locale in which to display the locale name.
\r
1433 * @return the localized locale name.
\r
1436 public static String getDisplayName(String localeID, ULocale displayLocale) {
\r
1437 return getDisplayNameInternal(new ULocale(localeID), displayLocale);
\r
1440 private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
\r
1441 return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale);
\r
1445 * {@icu} Returns this locale name localized for display in the default locale.
\r
1446 * If a dialect name is present in the locale data, then it is returned.
\r
1447 * @return the localized locale name.
\r
1449 * @provisional This API might change or be removed in a future release.
\r
1451 public String getDisplayNameWithDialect() {
\r
1452 return getDisplayNameWithDialectInternal(this, getDefault());
\r
1456 * {@icu} Returns this locale name localized for display in the provided locale.
\r
1457 * If a dialect name is present in the locale data, then it is returned.
\r
1458 * @param displayLocale the locale in which to display the locale name.
\r
1459 * @return the localized locale name.
\r
1461 * @provisional This API might change or be removed in a future release.
\r
1463 public String getDisplayNameWithDialect(ULocale displayLocale) {
\r
1464 return getDisplayNameWithDialectInternal(this, displayLocale);
\r
1468 * {@icu} Returns the locale ID localized for display in the provided locale.
\r
1469 * If a dialect name is present in the locale data, then it is returned.
\r
1470 * This is a cover for the ICU4C API.
\r
1471 * @param localeID the locale whose name is to be displayed.
\r
1472 * @param displayLocaleID the id of the locale in which to display the locale name.
\r
1473 * @return the localized locale name.
\r
1475 * @provisional This API might change or be removed in a future release.
\r
1477 public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
\r
1478 return getDisplayNameWithDialectInternal(new ULocale(localeID),
\r
1479 new ULocale(displayLocaleID));
\r
1483 * {@icu} Returns the locale ID localized for display in the provided locale.
\r
1484 * If a dialect name is present in the locale data, then it is returned.
\r
1485 * This is a cover for the ICU4C API.
\r
1486 * @param localeID the locale whose name is to be displayed.
\r
1487 * @param displayLocale the locale in which to display the locale name.
\r
1488 * @return the localized locale name.
\r
1490 * @provisional This API might change or be removed in a future release.
\r
1492 public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
\r
1493 return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
\r
1496 private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
\r
1497 return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES)
\r
1498 .localeDisplayName(locale);
\r
1502 * {@icu} Returns this locale's layout orientation for characters. The possible
\r
1503 * values are "left-to-right", "right-to-left", "top-to-bottom" or
\r
1504 * "bottom-to-top".
\r
1505 * @return The locale's layout orientation for characters.
\r
1508 public String getCharacterOrientation() {
\r
1509 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
\r
1510 "layout", "characters");
\r
1514 * {@icu} Returns this locale's layout orientation for lines. The possible
\r
1515 * values are "left-to-right", "right-to-left", "top-to-bottom" or
\r
1516 * "bottom-to-top".
\r
1517 * @return The locale's layout orientation for lines.
\r
1520 public String getLineOrientation() {
\r
1521 return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
\r
1522 "layout", "lines");
\r
1526 * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
\r
1527 * resource containing the data. This is always at or above the
\r
1528 * valid locale. If the valid locale does not contain the
\r
1529 * specific data being requested, then the actual locale will be
\r
1530 * above the valid locale. If the object was not constructed from
\r
1531 * locale data, then the valid locale is <i>null</i>.
\r
1533 * @draft ICU 2.8 (retain)
\r
1534 * @provisional This API might change or be removed in a future release.
\r
1536 public static Type ACTUAL_LOCALE = new Type();
\r
1539 * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
\r
1540 * locale for which any data exists. This is always at or above
\r
1541 * the requested locale, and at or below the actual locale. If
\r
1542 * the requested locale does not correspond to any resource data,
\r
1543 * then the valid locale will be above the requested locale. If
\r
1544 * the object was not constructed from locale data, then the
\r
1545 * actual locale is <i>null</i>.
\r
1547 * <p>Note: The valid locale will be returned correctly in ICU
\r
1548 * 3.0 or later. In ICU 2.8, it is not returned correctly.
\r
1549 * @draft ICU 2.8 (retain)
\r
1550 * @provisional This API might change or be removed in a future release.
\r
1552 public static Type VALID_LOCALE = new Type();
\r
1555 * Opaque selector enum for <tt>getLocale()</tt>.
\r
1556 * @see com.ibm.icu.util.ULocale
\r
1557 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
\r
1558 * @see com.ibm.icu.util.ULocale#VALID_LOCALE
\r
1559 * @draft ICU 2.8 (retainAll)
\r
1560 * @provisional This API might change or be removed in a future release.
\r
1562 public static final class Type {
\r
1567 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
\r
1568 * locale for the user. NullPointerException is thrown if acceptLanguageList or
\r
1569 * availableLocales is null. If fallback is non-null, it will contain true if a
\r
1570 * fallback locale (one not in the acceptLanguageList) was returned. The value on
\r
1571 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
\r
1572 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
\r
1573 * availableLocales matched). No ULocale array element should be null; behavior is
\r
1574 * undefined if this is the case.
\r
1575 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
\r
1576 * @param availableLocales list of available locales. One of these will be returned.
\r
1577 * @param fallback if non-null, a 1-element array containing a boolean to be set with
\r
1578 * the fallback status
\r
1579 * @return one of the locales from the availableLocales list, or null if none match
\r
1582 public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
\r
1583 boolean[] fallback) {
\r
1584 if (acceptLanguageList == null) {
\r
1585 throw new NullPointerException();
\r
1587 ULocale acceptList[] = null;
\r
1589 acceptList = parseAcceptLanguage(acceptLanguageList, true);
\r
1590 } catch (ParseException pe) {
\r
1591 acceptList = null;
\r
1593 if (acceptList == null) {
\r
1596 return acceptLanguage(acceptList, availableLocales, fallback);
\r
1600 * {@icu} Based on a list of acceptable locales, determine an available locale for the
\r
1601 * user. NullPointerException is thrown if acceptLanguageList or availableLocales is
\r
1602 * null. If fallback is non-null, it will contain true if a fallback locale (one not
\r
1603 * in the acceptLanguageList) was returned. The value on entry is ignored. ULocale
\r
1604 * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
\r
1605 * locale was used as a fallback (because nothing else in availableLocales matched).
\r
1606 * No ULocale array element should be null; behavior is undefined if this is the case.
\r
1607 * @param acceptLanguageList list of acceptable locales
\r
1608 * @param availableLocales list of available locales. One of these will be returned.
\r
1609 * @param fallback if non-null, a 1-element array containing a boolean to be set with
\r
1610 * the fallback status
\r
1611 * @return one of the locales from the availableLocales list, or null if none match
\r
1615 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
\r
1616 availableLocales, boolean[] fallback) {
\r
1619 if(fallback != null) {
\r
1622 for(i=0;i<acceptLanguageList.length;i++) {
\r
1623 ULocale aLocale = acceptLanguageList[i];
\r
1624 boolean[] setFallback = fallback;
\r
1626 for(j=0;j<availableLocales.length;j++) {
\r
1627 if(availableLocales[j].equals(aLocale)) {
\r
1628 if(setFallback != null) {
\r
1629 setFallback[0]=false; // first time with this locale - not a fallback.
\r
1631 return availableLocales[j];
\r
1634 Locale loc = aLocale.toLocale();
\r
1635 Locale parent = LocaleUtility.fallback(loc);
\r
1636 if(parent != null) {
\r
1637 aLocale = new ULocale(parent);
\r
1641 setFallback = null; // Do not set fallback in later iterations
\r
1642 } while (aLocale != null);
\r
1648 * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
\r
1649 * locale for the user. NullPointerException is thrown if acceptLanguageList or
\r
1650 * availableLocales is null. If fallback is non-null, it will contain true if a
\r
1651 * fallback locale (one not in the acceptLanguageList) was returned. The value on
\r
1652 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
\r
1653 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
\r
1654 * availableLocales matched). No ULocale array element should be null; behavior is
\r
1655 * undefined if this is the case. This function will choose a locale from the
\r
1656 * ULocale.getAvailableLocales() list as available.
\r
1657 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
\r
1658 * @param fallback if non-null, a 1-element array containing a boolean to be set with
\r
1659 * the fallback status
\r
1660 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
\r
1664 public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
\r
1665 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
\r
1670 * {@icu} Based on an ordered array of acceptable locales, determine an available
\r
1671 * locale for the user. NullPointerException is thrown if acceptLanguageList or
\r
1672 * availableLocales is null. If fallback is non-null, it will contain true if a
\r
1673 * fallback locale (one not in the acceptLanguageList) was returned. The value on
\r
1674 * entry is ignored. ULocale will be one of the locales in availableLocales, or the
\r
1675 * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
\r
1676 * availableLocales matched). No ULocale array element should be null; behavior is
\r
1677 * undefined if this is the case. This function will choose a locale from the
\r
1678 * ULocale.getAvailableLocales() list as available.
\r
1679 * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
\r
1680 * @param fallback if non-null, a 1-element array containing a boolean to be set with
\r
1681 * the fallback status
\r
1682 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
\r
1685 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
\r
1686 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
\r
1691 * Package local method used for parsing Accept-Language string
\r
1693 static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
\r
1694 throws ParseException {
\r
1695 class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
\r
1697 private double serial;
\r
1698 public ULocaleAcceptLanguageQ(double theq, int theserial) {
\r
1700 serial = theserial;
\r
1702 public int compareTo(ULocaleAcceptLanguageQ other) {
\r
1703 if (q > other.q) { // reverse - to sort in descending order
\r
1705 } else if (q < other.q) {
\r
1708 if (serial < other.serial) {
\r
1710 } else if (serial > other.serial) {
\r
1713 return 0; // same object
\r
1718 // parse out the acceptLanguage into an array
\r
1719 TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
\r
1720 new TreeMap<ULocaleAcceptLanguageQ, ULocale>();
\r
1721 StringBuilder languageRangeBuf = new StringBuilder();
\r
1722 StringBuilder qvalBuf = new StringBuilder();
\r
1724 acceptLanguage += ","; // append comma to simplify the parsing code
\r
1726 boolean subTag = false;
\r
1727 boolean q1 = false;
\r
1728 for (n = 0; n < acceptLanguage.length(); n++) {
\r
1729 boolean gotLanguageQ = false;
\r
1730 char c = acceptLanguage.charAt(n);
\r
1732 case 0: // before language-range start
\r
1733 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
\r
1734 // in language-range
\r
1735 languageRangeBuf.append(c);
\r
1738 } else if (c == '*') {
\r
1739 languageRangeBuf.append(c);
\r
1741 } else if (c != ' ' && c != '\t') {
\r
1742 // invalid character
\r
1746 case 1: // in language-range
\r
1747 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
\r
1748 languageRangeBuf.append(c);
\r
1749 } else if (c == '-') {
\r
1751 languageRangeBuf.append(c);
\r
1752 } else if (c == '_') {
\r
1755 languageRangeBuf.append(c);
\r
1759 } else if ('0' <= c && c <= '9') {
\r
1761 languageRangeBuf.append(c);
\r
1763 // DIGIT is allowed only in language sub tag
\r
1766 } else if (c == ',') {
\r
1768 gotLanguageQ = true;
\r
1769 } else if (c == ' ' || c == '\t') {
\r
1770 // language-range end
\r
1772 } else if (c == ';') {
\r
1776 // invalid character for language-range
\r
1780 case 2: // saw wild card range
\r
1783 gotLanguageQ = true;
\r
1784 } else if (c == ' ' || c == '\t') {
\r
1785 // language-range end
\r
1787 } else if (c == ';') {
\r
1795 case 3: // language-range end
\r
1798 gotLanguageQ = true;
\r
1799 } else if (c == ';') {
\r
1802 } else if (c != ' ' && c != '\t') {
\r
1807 case 4: // before q
\r
1811 } else if (c != ' ' && c != '\t') {
\r
1816 case 5: // before equal
\r
1820 } else if (c != ' ' && c != '\t') {
\r
1825 case 6: // before q value
\r
1827 // q value start with 0
\r
1829 qvalBuf.append(c);
\r
1831 } else if (c == '1') {
\r
1832 // q value start with 1
\r
1833 qvalBuf.append(c);
\r
1835 } else if (c == '.') {
\r
1837 qvalBuf.append(c);
\r
1842 } else if (c != ' ' && c != '\t') {
\r
1847 case 7: // q value start
\r
1849 // before q value fraction part
\r
1850 qvalBuf.append(c);
\r
1852 } else if (c == ',') {
\r
1854 gotLanguageQ = true;
\r
1855 } else if (c == ' ' || c == '\t') {
\r
1863 case 8: // before q value fraction part
\r
1864 if ('0' <= c || c <= '9') {
\r
1865 if (q1 && c != '0' && !isLenient) {
\r
1866 // if q value starts with 1, the fraction part must be 0
\r
1869 // in q value fraction part
\r
1870 qvalBuf.append(c);
\r
1878 case 9: // in q value fraction part
\r
1879 if ('0' <= c && c <= '9') {
\r
1880 if (q1 && c != '0') {
\r
1881 // if q value starts with 1, the fraction part must be 0
\r
1884 qvalBuf.append(c);
\r
1886 } else if (c == ',') {
\r
1888 gotLanguageQ = true;
\r
1889 } else if (c == ' ' || c == '\t') {
\r
1897 case 10: // after q value
\r
1900 gotLanguageQ = true;
\r
1901 } else if (c != ' ' && c != '\t') {
\r
1907 if (state == -1) {
\r
1909 throw new ParseException("Invalid Accept-Language", n);
\r
1911 if (gotLanguageQ) {
\r
1913 if (qvalBuf.length() != 0) {
\r
1915 q = Double.parseDouble(qvalBuf.toString());
\r
1916 } catch (NumberFormatException nfe) {
\r
1917 // Already validated, so it should never happen
\r
1924 if (languageRangeBuf.charAt(0) != '*') {
\r
1925 int serial = map.size();
\r
1926 ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
\r
1927 // sort in reverse order.. 1.0, 0.9, 0.8 .. etc
\r
1928 map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
\r
1931 // reset buffer and parse state
\r
1932 languageRangeBuf.setLength(0);
\r
1933 qvalBuf.setLength(0);
\r
1938 // Well, the parser should handle all cases. So just in case.
\r
1939 throw new ParseException("Invalid AcceptlLanguage", n);
\r
1942 // pull out the map
\r
1943 ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
\r
1944 return acceptList;
\r
1947 private static final String UNDEFINED_LANGUAGE = "und";
\r
1948 private static final String UNDEFINED_SCRIPT = "Zzzz";
\r
1949 private static final String UNDEFINED_REGION = "ZZ";
\r
1952 * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
\r
1953 * described in the following CLDR technical report:
\r
1955 * http://www.unicode.org/reports/tr35/#Likely_Subtags
\r
1957 * If the provided ULocale instance is already in the maximal form, or there is no
\r
1958 * data available available for maximization, it will be returned. For example,
\r
1959 * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
\r
1960 * Otherwise, a new ULocale instance with the maximal form is returned.
\r
1964 * "en" maximizes to "en_Latn_US"
\r
1966 * "de" maximizes to "de_Latn_US"
\r
1968 * "sr" maximizes to "sr_Cyrl_RS"
\r
1970 * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
\r
1972 * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
\r
1974 * @param loc The ULocale to maximize
\r
1975 * @return The maximized ULocale instance.
\r
1978 public static ULocale addLikelySubtags(ULocale loc) {
\r
1979 String[] tags = new String[3];
\r
1980 String trailing = null;
\r
1982 int trailingIndex = parseTagString(
\r
1986 if (trailingIndex < loc.localeID.length()) {
\r
1987 trailing = loc.localeID.substring(trailingIndex);
\r
1990 String newLocaleID =
\r
1991 createLikelySubtagsString(
\r
1997 return newLocaleID == null ? loc : new ULocale(newLocaleID);
\r
2001 * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
\r
2002 * in the following CLDR technical report:<blockquote>
\r
2004 * <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
\r
2005 *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
\r
2007 * If the provided ULocale instance is already in the minimal form, or there
\r
2008 * is no data available for minimization, it will be returned. Since the
\r
2009 * minimization algorithm relies on proper maximization, see the comments
\r
2010 * for addLikelySubtags for reasons why there might not be any data.
\r
2014 * "en_Latn_US" minimizes to "en"
\r
2016 * "de_Latn_US" minimizes to "de"
\r
2018 * "sr_Cyrl_RS" minimizes to "sr"
\r
2020 * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
\r
2021 * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
\r
2023 * @param loc The ULocale to minimize
\r
2024 * @return The minimized ULocale instance.
\r
2027 public static ULocale minimizeSubtags(ULocale loc) {
\r
2028 String[] tags = new String[3];
\r
2030 int trailingIndex = parseTagString(
\r
2034 String originalLang = tags[0];
\r
2035 String originalScript = tags[1];
\r
2036 String originalRegion = tags[2];
\r
2037 String originalTrailing = null;
\r
2039 if (trailingIndex < loc.localeID.length()) {
\r
2041 * Create a String that contains everything
\r
2042 * after the language, script, and region.
\r
2044 originalTrailing = loc.localeID.substring(trailingIndex);
\r
2048 * First, we need to first get the maximization
\r
2049 * by adding any likely subtags.
\r
2051 String maximizedLocaleID =
\r
2052 createLikelySubtagsString(
\r
2059 * If maximization fails, there's nothing
\r
2062 if (isEmptyString(maximizedLocaleID)) {
\r
2067 * Start first with just the language.
\r
2070 createLikelySubtagsString(
\r
2076 if (tag.equals(maximizedLocaleID)) {
\r
2077 String newLocaleID =
\r
2082 originalTrailing);
\r
2084 return new ULocale(newLocaleID);
\r
2089 * Next, try the language and region.
\r
2091 if (originalRegion.length() != 0) {
\r
2094 createLikelySubtagsString(
\r
2100 if (tag.equals(maximizedLocaleID)) {
\r
2101 String newLocaleID =
\r
2106 originalTrailing);
\r
2108 return new ULocale(newLocaleID);
\r
2113 * Finally, try the language and script. This is our last chance,
\r
2114 * since trying with all three subtags would only yield the
\r
2115 * maximal version that we already have.
\r
2117 if (originalRegion.length() != 0 &&
\r
2118 originalScript.length() != 0) {
\r
2121 createLikelySubtagsString(
\r
2127 if (tag.equals(maximizedLocaleID)) {
\r
2128 String newLocaleID =
\r
2133 originalTrailing);
\r
2135 return new ULocale(newLocaleID);
\r
2143 * A trivial utility function that checks for a null
\r
2144 * reference or checks the length of the supplied String.
\r
2146 * @param string The string to check
\r
2148 * @return true if the String is empty, or if the reference is null.
\r
2150 private static boolean isEmptyString(String string) {
\r
2151 return string == null || string.length() == 0;
\r
2155 * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
\r
2156 * not be a zero-length string.
\r
2158 * @param tag The tag to add.
\r
2159 * @param buffer The output buffer.
\r
2161 private static void appendTag(String tag, StringBuilder buffer) {
\r
2162 if (buffer.length() != 0) {
\r
2163 buffer.append(UNDERSCORE);
\r
2166 buffer.append(tag);
\r
2170 * Create a tag string from the supplied parameters. The lang, script and region
\r
2171 * parameters may be null references.
\r
2173 * If any of the language, script or region parameters are empty, and the alternateTags
\r
2174 * parameter is not null, it will be parsed for potential language, script and region tags
\r
2175 * to be used when constructing the new tag. If the alternateTags parameter is null, or
\r
2176 * it contains no language tag, the default tag for the unknown language is used.
\r
2178 * @param lang The language tag to use.
\r
2179 * @param script The script tag to use.
\r
2180 * @param region The region tag to use.
\r
2181 * @param trailing Any trailing data to append to the new tag.
\r
2182 * @param alternateTags A string containing any alternate tags.
\r
2183 * @return The new tag string.
\r
2185 private static String createTagString(String lang, String script, String region,
\r
2186 String trailing, String alternateTags) {
\r
2188 LocaleIDParser parser = null;
\r
2189 boolean regionAppended = false;
\r
2191 StringBuilder tag = new StringBuilder();
\r
2193 if (!isEmptyString(lang)) {
\r
2198 else if (isEmptyString(alternateTags)) {
\r
2200 * Append the value for an unknown language, if
\r
2201 * we found no language.
\r
2204 UNDEFINED_LANGUAGE,
\r
2208 parser = new LocaleIDParser(alternateTags);
\r
2210 String alternateLang = parser.getLanguage();
\r
2213 * Append the value for an unknown language, if
\r
2214 * we found no language.
\r
2217 !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
\r
2221 if (!isEmptyString(script)) {
\r
2226 else if (!isEmptyString(alternateTags)) {
\r
2228 * Parse the alternateTags string for the script.
\r
2230 if (parser == null) {
\r
2231 parser = new LocaleIDParser(alternateTags);
\r
2234 String alternateScript = parser.getScript();
\r
2236 if (!isEmptyString(alternateScript)) {
\r
2243 if (!isEmptyString(region)) {
\r
2248 regionAppended = true;
\r
2250 else if (!isEmptyString(alternateTags)) {
\r
2252 * Parse the alternateTags string for the region.
\r
2254 if (parser == null) {
\r
2255 parser = new LocaleIDParser(alternateTags);
\r
2258 String alternateRegion = parser.getCountry();
\r
2260 if (!isEmptyString(alternateRegion)) {
\r
2265 regionAppended = true;
\r
2269 if (trailing != null && trailing.length() > 1) {
\r
2271 * The current ICU format expects two underscores
\r
2272 * will separate the variant from the preceeding
\r
2273 * parts of the tag, if there is no region.
\r
2275 int separators = 0;
\r
2277 if (trailing.charAt(0) == UNDERSCORE) {
\r
2278 if (trailing.charAt(1) == UNDERSCORE) {
\r
2286 if (regionAppended) {
\r
2288 * If we appended a region, we may need to strip
\r
2289 * the extra separator from the variant portion.
\r
2291 if (separators == 2) {
\r
2292 tag.append(trailing.substring(1));
\r
2295 tag.append(trailing);
\r
2300 * If we did not append a region, we may need to add
\r
2301 * an extra separator to the variant portion.
\r
2303 if (separators == 1) {
\r
2304 tag.append(UNDERSCORE);
\r
2306 tag.append(trailing);
\r
2310 return tag.toString();
\r
2314 * Create a tag string from the supplied parameters. The lang, script and region
\r
2315 * parameters may be null references.If the lang parameter is an empty string, the
\r
2316 * default value for an unknown language is written to the output buffer.
\r
2318 * @param lang The language tag to use.
\r
2319 * @param script The script tag to use.
\r
2320 * @param region The region tag to use.
\r
2321 * @param trailing Any trailing data to append to the new tag.
\r
2322 * @return The new String.
\r
2324 static String createTagString(String lang, String script, String region, String trailing) {
\r
2325 return createTagString(lang, script, region, trailing, null);
\r
2329 * Parse the language, script, and region subtags from a tag string, and return the results.
\r
2331 * This function does not return the canonical strings for the unknown script and region.
\r
2333 * @param localeID The locale ID to parse.
\r
2334 * @param tags An array of three String references to return the subtag strings.
\r
2335 * @return The number of chars of the localeID parameter consumed.
\r
2337 private static int parseTagString(String localeID, String tags[]) {
\r
2338 LocaleIDParser parser = new LocaleIDParser(localeID);
\r
2340 String lang = parser.getLanguage();
\r
2341 String script = parser.getScript();
\r
2342 String region = parser.getCountry();
\r
2344 if (isEmptyString(lang)) {
\r
2345 tags[0] = UNDEFINED_LANGUAGE;
\r
2351 if (script.equals(UNDEFINED_SCRIPT)) {
\r
2358 if (region.equals(UNDEFINED_REGION)) {
\r
2366 * Search for the variant. If there is one, then return the index of
\r
2367 * the preceeding separator.
\r
2368 * If there's no variant, search for the keyword delimiter,
\r
2369 * and return its index. Otherwise, return the length of the
\r
2372 * $TOTO(dbertoni) we need to take into account that we might
\r
2373 * find a part of the language as the variant, since it can
\r
2374 * can have a variant portion that is long enough to contain
\r
2375 * the same characters as the variant.
\r
2377 String variant = parser.getVariant();
\r
2379 if (!isEmptyString(variant)){
\r
2380 int index = localeID.indexOf(variant);
\r
2383 return index > 0 ? index - 1 : index;
\r
2387 int index = localeID.indexOf('@');
\r
2389 return index == -1 ? localeID.length() : index;
\r
2393 private static String lookupLikelySubtags(String localeId) {
\r
2394 UResourceBundle bundle =
\r
2395 UResourceBundle.getBundleInstance(
\r
2396 ICUResourceBundle.ICU_BASE_NAME, "likelySubtags");
\r
2398 return bundle.getString(localeId);
\r
2400 catch(MissingResourceException e) {
\r
2405 private static String createLikelySubtagsString(String lang, String script, String region,
\r
2406 String variants) {
\r
2409 * Try the language with the script and region first.
\r
2411 if (!isEmptyString(script) && !isEmptyString(region)) {
\r
2413 String searchTag =
\r
2420 String likelySubtags = lookupLikelySubtags(searchTag);
\r
2423 if (likelySubtags == null) {
\r
2424 if (likelySubtags2 != null) {
\r
2425 System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
\r
2428 else if (likelySubtags2 == null) {
\r
2429 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
\r
2431 else if (!likelySubtags.equals(likelySubtags2)) {
\r
2432 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2
\r
2436 if (likelySubtags != null) {
\r
2437 // Always use the language tag from the
\r
2438 // maximal string, since it may be more
\r
2439 // specific than the one provided.
\r
2440 return createTagString(
\r
2450 * Try the language with just the script.
\r
2452 if (!isEmptyString(script)) {
\r
2454 String searchTag =
\r
2461 String likelySubtags = lookupLikelySubtags(searchTag);
\r
2462 if (likelySubtags != null) {
\r
2463 // Always use the language tag from the
\r
2464 // maximal string, since it may be more
\r
2465 // specific than the one provided.
\r
2466 return createTagString(
\r
2476 * Try the language with just the region.
\r
2478 if (!isEmptyString(region)) {
\r
2480 String searchTag =
\r
2487 String likelySubtags = lookupLikelySubtags(searchTag);
\r
2489 if (likelySubtags != null) {
\r
2490 // Always use the language tag from the
\r
2491 // maximal string, since it may be more
\r
2492 // specific than the one provided.
\r
2493 return createTagString(
\r
2503 * Finally, try just the language.
\r
2506 String searchTag =
\r
2513 String likelySubtags = lookupLikelySubtags(searchTag);
\r
2515 if (likelySubtags != null) {
\r
2516 // Always use the language tag from the
\r
2517 // maximal string, since it may be more
\r
2518 // specific than the one provided.
\r
2519 return createTagString(
\r
2531 // --------------------------------
\r
2532 // BCP47/OpenJDK APIs
\r
2533 // --------------------------------
\r
2536 * {@icu} The key for the private use locale extension ('x').
\r
2538 * @see #getExtension(char)
\r
2539 * @see Builder#setExtension(char, String)
\r
2542 * @provisional This API might change or be removed in a future release.
\r
2544 public static final char PRIVATE_USE_EXTENSION = 'x';
\r
2547 * {@icu} The key for Unicode locale extension ('u').
\r
2549 * @see #getExtension(char)
\r
2550 * @see Builder#setExtension(char, String)
\r
2553 * @provisional This API might change or be removed in a future release.
\r
2555 public static final char UNICODE_LOCALE_EXTENSION = 'u';
\r
2558 * {@icu} Returns the extension (or private use) value associated with
\r
2559 * the specified singleton key, or null if there is no extension
\r
2560 * associated with the key. To be valid, the key must be one
\r
2561 * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
\r
2562 * for example 'z' and 'Z' represent the same extension.
\r
2564 * @param key the extension key
\r
2565 * @return the extension, or null if this locale defines no
\r
2566 * extension for the specified key
\r
2567 * @throws IllegalArgumentException if the key is not valid
\r
2568 * @see #PRIVATE_USE_EXTENSION
\r
2569 * @see #UNICODE_LOCALE_EXTENSION
\r
2572 * @provisional This API might change or be removed in a future release.
\r
2574 public String getExtension(char key) {
\r
2575 String strKey = String.valueOf(key);
\r
2576 if (!LocaleExtensions.isValidKey(strKey)) {
\r
2577 throw new IllegalArgumentException("Invalid extension key: " + strKey);
\r
2579 return extensions().getExtensionValue(key);
\r
2583 * {@icu} Returns the set of extension keys associated with this locale, or the
\r
2584 * empty set if it has no extensions. The returned set is unmodifiable.
\r
2586 * @return the set of extension keys, or the empty set if this locale has
\r
2590 * @provisional This API might change or be removed in a future release.
\r
2592 public Set<Character> getExtensionKeys() {
\r
2593 return extensions().getKeys();
\r
2597 * {@icu} Returns the Unicode locale type associated with the specified Unicode
\r
2598 * locale key for this locale. Unicode locale keywrods are specified
\r
2599 * by the 'u' extension and consist of key/type pairs. The key must be
\r
2600 * two alphanumeric characters in length, or an IllegalArgumentException
\r
2602 * @param key the Unicode locale key
\r
2603 * @return the Unicode locale type associated with the key, or null if the
\r
2604 * locale does not define a value for the key.
\r
2605 * @throws IllegalArgumentException if the key is not valid.
\r
2608 * @provisional This API might change or be removed in a future release.
\r
2610 public String getUnicodeLocaleType(String key) {
\r
2611 if (!LocaleExtensions.isValidKey(key)) {
\r
2612 throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
\r
2614 return extensions().getUnicodeLocaleType(key);
\r
2618 * {@icu} Returns the set of keys for Unicode locale keywords defined by this locale,
\r
2619 * or null if this locale has no locale extension. The returned set is
\r
2622 * @return the set of the Unicode locale keys, or null
\r
2625 * @provisional This API might change or be removed in a future release.
\r
2627 public Set<String> getUnicodeLocaleKeys() {
\r
2628 return extensions().getUnicodeLocaleKeys();
\r
2632 * {@icu} Returns a well-formed IETF BCP 47 language tag representing
\r
2636 * If this <code>ULocale</code> object has language, country, or variant
\r
2637 * that does not satisfy the IETF BCP 47 language tag syntax requirements,
\r
2638 * this method handles these fields as described below:
\r
2640 * <b>Language:</b> If language is empty or ill-formed (for example "a" or "e2"),
\r
2641 * it will be emitted as "und" (Undetermined).
\r
2643 * <b>Country:</b> If country is ill-formed (for example "12" or "USA"), it
\r
2644 * will be omitted.
\r
2646 * <b>Variant:</b> Variant is treated as consisting of subtags separated by
\r
2647 * underscore and converted to lower case letters. 'Well-formed' subtags
\r
2648 * consist of either an ASCII letter followed by 4-7 ASCII characters, or an
\r
2649 * ASCII digit followed by 3-7 ASCII characters. If well-formed, the variant
\r
2650 * is emitted as each subtag in order (separated by hyphen). Otherwise:
\r
2652 * <li>if all sub-segments consist of 1 to 8 ASCII alphanumerics (for example
\r
2653 * "WIN", "WINDOWS_XP", "SOLARIS_10"), the first ill-formed variant subtag
\r
2654 * and all following sub-segments will be emitted as private use subtags prefixed
\r
2655 * by the special private use subtag "variant" followed by each subtag in order
\r
2656 * (separated by hyphen). For example, locale "en_US_WIN" is converted to language
\r
2657 * tag "en-US-x-variant-win", locale "de_WINDOWS_XP" is converted to language tag
\r
2658 * "de-windows-x-variant-xp". If this locale has a private use extension value,
\r
2659 * the special private use subtags prefixed by "variant" are appended after the
\r
2660 * locale's private use value.
\r
2661 * <li>if any subtag does not consist of 1 to 8 ASCII alphanumerics, the
\r
2662 * variant will be truncated and the problematic subtag and all following
\r
2663 * sub-segments will be omitted. If the remainder is non-empty, it will be
\r
2664 * emitted as a private use subtag as above (even if the remainder turns out
\r
2665 * to be well-formed). For example, "Solaris_isjustthecoolestthing" is emitted
\r
2666 * as "x-jvariant-Solaris", not as "solaris".</li>
\r
2669 * <p><b>Note:</b> Although the language tag created by this method
\r
2670 * satisfies the syntax requirements defined by the IETF BCP 47
\r
2671 * specification, it is not always a valid BCP 47 language tag.
\r
2674 * new ULocale("xx_YY").toLanguageTag();
\r
2676 * will return "xx-YY", but the language subtag "xx" and the region subtag "YY"
\r
2677 * are invalid because they are not registered in the
\r
2678 * <a href="http://www.iana.org/assignments/language-subtag-registry">
\r
2679 * IANA Language Subtag Registry</a>.
\r
2681 * @return a BCP47 language tag representing the locale
\r
2682 * @see #forLanguageTag(String)
\r
2685 * @provisional This API might change or be removed in a future release.
\r
2687 public String toLanguageTag() {
\r
2688 LanguageTag tag = LanguageTag.parseLocale(base(), extensions());
\r
2689 return tag.getID();
\r
2693 * {@icu} Returns a locale for the specified IETF BCP 47 language tag string.
\r
2694 * If the specified language tag contains any ill-formed subtags,
\r
2695 * the first such subtag and all following subtags are ignored.
\r
2697 * <p>This implements the 'Language-Tag' production of BCP47, and
\r
2698 * so supports grandfathered (regular and irregular) as well as
\r
2699 * private use language tags. Stand alone private use tags are
\r
2700 * represented as empty language and extension 'x-whatever',
\r
2701 * and grandfathered tags are converted to their canonical replacements
\r
2702 * where they exist. Note that a few grandfathered tags have no
\r
2703 * modern replacement; these will be converted using the fallback
\r
2704 * described above so some information might be lost.
\r
2706 * <p>For a list of grandfathered tags, see the
\r
2707 * <a href="http://www.iana.org/assignments/language-subtag-registry">
\r
2708 * IANA Language Subtag Registry</a>.
\r
2710 * <p><b>Notes:</b> This method converts private use subtags prefixed
\r
2711 * by "variant" to variant field in the result locale. For example,
\r
2712 * the code below will return "POSIX".
\r
2714 * ULocale.forLanguageTag("en-US-x-variant-posix).getVariant();
\r
2717 * @param languageTag the language tag
\r
2718 * @return the locale that best represents the language tag
\r
2719 * @exception NullPointerException if <code>languageTag</code> is <code>null</code>
\r
2720 * @see #toLanguageTag()
\r
2723 * @provisional This API might change or be removed in a future release.
\r
2725 public static ULocale forLanguageTag(String languageTag) {
\r
2726 LanguageTag tag = LanguageTag.parse(languageTag, true);
\r
2727 return getInstance(tag.getBaseLocale(), tag.getLocaleExtensions());
\r
2732 * <code>Builder</code> is used to build instances of <code>ULocale</code>
\r
2733 * from values configured by the setter. Unlike the <code>ULocale</code>
\r
2734 * constructors, the <code>Builder</code> checks if a value configured by a
\r
2735 * setter satisfies the syntactical requirements defined by the <code>ULocale</code>
\r
2736 * class. A <code>ULocale</code> object created by a <code>Builder</code> is
\r
2737 * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
\r
2738 * without losing information.
\r
2741 * <b>Note:</b> The <code>ULocale</code> class does not provide
\r
2742 * any syntactical restrictions on variant, while BCP 47
\r
2743 * requires each variant subtag to be 5 to 8 alphanumeric letters or a single
\r
2744 * numeric letter followed by 3 alphanumeric letters. By default,
\r
2745 * the <code>setVariant</code> method throws <code>IllformedLocaleException</code>
\r
2746 * for a variant that does not satisfy the syntax above. If it is
\r
2747 * necessary to support such a variant, you could use the constructor <code>
\r
2748 * Builder(boolean isLenientVariant)</code> passing <code>true</code> to
\r
2749 * skip the syntax validation for variant. However, you should keep in
\r
2750 * mind that a <code>Locale</code> object created this way might lose
\r
2751 * the variant information when transformed to a BCP 47 language tag.
\r
2754 * The following example shows how to create a <code>ULocale</code> object
\r
2755 * with the <code>Builder</code>.
\r
2758 * ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
\r
2762 * <p>Builders can be reused; <code>clear()</code> resets all
\r
2763 * fields to their default values.
\r
2765 * @see ULocale#toLanguageTag()
\r
2768 * @provisional This API might change or be removed in a future release.
\r
2770 public static final class Builder {
\r
2772 private final InternalLocaleBuilder _locbld;
\r
2775 * Constructs an empty Builder. The default value of all
\r
2776 * fields, extensions, and private use information is the
\r
2780 * @provisional This API might change or be removed in a future release.
\r
2782 public Builder() {
\r
2787 * Constructs an empty Builder with an option whether to allow
\r
2788 * <code>setVariant</code> to accept a value that does not
\r
2789 * conform to the IETF BCP 47 variant subtag's syntax requirements.
\r
2791 * @param isLenientVariant When true, this <code>Builder</code>
\r
2792 * will accept an ill-formed variant.
\r
2793 * @see #setVariant(String)
\r
2796 * @provisional This API might change or be removed in a future release.
\r
2798 public Builder(boolean isLenientVariant) {
\r
2799 _locbld = new InternalLocaleBuilder(isLenientVariant);
\r
2803 * Returns true if this <code>Builder</code> accepts a value that does
\r
2804 * not conform to the IETF BCP 47 variant subtag's syntax requirements
\r
2805 * in <code>setVariant</code>
\r
2807 * @return true if this <code>Build</code> accepts an ill-formed variant.
\r
2810 * @provisional This API might change or be removed in a future release.
\r
2812 public boolean isLenientVariant() {
\r
2813 return _locbld.isLenientVariant();
\r
2818 * Resets the <code>Builder</code> to match the provided <code>locale</code>.
\r
2819 * The previous state of the builder is discarded. Fields that do
\r
2820 * not conform to the <code>ULocale</code> class specification, for example,
\r
2821 * a single letter language, are ill-formed.
\r
2823 * @param locale the locale
\r
2824 * @return this builder
\r
2825 * @throws IllformedLocaleException if <code>locale</code> has
\r
2826 * any ill-formed fields.
\r
2829 * @provisional This API might change or be removed in a future release.
\r
2831 public Builder setLocale(ULocale locale) {
\r
2833 _locbld.setLocale(locale.base(), locale.extensions());
\r
2834 } catch (LocaleSyntaxException e) {
\r
2835 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2841 * Resets the builder to match the provided IETF BCP 47 language tag.
\r
2842 * The previous state of the builder is discarded.
\r
2844 * @param languageTag the language tag
\r
2845 * @return this builder
\r
2846 * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed.
\r
2847 * @throws NullPointerException if <code>languageTag</code> is null.
\r
2848 * @see ULocale#forLanguageTag(String)
\r
2851 * @provisional This API might change or be removed in a future release.
\r
2853 public Builder setLanguageTag(String languageTag) {
\r
2854 LanguageTag tag = null;
\r
2856 tag = LanguageTag.parseStrict(languageTag, _locbld.isLenientVariant());
\r
2857 } catch (LocaleSyntaxException e) {
\r
2858 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2862 _locbld.setLocale(tag.getBaseLocale(),tag.getLocaleExtensions());
\r
2863 } catch (LocaleSyntaxException e) {
\r
2864 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2871 * Sets the language. If <code>language</code> is the empty string,
\r
2872 * the language in this <code>Builder</code> will be removed.
\r
2873 * Typical language value is a two or three-letter language
\r
2874 * code as defined in ISO639.
\r
2875 * Well-formed values are any string of two to eight alpha
\r
2876 * letters. This method accepts upper case alpha letters
\r
2877 * [A-Z], but the language value in the <code>ULocale</code>
\r
2878 * created by the <code>Builder</code> is always normalized
\r
2879 * to lower case letters.
\r
2881 * @param language the language
\r
2882 * @return this builder
\r
2883 * @throws IllformedLocaleException if <code>language</code> is ill-formed
\r
2886 * @provisional This API might change or be removed in a future release.
\r
2888 public Builder setLanguage(String language) {
\r
2890 _locbld.setLanguage(language);
\r
2891 } catch (LocaleSyntaxException e) {
\r
2892 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2898 * Sets the script. If <code>script</code> is the empty string,
\r
2899 * the script in this <code>Builder</code> is removed.
\r
2900 * Typical script value is a four-letter script code as defined by ISO 15924.
\r
2901 * Well-formed values are any string of four alpha letters.
\r
2902 * This method accepts both upper and lower case alpha letters [a-zA-Z],
\r
2903 * but the script value in the <code>ULocale</code> created by the
\r
2904 * <code>Builder</code> is always normalized to title case
\r
2905 * (the first letter is upper case and the rest of letters are lower case).
\r
2907 * @param script the script
\r
2908 * @return this builder
\r
2909 * @throws IllformedLocaleException if <code>script</code> is ill-formed
\r
2912 * @provisional This API might change or be removed in a future release.
\r
2914 public Builder setScript(String script) {
\r
2916 _locbld.setScript(script);
\r
2917 } catch (LocaleSyntaxException e) {
\r
2918 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2924 * Sets the region. If region is the empty string, the region
\r
2925 * in this <code>Builder</code> is removed.
\r
2926 * Typical region value is a two-letter ISO 3166 code or a three-digit UN M.49
\r
2927 * area code. Well-formed values are any two-letter or three-digit string.
\r
2928 * This method accepts lower case letters [a-z], but the country value in
\r
2929 * the <code>ULocale</code> created by the <code>Builder</code> is always
\r
2930 * normalized to upper case.
\r
2932 * @param region the region
\r
2933 * @return this builder
\r
2934 * @throws IllformedLocaleException if <code>region</code> is ill-formed
\r
2937 * @provisional This API might change or be removed in a future release.
\r
2939 public Builder setRegion(String region) {
\r
2941 _locbld.setRegion(region);
\r
2942 } catch (LocaleSyntaxException e) {
\r
2943 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2949 * Sets the variant. If variant is the empty string, the
\r
2950 * variant in this <code>Builder</code> is removed.
\r
2952 * <b>Note:</b> By default, this method checks if <code>variant</code>
\r
2953 * satisfies the IETF BCP 47 variant subtag's syntax requirements.
\r
2954 * However, the <code>ULocale</code> class itself does not impose any syntactical
\r
2955 * restriction on variant. When a <code>Builder</code> is created by the
\r
2956 * constructor <code>Builder(boolean isLenientVariant)</code>
\r
2957 * with <code>true</code>, this method skips the syntax check.
\r
2959 * @param variant the variant
\r
2960 * @return this builder
\r
2961 * @throws IllformedLocaleException if <code>variant</code> is ill-formed
\r
2964 * @provisional This API might change or be removed in a future release.
\r
2966 public Builder setVariant(String variant) {
\r
2968 _locbld.setVariant(variant);
\r
2969 } catch (LocaleSyntaxException e) {
\r
2970 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
2976 * Sets the extension for the given key. If the value is the
\r
2977 * empty string, the extension is removed. Legal keys are
\r
2978 * characters in the ranges <code>[0-9A-Za-z]</code>. Keys
\r
2979 * are case-insensitive, so for example 'z' and 'Z' represent
\r
2980 * the same extension. In general, well-formed values are any
\r
2981 * series of fields of two to eight alphanumeric characters,
\r
2982 * separated by hyphen or underscore.
\r
2984 * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
\r
2985 * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
\r
2986 * Setting a value for this key replaces any existing Unicode locale key/type
\r
2987 * pairs with those defined in the extension.
\r
2988 * To be well-formed, a value for this extension must meet the additional
\r
2989 * constraints that each locale key is two alphanumeric characters,
\r
2990 * followed by at least one locale type subtag represented by
\r
2991 * three to eight alphanumeric characters, and that the keys and types
\r
2992 * be legal Unicode locale keys and values.
\r
2994 * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
\r
2995 * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
\r
2996 * well-formed, the value for this key needs only to have fields of one to
\r
2997 * eight alphanumeric characters, not two to eight as in the general case.
\r
2999 * @param key the extension key
\r
3000 * @param value the extension value
\r
3001 * @return this builder
\r
3002 * @throws IllformedLocaleException if <code>key</code> is illegal
\r
3003 * or <code>value</code> is ill-formed
\r
3004 * @see #setUnicodeLocaleKeyword(String, String)
\r
3007 * @provisional This API might change or be removed in a future release.
\r
3009 public Builder setExtension(char key, String value) {
\r
3011 _locbld.setExtension(key, value);
\r
3012 } catch (LocaleSyntaxException e) {
\r
3013 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
3019 * Sets the Unicode locale keyword type for the given key. If the
\r
3020 * value is the empty string, the Unicode keyword is removed.
\r
3021 * Well-formed keys are strings of two alphanumeric characters.
\r
3022 * Well-formed types are one or more subtags where each of them is
\r
3023 * three to eight alphanumeric characters.
\r
3025 * <b>Note</b>:Setting the 'u' extension replaces all Unicode locale
\r
3026 * keywords with those defined in the extension.
\r
3027 * @param key the Unicode locale key
\r
3028 * @param type the Unicode locale type
\r
3029 * @return this builder
\r
3030 * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
\r
3032 * @see #setExtension(char, String)
\r
3035 * @provisional This API might change or be removed in a future release.
\r
3037 public Builder setUnicodeLocaleKeyword(String key, String type) {
\r
3039 _locbld.setUnicodeLocaleExtension(key, type);
\r
3040 } catch (LocaleSyntaxException e) {
\r
3041 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
\r
3047 * Resets the builder to its initial, empty state.
\r
3049 * @return this builder
\r
3052 * @provisional This API might change or be removed in a future release.
\r
3054 public Builder clear() {
\r
3060 * Resets the extensions to their initial, empty state.
\r
3061 * Language, script, region and variant are unchanged.
\r
3063 * @return this builder
\r
3064 * @see #setExtension(char, String)
\r
3067 * @provisional This API might change or be removed in a future release.
\r
3069 public Builder clearExtensions() {
\r
3070 _locbld.removeLocaleExtensions();
\r
3075 * Returns an instance of Locale created from the fields set
\r
3076 * on this builder.
\r
3078 * @return a new Locale
\r
3081 * @provisional This API might change or be removed in a future release.
\r
3083 public ULocale build() {
\r
3084 return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
\r
3088 private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
\r
3089 String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
\r
3090 base.getVariant());
\r
3092 Set<Character> extKeys = exts.getKeys();
\r
3093 if (!extKeys.isEmpty()) {
\r
3094 // legacy locale ID assume Unicode locale keywords and
\r
3095 // other extensions are at the same level.
\r
3096 // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
\r
3098 TreeMap<String, String> kwds = new TreeMap<String, String>();
\r
3099 for (Character key : extKeys) {
\r
3100 Extension ext = exts.getExtension(key);
\r
3101 if (ext instanceof UnicodeLocaleExtension) {
\r
3102 UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
\r
3103 Set<String> ukeys = uext.getKeys();
\r
3104 for (String bcpKey : ukeys) {
\r
3105 String bcpType = uext.getType(bcpKey);
\r
3106 // convert to legacy key/type
\r
3107 String lkey = bcp47ToLDMLKey(bcpKey);
\r
3108 String ltype = bcp47ToLDMLType(lkey, bcpType);
\r
3109 kwds.put(lkey, ltype);
\r
3112 kwds.put(String.valueOf(key), ext.getValue());
\r
3116 if (!kwds.isEmpty()) {
\r
3117 StringBuilder buf = new StringBuilder(id);
\r
3119 Set<Map.Entry<String, String>> kset = kwds.entrySet();
\r
3120 boolean insertSep = false;
\r
3121 for (Map.Entry<String, String> kwd : kset) {
\r
3127 buf.append(kwd.getKey());
\r
3129 buf.append(kwd.getValue());
\r
3132 id = buf.toString();
\r
3135 return new ULocale(id);
\r
3138 private BaseLocale base() {
\r
3139 if (baseLocale == null) {
\r
3140 String language = getLanguage();
\r
3141 if (equals(ULocale.ROOT)) {
\r
3144 baseLocale = BaseLocale.getInstance(language, getScript(), getCountry(), getVariant());
\r
3146 return baseLocale;
\r
3149 private LocaleExtensions extensions() {
\r
3150 if (extensions == null) {
\r
3151 Iterator<String> kwitr = getKeywords();
\r
3152 if (kwitr == null) {
\r
3153 extensions = LocaleExtensions.EMPTY_EXTENSIONS;
\r
3155 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
\r
3156 while (kwitr.hasNext()) {
\r
3157 String key = kwitr.next();
\r
3158 if (key.length() >= 2) {
\r
3159 String bcpKey = ldmlKeyToBCP47(key);
\r
3160 String bcpType = ldmlTypeToBCP47(key, getKeywordValue(key));
\r
3161 if (bcpKey != null && bcpType != null) {
\r
3163 intbld.setUnicodeLocaleExtension(bcpKey, bcpType);
\r
3164 } catch (LocaleSyntaxException e) {
\r
3165 // ignore and fall through
\r
3168 } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
\r
3170 intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
\r
3171 LanguageTag.SEP));
\r
3172 } catch (LocaleSyntaxException e) {
\r
3173 // ignore and fall through
\r
3177 extensions = intbld.getLocaleExtensions();
\r
3180 return extensions;
\r
3184 // LDML legacy/BCP47 key and type mapping functions
\r
3186 private static String ldmlKeyToBCP47(String key) {
\r
3187 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
\r
3188 ICUResourceBundle.ICU_BASE_NAME,
\r
3190 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
\r
3191 UResourceBundle keyMap = keyTypeData.get("keyMap");
\r
3193 // normalize key to lowercase
\r
3194 key = AsciiUtil.toLowerString(key);
\r
3195 String bcpKey = null;
\r
3197 bcpKey = keyMap.getString(key);
\r
3198 } catch (MissingResourceException mre) {
\r
3202 if (bcpKey == null) {
\r
3203 if (key.length() == 2 && LanguageTag.isExtensionSubtag(key)) {
\r
3211 private static String bcp47ToLDMLKey(String bcpKey) {
\r
3212 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
\r
3213 ICUResourceBundle.ICU_BASE_NAME,
\r
3215 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
\r
3216 UResourceBundle keyMap = keyTypeData.get("keyMap");
\r
3218 // normalize bcp key to lowercase
\r
3219 bcpKey = AsciiUtil.toLowerString(bcpKey);
\r
3220 String key = null;
\r
3221 for (int i = 0; i < keyMap.getSize(); i++) {
\r
3222 UResourceBundle mapData = keyMap.get(i);
\r
3223 if (bcpKey.equals(mapData.getString())) {
\r
3224 key = mapData.getKey();
\r
3228 if (key == null) {
\r
3234 private static String ldmlTypeToBCP47(String key, String type) {
\r
3235 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
\r
3236 ICUResourceBundle.ICU_BASE_NAME,
\r
3238 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
\r
3239 UResourceBundle typeMap = keyTypeData.get("typeMap");
\r
3241 // keys are case-insensitive, while types are case-sensitive
\r
3242 key = AsciiUtil.toLowerString(key);
\r
3243 UResourceBundle typeMapForKey = null;
\r
3244 String bcpType = null;
\r
3245 String typeResKey = key.equals("timezone") ? type.replace('/', ':') : type;
\r
3247 typeMapForKey = typeMap.get(key);
\r
3248 bcpType = typeMapForKey.getString(typeResKey);
\r
3249 } catch (MissingResourceException mre) {
\r
3253 if (bcpType == null && typeMapForKey != null) {
\r
3254 // is this type alias?
\r
3255 UResourceBundle typeAlias = keyTypeData.get("typeAlias");
\r
3257 UResourceBundle typeAliasForKey = typeAlias.get(key);
\r
3258 typeResKey = typeAliasForKey.getString(typeResKey);
\r
3259 bcpType = typeMapForKey.getString(typeResKey.replace('/', ':'));
\r
3260 } catch (MissingResourceException mre) {
\r
3265 if (bcpType == null) {
\r
3266 int typeLen = type.length();
\r
3267 if (typeLen >= 3 && typeLen <= 8 && LanguageTag.isExtensionSubtag(type)) {
\r
3275 private static String bcp47ToLDMLType(String key, String bcpType) {
\r
3276 UResourceBundle keyTypeData = UResourceBundle.getBundleInstance(
\r
3277 ICUResourceBundle.ICU_BASE_NAME,
\r
3279 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
\r
3280 UResourceBundle typeMap = keyTypeData.get("typeMap");
\r
3282 // normalize key/bcpType to lowercase
\r
3283 key = AsciiUtil.toLowerString(key);
\r
3284 bcpType = AsciiUtil.toLowerString(bcpType);
\r
3286 String type = null;
\r
3288 UResourceBundle typeMapForKey = typeMap.get(key);
\r
3290 // Note: Linear search for time zone ID might be too slow.
\r
3291 // ICU services do not use timezone keywords for now.
\r
3292 // In future, we may need to build the optimized inverse
\r
3295 for (int i = 0; i < typeMapForKey.getSize(); i++) {
\r
3296 UResourceBundle mapData = typeMapForKey.get(i);
\r
3297 if (bcpType.equals(mapData.getString())) {
\r
3298 type = mapData.getKey();
\r
3299 if (key.equals("timezone")) {
\r
3300 type = type.replace(':', '/');
\r
3305 } catch (MissingResourceException mre) {
\r
3309 if (type == null) {
\r