2 ******************************************************************************
\r
3 * Copyright (C) 2003-2008, 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.lang.ref.SoftReference;
\r
12 import java.text.ParseException;
\r
13 import java.util.Collections;
\r
14 import java.util.Comparator;
\r
15 import java.util.HashMap;
\r
16 import java.util.Iterator;
\r
17 import java.util.Locale;
\r
18 import java.util.Map;
\r
19 import java.util.MissingResourceException;
\r
20 import java.util.TreeMap;
\r
22 //import com.ibm.icu.impl.SimpleCache;
\r
23 //import com.ibm.icu.impl.ICUResourceBundle;
\r
24 //import com.ibm.icu.impl.LocaleUtility;
\r
27 * A class analogous to {@link java.util.Locale} that provides additional
\r
28 * support for ICU protocol. In ICU 3.0 this class is enhanced to support
\r
29 * RFC 3066 language identifiers.
\r
31 * <p>Many classes and services in ICU follow a factory idiom, in
\r
32 * which a factory method or object responds to a client request with
\r
33 * an object. The request includes a locale (the <i>requested</i>
\r
34 * locale), and the returned object is constructed using data for that
\r
35 * locale. The system may lack data for the requested locale, in
\r
36 * which case the locale fallback mechanism will be invoked until a
\r
37 * populated locale is found (the <i>valid</i> locale). Furthermore,
\r
38 * even when a populated locale is found (the <i>valid</i> locale),
\r
39 * further fallback may be required to reach a locale containing the
\r
40 * specific data required by the service (the <i>actual</i> locale).
\r
42 * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
\r
43 * Normalization 'cleans up' ICU locale ids as follows:
\r
45 * <li>language, script, country, variant, and keywords are properly cased<br>
\r
46 * (lower, title, upper, upper, and lower case respectively)</li>
\r
47 * <li>hyphens used as separators are converted to underscores</li>
\r
48 * <li>three-letter language and country ids are converted to two-letter
\r
49 * equivalents where available</li>
\r
50 * <li>surrounding spaces are removed from keywords and values</li>
\r
51 * <li>if there are multiple keywords, they are put in sorted order</li>
\r
53 * Canonicalization additionally performs the following:
\r
55 * <li>POSIX ids are converted to ICU format IDs</li>
\r
56 * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
\r
57 * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form, with the currency
\r
58 * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
\r
60 * All ULocale constructors automatically normalize the locale id. To handle
\r
61 * POSIX ids, <code>canonicalize</code> can be called to convert the id
\r
62 * to canonical form, or the <code>canonicalInstance</code> factory method
\r
63 * can be called.</p>
\r
65 * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
\r
66 * #ACTUAL_LOCALE} intended for use in methods named
\r
67 * <tt>getLocale()</tt>. These methods exist in several ICU classes,
\r
68 * including {@link com.ibm.icu.util.Calendar}, {@link
\r
69 * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
\r
70 * {@link com.ibm.icu.text.BreakIterator}, {@link
\r
71 * com.ibm.icu.text.Collator}, {@link
\r
72 * com.ibm.icu.text.DateFormatSymbols}, and {@link
\r
73 * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
\r
74 * any. Once an object of one of these classes has been created,
\r
75 * <tt>getLocale()</tt> may be called on it to determine the valid and
\r
76 * actual locale arrived at during the object's construction.
\r
78 * <p>Note: The <tt>getLocale()</tt> method will be implemented in ICU
\r
79 * 3.0; ICU 2.8 contains a partial preview implementation. The
\r
80 * <i>actual</i> locale is returned correctly, but the <i>valid</i>
\r
81 * locale is not, in most cases.
\r
83 * @see java.util.Locale
\r
86 * @author Ram Viswanadha
\r
89 public final class ULocale implements Serializable {
\r
90 // using serialver from jdk1.4.2_05
\r
91 private static final long serialVersionUID = 3715177670352309217L;
\r
94 * Useful constant for language.
\r
97 public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
\r
100 * Useful constant for language.
\r
103 public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
\r
106 * Useful constant for language.
\r
109 public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
\r
112 * Useful constant for language.
\r
115 public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
\r
118 * Useful constant for language.
\r
121 public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
\r
124 * Useful constant for language.
\r
127 public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
\r
130 * Useful constant for language.
\r
133 public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
\r
136 * Useful constant for language.
\r
139 public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans", Locale.CHINESE);
\r
142 * Useful constant for language.
\r
145 public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant", Locale.CHINESE);
\r
148 * Useful constant for country/region.
\r
151 public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
\r
154 * Useful constant for country/region.
\r
157 public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
\r
160 * Useful constant for country/region.
\r
163 public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
\r
166 * Useful constant for country/region.
\r
169 public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
\r
172 * Useful constant for country/region.
\r
175 public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
\r
178 * Useful constant for country/region.
\r
181 public static final ULocale CHINA = new ULocale("zh_Hans_CN", Locale.CHINA);
\r
184 * Useful constant for country/region.
\r
187 public static final ULocale PRC = CHINA;
\r
190 * Useful constant for country/region.
\r
193 public static final ULocale TAIWAN = new ULocale("zh_Hant_TW", Locale.TAIWAN);
\r
196 * Useful constant for country/region.
\r
199 public static final ULocale UK = new ULocale("en_GB", Locale.UK);
\r
202 * Useful constant for country/region.
\r
205 public static final ULocale US = new ULocale("en_US", Locale.US);
\r
208 * Useful constant for country/region.
\r
211 public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
\r
214 * Useful constant for country/region.
\r
217 public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
\r
222 private static final String EMPTY_STRING = "";
\r
224 // Used in both ULocale and IDParser, so moved up here.
\r
225 private static final char UNDERSCORE = '_';
\r
227 // default empty locale
\r
228 private static final Locale EMPTY_LOCALE = new Locale("", "");
\r
231 * The root ULocale.
\r
234 public static final ULocale ROOT = new ULocale("root", EMPTY_LOCALE);
\r
236 // private static final SimpleCache CACHE = new SimpleCache();
\r
237 private static final HashMap CACHE = new HashMap(20);
\r
240 * Cache the locale.
\r
242 private transient Locale locale;
\r
245 * The raw localeID that we were passed in.
\r
247 private String localeID;
\r
250 * Tables used in normalizing portions of the id.
\r
252 /* tables updated per http://lcweb.loc.gov/standards/iso639-2/
\r
253 to include the revisions up to 2001/7/27 *CWB*/
\r
254 /* The 3 character codes are the terminology codes like RFC 3066.
\r
255 This is compatible with prior ICU codes */
\r
256 /* "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in
\r
257 the table but now at the end of the table because
\r
258 3 character codes are duplicates. This avoids bad searches
\r
259 going from 3 to 2 character codes.*/
\r
260 /* The range qaa-qtz is reserved for local use. */
\r
262 private static String[] _languages;
\r
263 private static String[] _replacementLanguages;
\r
264 private static String[] _obsoleteLanguages;
\r
265 private static String[] _languages3;
\r
266 private static String[] _obsoleteLanguages3;
\r
268 // Avoid initializing languages tables unless we have to.
\r
269 private static void initLanguageTables() {
\r
270 if (_languages == null) {
\r
272 /* This list MUST be in sorted order, and MUST contain the two-letter codes
\r
273 if one exists otherwise use the three letter code */
\r
274 String[] tempLanguages = {
\r
275 "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa",
\r
276 "afh", "ak", "akk", "ale", "alg", "am", "an", "ang", "apa",
\r
277 "ar", "arc", "arn", "arp", "art", "arw", "as", "ast",
\r
278 "ath", "aus", "av", "awa", "ay", "az", "ba", "bad",
\r
279 "bai", "bal", "ban", "bas", "bat", "be", "bej",
\r
280 "bem", "ber", "bg", "bh", "bho", "bi", "bik", "bin",
\r
281 "bla", "bm", "bn", "bnt", "bo", "br", "bra", "bs",
\r
282 "btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau",
\r
283 "ce", "ceb", "cel", "ch", "chb", "chg", "chk", "chm",
\r
284 "chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop",
\r
285 "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus",
\r
286 "cv", "cy", "da", "dak", "dar", "day", "de", "del", "den",
\r
287 "dgr", "din", "doi", "dra", "dsb", "dua", "dum", "dv", "dyu",
\r
288 "dz", "ee", "efi", "egy", "eka", "el", "elx", "en",
\r
289 "enm", "eo", "es", "et", "eu", "ewo", "fa",
\r
290 "fan", "fat", "ff", "fi", "fiu", "fj", "fo", "fon",
\r
291 "fr", "frm", "fro", "fur", "fy", "ga", "gaa", "gay",
\r
292 "gba", "gd", "gem", "gez", "gil", "gl", "gmh", "gn",
\r
293 "goh", "gon", "gor", "got", "grb", "grc", "gu", "gv",
\r
294 "gwi", "ha", "hai", "haw", "he", "hi", "hil", "him",
\r
295 "hit", "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy", "hz",
\r
296 "ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik",
\r
297 "ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it",
\r
298 "iu", "ja", "jbo", "jpr", "jrb", "jv", "ka", "kaa", "kab",
\r
299 "kac", "kam", "kar", "kaw", "kbd", "kg", "kha", "khi",
\r
300 "kho", "ki", "kj", "kk", "kl", "km", "kmb", "kn",
\r
301 "ko", "kok", "kos", "kpe", "kr", "krc", "kro", "kru", "ks",
\r
302 "ku", "kum", "kut", "kv", "kw", "ky", "la", "lad",
\r
303 "lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol",
\r
304 "loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus",
\r
305 "lv", "mad", "mag", "mai", "mak", "man", "map", "mas",
\r
306 "mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min",
\r
307 "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno",
\r
308 "mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun",
\r
309 "mus", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nap",
\r
310 "nb", "nd", "nds", "ne", "new", "ng", "nia", "nic",
\r
311 "niu", "nl", "nn", "no", "nog", "non", "nr", "nso", "nub",
\r
312 "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj",
\r
313 "om", "or", "os", "osa", "ota", "oto", "pa", "paa",
\r
314 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
\r
315 "pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu",
\r
316 "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rom",
\r
317 "ru", "rup", "rw", "sa", "sad", "sah", "sai", "sal", "sam",
\r
318 "sas", "sat", "sc", "sco", "sd", "se", "sel", "sem",
\r
319 "sg", "sga", "sgn", "shn", "si", "sid", "sio", "sit",
\r
320 "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn",
\r
321 "sms", "sn", "snk", "so", "sog", "son", "sq", "sr",
\r
322 "srr", "ss", "ssa", "st", "su", "suk", "sus", "sux",
\r
323 "sv", "sw", "syr", "ta", "tai", "te", "tem", "ter",
\r
324 "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl",
\r
325 "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr",
\r
326 "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw",
\r
327 "ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur",
\r
328 "uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak",
\r
329 "wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap",
\r
330 "yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd",
\r
334 String[] tempReplacementLanguages = {
\r
335 "id", "he", "yi", "jv", "sr", "nb",/* replacement language codes */
\r
338 String[] tempObsoleteLanguages = {
\r
339 "in", "iw", "ji", "jw", "sh", "no", /* obsolete language codes */
\r
342 /* This list MUST contain a three-letter code for every two-letter code in the
\r
343 list above, and they MUST ne in the same order (i.e., the same language must
\r
344 be in the same place in both lists)! */
\r
345 String[] tempLanguages3 = {
\r
346 /*"aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa", */
\r
347 "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr", "afa",
\r
348 /*"afh", "ak", "akk", "ale", "alg", "am", "an", "ang", "apa", */
\r
349 "afh", "aka", "akk", "ale", "alg", "amh", "arg", "ang", "apa",
\r
350 /*"ar", "arc", "arn", "arp", "art", "arw", "as", "ast", */
\r
351 "ara", "arc", "arn", "arp", "art", "arw", "asm", "ast",
\r
352 /*"ath", "aus", "av", "awa", "ay", "az", "ba", "bad", */
\r
353 "ath", "aus", "ava", "awa", "aym", "aze", "bak", "bad",
\r
354 /*"bai", "bal", "ban", "bas", "bat", "be", "bej", */
\r
355 "bai", "bal", "ban", "bas", "bat", "bel", "bej",
\r
356 /*"bem", "ber", "bg", "bh", "bho", "bi", "bik", "bin", */
\r
357 "bem", "ber", "bul", "bih", "bho", "bis", "bik", "bin",
\r
358 /*"bla", "bm", "bn", "bnt", "bo", "br", "bra", "bs", */
\r
359 "bla", "bam", "ben", "bnt", "bod", "bre", "bra", "bos",
\r
360 /*"btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau", */
\r
361 "btk", "bua", "bug", "byn", "cat", "cad", "cai", "car", "cau",
\r
362 /*"ce", "ceb", "cel", "ch", "chb", "chg", "chk", "chm", */
\r
363 "che", "ceb", "cel", "cha", "chb", "chg", "chk", "chm",
\r
364 /*"chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop", */
\r
365 "chn", "cho", "chp", "chr", "chy", "cmc", "cos", "cop",
\r
366 /*"cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus", */
\r
367 "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces", "csb", "chu", "cus",
\r
368 /*"cv", "cy", "da", "dak", "dar", "day", "de", "del", "den", */
\r
369 "chv", "cym", "dan", "dak", "dar", "day", "deu", "del", "den",
\r
370 /*"dgr", "din", "doi", "dra", "dsb", "dua", "dum", "dv", "dyu", */
\r
371 "dgr", "din", "doi", "dra", "dsb", "dua", "dum", "div", "dyu",
\r
372 /*"dz", "ee", "efi", "egy", "eka", "el", "elx", "en", */
\r
373 "dzo", "ewe", "efi", "egy", "eka", "ell", "elx", "eng",
\r
374 /*"enm", "eo", "es", "et", "eu", "ewo", "fa", */
\r
375 "enm", "epo", "spa", "est", "eus", "ewo", "fas",
\r
376 /*"fan", "fat", "ff", "fi", "fiu", "fj", "fo", "fon", */
\r
377 "fan", "fat", "ful", "fin", "fiu", "fij", "fao", "fon",
\r
378 /*"fr", "frm", "fro", "fur", "fy", "ga", "gaa", "gay", */
\r
379 "fra", "frm", "fro", "fur", "fry", "gle", "gaa", "gay",
\r
380 /*"gba", "gd", "gem", "gez", "gil", "gl", "gmh", "gn", */
\r
381 "gba", "gla", "gem", "gez", "gil", "glg", "gmh", "grn",
\r
382 /*"goh", "gon", "gor", "got", "grb", "grc", "gu", "gv", */
\r
383 "goh", "gon", "gor", "got", "grb", "grc", "guj", "glv",
\r
384 /*"gwi", "ha", "hai", "haw", "he", "hi", "hil", "him", */
\r
385 "gwi", "hau", "hai", "haw", "heb", "hin", "hil", "him",
\r
386 /*"hit", "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy", "hz", */
\r
387 "hit", "hmn", "hmo", "hrv", "hsb", "hat", "hun", "hup", "hye", "her",
\r
388 /*"ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik", */
\r
389 "ina", "iba", "ind", "ile", "ibo", "iii", "ijo", "ipk",
\r
390 /*"ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it", */
\r
391 "ilo", "inc", "ine", "inh", "ido", "ira", "iro", "isl", "ita",
\r
392 /*"iu", "ja", "jbo", "jpr", "jrb", "jv", "ka", "kaa", "kab", */
\r
393 "iku", "jpn", "jbo", "jpr", "jrb", "jaw", "kat", "kaa", "kab",
\r
394 /*"kac", "kam", "kar", "kaw", "kbd", "kg", "kha", "khi", */
\r
395 "kac", "kam", "kar", "kaw", "kbd", "kon", "kha", "khi",
\r
396 /*"kho", "ki", "kj", "kk", "kl", "km", "kmb", "kn", */
\r
397 "kho", "kik", "kua", "kaz", "kal", "khm", "kmb", "kan",
\r
398 /*"ko", "kok", "kos", "kpe", "kr", "krc", "kro", "kru", "ks", */
\r
399 "kor", "kok", "kos", "kpe", "kau", "krc", "kro", "kru", "kas",
\r
400 /*"ku", "kum", "kut", "kv", "kw", "ky", "la", "lad", */
\r
401 "kur", "kum", "kut", "kom", "cor", "kir", "lat", "lad",
\r
402 /*"lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol", */
\r
403 "lah", "lam", "ltz", "lez", "lug", "lim", "lin", "lao", "lol",
\r
404 /*"loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus", */
\r
405 "loz", "lit", "lub", "lua", "lui", "lun", "luo", "lus",
\r
406 /*"lv", "mad", "mag", "mai", "mak", "man", "map", "mas", */
\r
407 "lav", "mad", "mag", "mai", "mak", "man", "map", "mas",
\r
408 /*"mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min", */
\r
409 "mdf", "mdr", "men", "mlg", "mga", "mah", "mri", "mic", "min",
\r
410 /*"mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno", */
\r
411 "mis", "mkd", "mkh", "mal", "mon", "mnc", "mni", "mno",
\r
412 /*"mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun", */
\r
413 "mol", "moh", "mos", "mar", "msa", "mlt", "mul", "mun",
\r
414 /*"mus", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nap", */
\r
415 "mus", "mwr", "mya", "myn", "myv", "nau", "nah", "nai", "nap",
\r
416 /*"nb", "nd", "nds", "ne", "new", "ng", "nia", "nic", */
\r
417 "nob", "nde", "nds", "nep", "new", "ndo", "nia", "nic",
\r
418 /*"niu", "nl", "nn", "no", "nog", "non", "nr", "nso", "nub", */
\r
419 "niu", "nld", "nno", "nor", "nog", "non", "nbl", "nso", "nub",
\r
420 /*"nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj", */
\r
421 "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi", "oci", "oji",
\r
422 /*"om", "or", "os", "osa", "ota", "oto", "pa", "paa", */
\r
423 "orm", "ori", "oss", "osa", "ota", "oto", "pan", "paa",
\r
424 /*"pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn", */
\r
425 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
\r
426 /*"pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu", */
\r
427 "pli", "pol", "pon", "pra", "pro", "pus", "por", "que",
\r
428 /*"raj", "rap", "rar", "rm", "rn", "ro", "roa", "rom", */
\r
429 "raj", "rap", "rar", "roh", "run", "ron", "roa", "rom",
\r
430 /*"ru", "rup", "rw", "sa", "sad", "sah", "sai", "sal", "sam", */
\r
431 "rus", "rup", "kin", "san", "sad", "sah", "sai", "sal", "sam",
\r
432 /*"sas", "sat", "sc", "sco", "sd", "se", "sel", "sem", */
\r
433 "sas", "sat", "srd", "sco", "snd", "sme", "sel", "sem",
\r
434 /*"sg", "sga", "sgn", "shn", "si", "sid", "sio", "sit", */
\r
435 "sag", "sga", "sgn", "shn", "sin", "sid", "sio", "sit",
\r
436 /*"sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn", */
\r
437 "slk", "slv", "sla", "smo", "sma", "smi", "smj", "smn",
\r
438 /*"sms", "sn", "snk", "so", "sog", "son", "sq", "sr", */
\r
439 "sms", "sna", "snk", "som", "sog", "son", "sqi", "srp",
\r
440 /*"srr", "ss", "ssa", "st", "su", "suk", "sus", "sux", */
\r
441 "srr", "ssw", "ssa", "sot", "sun", "suk", "sus", "sux",
\r
442 /*"sv", "sw", "syr", "ta", "tai", "te", "tem", "ter", */
\r
443 "swe", "swa", "syr", "tam", "tai", "tel", "tem", "ter",
\r
444 /*"tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl", */
\r
445 "tet", "tgk", "tha", "tir", "tig", "tiv", "tuk", "tkl",
\r
446 /*"tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", */
\r
447 "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur",
\r
448 /*"ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", */
\r
449 "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi",
\r
450 /*"ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur", */
\r
451 "tah", "tyv", "udm", "uig", "uga", "ukr", "umb", "und", "urd",
\r
452 /*"uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak", */
\r
453 "uzb", "vai", "ven", "vie", "vol", "vot", "wln", "wak",
\r
454 /*"wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap", */
\r
455 "wal", "war", "was", "wen", "wol", "xal", "xho", "yao", "yap",
\r
456 /*"yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd", */
\r
457 "yid", "yor", "ypk", "zha", "zap", "zen", "zho", "znd",
\r
462 String[] tempObsoleteLanguages3 = {
\r
463 /* "in", "iw", "ji", "jw", "sh", */
\r
464 "ind", "heb", "yid", "jaw", "srp",
\r
467 synchronized (ULocale.class) {
\r
468 if (_languages == null) {
\r
469 _languages = tempLanguages;
\r
470 _replacementLanguages = tempReplacementLanguages;
\r
471 _obsoleteLanguages = tempObsoleteLanguages;
\r
472 _languages3 = tempLanguages3;
\r
473 _obsoleteLanguages3 = tempObsoleteLanguages3;
\r
479 private static String[] _countries;
\r
480 private static String[] _deprecatedCountries;
\r
481 private static String[] _replacementCountries;
\r
482 private static String[] _obsoleteCountries;
\r
483 private static String[] _countries3;
\r
484 private static String[] _obsoleteCountries3;
\r
486 // Avoid initializing country tables unless we have to.
\r
487 private static void initCountryTables() {
\r
488 if (_countries == null) {
\r
489 /* ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per
\r
490 http://www.evertype.com/standards/iso3166/iso3166-1-en.html
\r
491 added new codes keeping the old ones for compatibility
\r
492 updated to include 1999/12/03 revisions *CWB*/
\r
494 /* RO(ROM) is now RO(ROU) according to
\r
495 http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html
\r
498 /* This list MUST be in sorted order, and MUST contain only two-letter codes! */
\r
499 String[] tempCountries = {
\r
500 "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN",
\r
501 "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ",
\r
502 "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
\r
503 "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BT", "BV",
\r
504 "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG",
\r
505 "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR",
\r
506 "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK",
\r
507 "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER",
\r
508 "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR",
\r
509 "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL",
\r
510 "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU",
\r
511 "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
\r
512 "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS",
\r
513 "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI",
\r
514 "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
\r
515 "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU",
\r
516 "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK",
\r
517 "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS",
\r
518 "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA",
\r
519 "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP",
\r
520 "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG",
\r
521 "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT",
\r
522 "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA",
\r
523 "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ",
\r
524 "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV",
\r
525 "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ",
\r
526 "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV",
\r
527 "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ",
\r
528 "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
\r
529 "WS", "YE", "YT", "ZA", "ZM", "ZW",
\r
532 /* this table is used for 3 letter codes */
\r
533 String[] tempObsoleteCountries = {
\r
534 "FX", "CS", "RO", "TP", "YU", "ZR", /* obsolete country codes */
\r
537 String[] tempDeprecatedCountries = {
\r
538 "BU", "CS", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" /* deprecated country list */
\r
540 String[] tempReplacementCountries = {
\r
541 /* "BU", "CS", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" */
\r
542 "MM", "RS", "BJ", "FR", "BF", "VU", "ZW", "TL", "RS", "CD", /* replacement country codes */
\r
545 /* This list MUST contain a three-letter code for every two-letter code in
\r
546 the above list, and they MUST be listed in the same order! */
\r
547 String[] tempCountries3 = {
\r
548 /* "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", */
\r
549 "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", "ANT",
\r
550 /* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */
\r
551 "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE",
\r
552 /* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */
\r
553 "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI",
\r
554 /* "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BT", "BV", */
\r
555 "BEN", "BLM", "BMU", "BRN", "BOL", "BRA", "BHS", "BTN", "BVT",
\r
556 /* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */
\r
557 "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG",
\r
558 /* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */
\r
559 "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI",
\r
560 /* "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", */
\r
561 "CUB", "CPV", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK",
\r
562 /* "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", */
\r
563 "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI",
\r
564 /* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */
\r
565 "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA",
\r
566 /* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */
\r
567 "GAB", "GBR", "GRD", "GEO", "GUF", "GGY", "GHA", "GIB", "GRL",
\r
568 /* "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */
\r
569 "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM",
\r
570 /* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */
\r
571 "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN",
\r
572 /* "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */
\r
573 "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL",
\r
574 /* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */
\r
575 "ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR",
\r
576 /* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */
\r
577 "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO",
\r
578 /* "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */
\r
579 "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX",
\r
580 /* "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", */
\r
581 "LVA", "LBY", "MAR", "MCO", "MDA", "MNE", "MAF", "MDG", "MHL", "MKD",
\r
582 /* "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */
\r
583 "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR",
\r
584 /* "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */
\r
585 "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM",
\r
586 /* "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */
\r
587 "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL",
\r
588 /* "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */
\r
589 "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG",
\r
590 /* "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */
\r
591 "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT",
\r
592 /* "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", */
\r
593 "PLW", "PRY", "QAT", "REU", "ROU", "SRB", "RUS", "RWA", "SAU",
\r
594 /* "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */
\r
595 "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM",
\r
596 /* "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", */
\r
597 "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "STP", "SLV",
\r
598 /* "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */
\r
599 "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
\r
600 /* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */
\r
601 "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV",
\r
602 /* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */
\r
603 "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB",
\r
604 /* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */
\r
605 "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF",
\r
606 /* "WS", "YE", "YT", "ZA", "ZM", "ZW" */
\r
607 "WSM", "YEM", "MYT", "ZAF", "ZMB", "ZWE",
\r
610 String[] tempObsoleteCountries3 = {
\r
611 /*"FX", "CS", "RO", "TP", "YU", "ZR", */
\r
612 "FXX", "SCG", "ROM", "TMP", "YUG", "ZAR",
\r
615 synchronized (ULocale.class) {
\r
616 if (_countries == null) {
\r
617 _countries = tempCountries;
\r
618 _deprecatedCountries = tempDeprecatedCountries;
\r
619 _replacementCountries = tempReplacementCountries;
\r
620 _obsoleteCountries = tempObsoleteCountries;
\r
621 _countries3 = tempCountries3;
\r
622 _obsoleteCountries3 = tempObsoleteCountries3;
\r
628 private static String[][] CANONICALIZE_MAP;
\r
629 private static String[][] variantsToKeywords;
\r
631 private static void initCANONICALIZE_MAP() {
\r
632 if (CANONICALIZE_MAP == null) {
\r
634 * This table lists pairs of locale ids for canonicalization. The
\r
635 * The 1st item is the normalized id. The 2nd item is the
\r
636 * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
\r
638 String[][] tempCANONICALIZE_MAP = {
\r
639 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */
\r
640 { "C", "en_US_POSIX", null, null }, /* POSIX name */
\r
641 { "art_LOJBAN", "jbo", null, null }, /* registered name */
\r
642 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */
\r
643 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */
\r
644 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
\r
645 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */
\r
646 { "de_1901", "de__1901", null, null }, /* registered name */
\r
647 { "de_1906", "de__1906", null, null }, /* registered name */
\r
648 { "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
\r
649 { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
\r
650 { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
\r
651 { "de_LU_PREEURO", "de_LU", "currency", "EUR" },
\r
652 { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
\r
653 { "en_BOONT", "en__BOONT", null, null }, /* registered name */
\r
654 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */
\r
655 { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
\r
656 { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
\r
657 { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
\r
658 { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
\r
659 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
\r
660 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
\r
661 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
\r
662 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
\r
663 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
\r
664 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
\r
665 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
\r
666 { "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
\r
667 { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
\r
668 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
\r
669 // { "nb_NO_NY", "nn_NO", null, null },
\r
670 { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
\r
671 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
\r
672 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
\r
673 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */
\r
674 { "sr_SP_CYRL", "sr_Cyrl_RS", null, null }, /* .NET name */
\r
675 { "sr_SP_LATN", "sr_Latn_RS", null, null }, /* .NET name */
\r
676 { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
\r
677 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
\r
678 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
\r
679 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */
\r
680 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */
\r
681 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */
\r
682 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */
\r
683 { "zh_GAN", "zh__GAN", null, null }, /* registered name */
\r
684 { "zh_GUOYU", "zh", null, null }, /* registered name */
\r
685 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */
\r
686 { "zh_MIN", "zh__MIN", null, null }, /* registered name */
\r
687 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */
\r
688 { "zh_WUU", "zh__WUU", null, null }, /* registered name */
\r
689 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */
\r
690 { "zh_YUE", "zh__YUE", null, null } /* registered name */
\r
693 synchronized (ULocale.class) {
\r
694 if (CANONICALIZE_MAP == null) {
\r
695 CANONICALIZE_MAP = tempCANONICALIZE_MAP;
\r
699 if (variantsToKeywords == null) {
\r
701 * This table lists pairs of locale ids for canonicalization. The
\r
702 * The first item is the normalized variant id.
\r
704 String[][] tempVariantsToKeywords = {
\r
705 { "EURO", "currency", "EUR" },
\r
706 { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
\r
707 { "STROKE", "collation", "stroke" } /* Solaris variant */
\r
710 synchronized (ULocale.class) {
\r
711 if (variantsToKeywords == null) {
\r
712 variantsToKeywords = tempVariantsToKeywords;
\r
719 * This table is used for mapping between ICU and special Java
\r
720 * locales. When an ICU locale matches <minumum base> with
\r
721 * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
\r
722 * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
\r
723 * are mapped to Java locale "ja_JP_JP". ICU locale "nn" is mapped
\r
724 * to Java locale "no_NO_NY".
\r
726 private static final String[][] _javaLocaleMap = {
\r
727 // { <Java>, <ICU base>, <keyword>, <value>, <minimum base>
\r
728 { "ja_JP_JP", "ja_JP", "calendar", "japanese", "ja"},
\r
729 { "no_NO_NY", "nn_NO", null, null, "nn"},
\r
730 // { "th_TH_TH", "th_TH", ??, ??, "th"} //TODO
\r
734 * Private constructor used by static initializers.
\r
736 private ULocale(String localeID, Locale locale) {
\r
737 this.localeID = localeID;
\r
738 this.locale = locale;
\r
742 * Construct a ULocale object from a {@link java.util.Locale}.
\r
743 * @param loc a JDK locale
\r
747 private ULocale(Locale loc) {
\r
748 this.localeID = getName(forLocale(loc).toString());
\r
753 * Return a ULocale object for a {@link java.util.Locale}.
\r
754 * The ULocale is canonicalized.
\r
755 * @param loc a JDK locale
\r
758 public static ULocale forLocale(Locale loc) {
\r
762 ULocale result = (ULocale)CACHE.get(loc);
\r
763 if (result == null) {
\r
764 if (defaultULocale != null && loc == defaultULocale.locale) {
\r
765 result = defaultULocale;
\r
767 String locStr = loc.toString();
\r
768 if (locStr.length() == 0) {
\r
771 for (int i = 0; i < _javaLocaleMap.length; i++) {
\r
772 if (_javaLocaleMap[i][0].equals(locStr)) {
\r
773 IDParser p = new IDParser(_javaLocaleMap[i][1]);
\r
774 p.setKeywordValue(_javaLocaleMap[i][2], _javaLocaleMap[i][3]);
\r
775 locStr = p.getName();
\r
779 result = new ULocale(locStr, loc);
\r
782 CACHE.put(loc, result);
\r
788 * Construct a ULocale from a RFC 3066 locale ID. The locale ID consists
\r
789 * of optional language, script, country, and variant fields in that order,
\r
790 * separated by underscores, followed by an optional keyword list. The
\r
791 * script, if present, is four characters long-- this distinguishes it
\r
792 * from a country code, which is two characters long. Other fields
\r
793 * are distinguished by position as indicated by the underscores. The
\r
794 * start of the keyword list is indicated by '@', and consists of two
\r
795 * or more keyword/value pairs separated by semicolons(';').
\r
797 * This constructor does not canonicalize the localeID. So, for
\r
798 * example, "zh__pinyin" remains unchanged instead of converting
\r
799 * to "zh@collation=pinyin". By default ICU only recognizes the
\r
800 * latter as specifying pinyin collation. Use {@link #createCanonical}
\r
801 * or {@link #canonicalize} if you need to canonicalize the localeID.
\r
803 * @param localeID string representation of the locale, e.g:
\r
804 * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
\r
807 public ULocale(String localeID) {
\r
808 this.localeID = getName(localeID);
\r
812 * Convenience overload of ULocale(String, String, String) for
\r
813 * compatibility with java.util.Locale.
\r
814 * @see #ULocale(String, String, String)
\r
817 public ULocale(String a, String b) {
\r
822 * Construct a ULocale from a localeID constructed from the three 'fields' a, b, and c. These
\r
823 * fields are concatenated using underscores to form a localeID of
\r
824 * the form a_b_c, which is then handled like the localeID passed
\r
825 * to <code>ULocale(String localeID)</code>.
\r
827 * <p>Java locale strings consisting of language, country, and
\r
828 * variant will be handled by this form, since the country code
\r
829 * (being shorter than four letters long) will not be interpreted
\r
830 * as a script code. If a script code is present, the final
\r
831 * argument ('c') will be interpreted as the country code. It is
\r
832 * recommended that this constructor only be used to ease porting,
\r
833 * and that clients instead use the single-argument constructor
\r
834 * when constructing a ULocale from a localeID.
\r
835 * @param a first component of the locale id
\r
836 * @param b second component of the locale id
\r
837 * @param c third component of the locale id
\r
838 * @see #ULocale(String)
\r
841 public ULocale(String a, String b, String c) {
\r
842 localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
\r
846 * Create a ULocale from the id by first canonicalizing the id.
\r
847 * @param nonCanonicalID the locale id to canonicalize
\r
848 * @return the locale created from the canonical version of the ID.
\r
851 public static ULocale createCanonical(String nonCanonicalID) {
\r
852 return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
\r
855 private static String lscvToID(String lang, String script, String country, String variant) {
\r
856 StringBuffer buf = new StringBuffer();
\r
858 if (lang != null && lang.length() > 0) {
\r
861 if (script != null && script.length() > 0) {
\r
862 buf.append(UNDERSCORE);
\r
863 buf.append(script);
\r
865 if (country != null && country.length() > 0) {
\r
866 buf.append(UNDERSCORE);
\r
867 buf.append(country);
\r
869 if (variant != null && variant.length() > 0) {
\r
870 if (country == null || country.length() == 0) {
\r
871 buf.append(UNDERSCORE);
\r
873 buf.append(UNDERSCORE);
\r
874 buf.append(variant);
\r
876 return buf.toString();
\r
880 * Convert this ULocale object to a {@link java.util.Locale}.
\r
881 * @return a JDK locale that either exactly represents this object
\r
882 * or is the closest approximation.
\r
885 public Locale toLocale() {
\r
886 if (locale == null) {
\r
887 IDParser p = new IDParser(localeID);
\r
888 String base = p.getBaseName();
\r
889 for (int i = 0; i < _javaLocaleMap.length; i++) {
\r
890 if (base.equals(_javaLocaleMap[i][1]) || base.equals(_javaLocaleMap[i][4])) {
\r
891 if (_javaLocaleMap[i][2] != null) {
\r
892 String val = p.getKeywordValue(_javaLocaleMap[i][2]);
\r
893 if (val != null && val.equals(_javaLocaleMap[i][3])) {
\r
894 p = new IDParser(_javaLocaleMap[i][0]);
\r
898 p = new IDParser(_javaLocaleMap[i][0]);
\r
903 String[] names = p.getLanguageScriptCountryVariant();
\r
904 locale = new Locale(names[0], names[2], names[3]);
\r
909 private static Locale toLocale(String localeID) {
\r
910 IDParser p = new IDParser(localeID);
\r
911 String base = p.getBaseName();
\r
912 for (int i = 0; i < _javaLocaleMap.length; i++) {
\r
913 if (base.equals(_javaLocaleMap[i][1]) || base.equals(_javaLocaleMap[i][4])) {
\r
914 if (_javaLocaleMap[i][2] != null) {
\r
915 String val = p.getKeywordValue(_javaLocaleMap[i][2]);
\r
916 if (val != null && val.equals(_javaLocaleMap[i][3])) {
\r
917 p = new IDParser(_javaLocaleMap[i][0]);
\r
921 p = new IDParser(_javaLocaleMap[i][0]);
\r
926 String[] names = p.getLanguageScriptCountryVariant();
\r
927 return new Locale(names[0], names[2], names[3]);
\r
931 private static SoftReference nameCacheRef = new SoftReference(Collections.synchronizedMap(new HashMap()));
\r
933 * Keep our own default ULocale.
\r
935 private static Locale defaultLocale = Locale.getDefault();
\r
936 private static ULocale defaultULocale = new ULocale(defaultLocale);
\r
939 * Returns the current default ULocale.
\r
942 public static ULocale getDefault() {
\r
943 synchronized (ULocale.class) {
\r
944 Locale currentDefault = Locale.getDefault();
\r
945 if (defaultLocale != currentDefault) {
\r
946 defaultLocale = currentDefault;
\r
947 defaultULocale = new ULocale(defaultLocale);
\r
949 return defaultULocale;
\r
954 * Sets the default ULocale. This also sets the default Locale.
\r
955 * If the caller does not have write permission to the
\r
956 * user.language property, a security exception will be thrown,
\r
957 * and the default ULocale will remain unchanged.
\r
958 * @param newLocale the new default locale
\r
959 * @throws SecurityException
\r
960 * if a security manager exists and its
\r
961 * <code>checkPermission</code> method doesn't allow the operation.
\r
962 * @throws NullPointerException if <code>newLocale</code> is null
\r
963 * @see SecurityManager#checkPermission(java.security.Permission)
\r
964 * @see java.util.PropertyPermission
\r
967 public static synchronized void setDefault(ULocale newLocale){
\r
968 Locale.setDefault(newLocale.toLocale());
\r
969 defaultULocale = newLocale;
\r
973 * This is for compatibility with Locale-- in actuality, since ULocale is
\r
974 * immutable, there is no reason to clone it, so this API returns 'this'.
\r
977 public Object clone() {
\r
982 * Returns the hashCode.
\r
985 public int hashCode() {
\r
986 return localeID.hashCode();
\r
990 * Returns true if the other object is another ULocale with the
\r
991 * same full name, or is a String localeID that matches the full name.
\r
992 * Note that since names are not canonicalized, two ULocales that
\r
993 * function identically might not compare equal.
\r
995 * @return true if this Locale is equal to the specified object.
\r
998 public boolean equals(Object obj) {
\r
1002 if (obj instanceof String) {
\r
1003 return localeID.equals((String)obj);
\r
1005 if (obj instanceof ULocale) {
\r
1006 return localeID.equals(((ULocale)obj).localeID);
\r
1011 private static ULocale[] ulocales;
\r
1014 * Returns a list of all installed locales.
\r
1017 public static ULocale[] getAvailableLocales() {
\r
1018 // return ICUResourceBundle.getAvailableULocales();
\r
1019 if (ulocales == null) {
\r
1020 Locale[] locales = Locale.getAvailableLocales();
\r
1021 ULocale[] ul = new ULocale[locales.length];
\r
1022 for (int i = 0; i < locales.length; ++i) {
\r
1023 ul[i] = ULocale.forLocale(locales[i]);
\r
1027 return (ULocale[])ulocales.clone();
\r
1031 * Returns a list of all 2-letter country codes defined in ISO 3166.
\r
1032 * Can be used to create Locales.
\r
1035 public static String[] getISOCountries() {
\r
1036 initCountryTables();
\r
1037 return (String[])_countries.clone();
\r
1041 * Returns a list of all 2-letter language codes defined in ISO 639.
\r
1042 * Can be used to create Locales.
\r
1043 * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
\r
1044 * The list this function returns includes both the new and the old codes for the
\r
1045 * languages whose codes have changed.]
\r
1048 public static String[] getISOLanguages() {
\r
1049 initLanguageTables();
\r
1050 return (String[])_languages.clone();
\r
1054 * Returns the language code for this locale, which will either be the empty string
\r
1055 * or a lowercase ISO 639 code.
\r
1056 * @see #getDisplayLanguage()
\r
1057 * @see #getDisplayLanguage(ULocale)
\r
1060 public String getLanguage() {
\r
1061 return getLanguage(localeID);
\r
1065 * Returns the language code for the locale ID,
\r
1066 * which will either be the empty string
\r
1067 * or a lowercase ISO 639 code.
\r
1068 * @see #getDisplayLanguage()
\r
1069 * @see #getDisplayLanguage(ULocale)
\r
1072 public static String getLanguage(String localeID) {
\r
1073 return new IDParser(localeID).getLanguage();
\r
1077 * Returns the script code for this locale, which might be the empty string.
\r
1078 * @see #getDisplayScript()
\r
1079 * @see #getDisplayScript(ULocale)
\r
1082 public String getScript() {
\r
1083 return getScript(localeID);
\r
1087 * Returns the script code for the specified locale, which might be the empty string.
\r
1088 * @see #getDisplayScript()
\r
1089 * @see #getDisplayScript(ULocale)
\r
1092 public static String getScript(String localeID) {
\r
1093 return new IDParser(localeID).getScript();
\r
1097 * Returns the country/region code for this locale, which will either be the empty string
\r
1098 * or an uppercase ISO 3166 2-letter code.
\r
1099 * @see #getDisplayCountry()
\r
1100 * @see #getDisplayCountry(ULocale)
\r
1103 public String getCountry() {
\r
1104 return getCountry(localeID);
\r
1108 * Returns the country/region code for this locale, which will either be the empty string
\r
1109 * or an uppercase ISO 3166 2-letter code.
\r
1111 * @see #getDisplayCountry()
\r
1112 * @see #getDisplayCountry(ULocale)
\r
1115 public static String getCountry(String localeID) {
\r
1116 return new IDParser(localeID).getCountry();
\r
1120 * Returns the variant code for this locale, which might be the empty string.
\r
1121 * @see #getDisplayVariant()
\r
1122 * @see #getDisplayVariant(ULocale)
\r
1125 public String getVariant() {
\r
1126 return getVariant(localeID);
\r
1130 * Returns the variant code for the specified locale, which might be the empty string.
\r
1131 * @see #getDisplayVariant()
\r
1132 * @see #getDisplayVariant(ULocale)
\r
1135 public static String getVariant(String localeID) {
\r
1136 return new IDParser(localeID).getVariant();
\r
1140 * Returns the fallback locale for the specified locale, which might be the empty string.
\r
1143 public static String getFallback(String localeID) {
\r
1144 return getFallbackString(getName(localeID));
\r
1148 * Returns the fallback locale for this locale. If this locale is root, returns null.
\r
1151 public ULocale getFallback() {
\r
1152 if (localeID.length() == 0 || localeID.charAt(0) == '@') {
\r
1155 return new ULocale(getFallbackString(localeID), (Locale)null);
\r
1159 * Return the given (canonical) locale id minus the last part before the tags.
\r
1161 private static String getFallbackString(String fallback) {
\r
1162 int limit = fallback.indexOf('@');
\r
1163 if (limit == -1) {
\r
1164 limit = fallback.length();
\r
1166 int start = fallback.lastIndexOf('_', limit);
\r
1167 if (start == -1) {
\r
1170 return fallback.substring(0, start) + fallback.substring(limit);
\r
1174 * Returns the (normalized) base name for this locale.
\r
1175 * @return the base name as a String.
\r
1178 public String getBaseName() {
\r
1179 return getBaseName(localeID);
\r
1183 * Returns the (normalized) base name for the specified locale.
\r
1184 * @param localeID the locale ID as a string
\r
1185 * @return the base name as a String.
\r
1188 public static String getBaseName(String localeID){
\r
1189 if (localeID.indexOf('@') == -1) {
\r
1192 return new IDParser(localeID).getBaseName();
\r
1196 * Returns the (normalized) full name for this locale.
\r
1198 * @return String the full name of the localeID
\r
1201 public String getName() {
\r
1202 return localeID; // always normalized
\r
1206 * Returns the (normalized) full name for the specified locale.
\r
1208 * @param localeID the localeID as a string
\r
1209 * @return String the full name of the localeID
\r
1212 public static String getName(String localeID){
\r
1213 Map cache = (Map)nameCacheRef.get();
\r
1214 if (cache == null) {
\r
1215 cache = Collections.synchronizedMap(new HashMap());
\r
1216 nameCacheRef = new SoftReference(cache);
\r
1218 String name = (String)cache.get(localeID);
\r
1219 if (name == null) {
\r
1220 name = new IDParser(localeID).getName();
\r
1221 cache.put(localeID, name);
\r
1227 * Returns a string representation of this object.
\r
1230 public String toString() {
\r
1235 * Returns an iterator over keywords for this locale. If there
\r
1236 * are no keywords, returns null.
\r
1237 * @return iterator over keywords, or null if there are no keywords.
\r
1240 public Iterator getKeywords() {
\r
1241 return getKeywords(localeID);
\r
1245 * Returns an iterator over keywords for the specified locale. If there
\r
1246 * are no keywords, returns null.
\r
1247 * @return an iterator over the keywords in the specified locale, or null
\r
1248 * if there are no keywords.
\r
1251 public static Iterator getKeywords(String localeID){
\r
1252 return new IDParser(localeID).getKeywords();
\r
1256 * Returns the value for a keyword in this locale. If the keyword is not defined, returns null.
\r
1257 * @param keywordName name of the keyword whose value is desired. Case insensitive.
\r
1258 * @return the value of the keyword, or null.
\r
1261 public String getKeywordValue(String keywordName){
\r
1262 return getKeywordValue(localeID, keywordName);
\r
1266 * Returns the value for a keyword in the specified locale. If the keyword is not defined, returns null.
\r
1267 * The locale name does not need to be normalized.
\r
1268 * @param keywordName name of the keyword whose value is desired. Case insensitive.
\r
1269 * @return String the value of the keyword as a string
\r
1272 public static String getKeywordValue(String localeID, String keywordName) {
\r
1273 return new IDParser(localeID).getKeywordValue(keywordName);
\r
1277 * Utility class to parse and normalize locale ids (including POSIX style)
\r
1279 private static final class IDParser {
\r
1280 private char[] id;
\r
1281 private int index;
\r
1282 private char[] buffer;
\r
1284 // um, don't handle POSIX ids unless we request it. why not? well... because.
\r
1285 private boolean canonicalize;
\r
1286 private boolean hadCountry;
\r
1288 // used when canonicalizing
\r
1293 * Parsing constants.
\r
1295 private static final char KEYWORD_SEPARATOR = '@';
\r
1296 private static final char HYPHEN = '-';
\r
1297 private static final char KEYWORD_ASSIGN = '=';
\r
1298 private static final char COMMA = ',';
\r
1299 private static final char ITEM_SEPARATOR = ';';
\r
1300 private static final char DOT = '.';
\r
1302 private IDParser(String localeID) {
\r
1303 this(localeID, false);
\r
1306 private IDParser(String localeID, boolean canonicalize) {
\r
1307 id = localeID.toCharArray();
\r
1309 buffer = new char[id.length + 5];
\r
1311 this.canonicalize = canonicalize;
\r
1314 private void reset() {
\r
1318 // utilities for working on text in the buffer
\r
1321 * Append c to the buffer.
\r
1323 private void append(char c) {
\r
1327 catch (IndexOutOfBoundsException e) {
\r
1328 if (buffer.length > 512) {
\r
1329 // something is seriously wrong, let this go
\r
1332 char[] nbuffer = new char[buffer.length * 2];
\r
1333 System.arraycopy(buffer, 0, nbuffer, 0, buffer.length);
\r
1334 nbuffer[blen] = c;
\r
1340 private void addSeparator() {
\r
1341 append(UNDERSCORE);
\r
1345 * Returns the text in the buffer from start to blen as a String.
\r
1347 private String getString(int start) {
\r
1348 if (start == blen) {
\r
1349 return EMPTY_STRING;
\r
1351 return new String(buffer, start, blen-start);
\r
1355 * Set the length of the buffer to pos, then append the string.
\r
1357 private void set(int pos, String s) {
\r
1358 this.blen = pos; // no safety
\r
1363 * Append the string to the buffer.
\r
1365 private void append(String s) {
\r
1366 for (int i = 0; i < s.length(); ++i) {
\r
1367 append(s.charAt(i));
\r
1371 // utilities for parsing text out of the id
\r
1374 * Character to indicate no more text is available in the id.
\r
1376 private static final char DONE = '\uffff';
\r
1379 * Returns the character at index in the id, and advance index. The returned character
\r
1380 * is DONE if index was at the limit of the buffer. The index is advanced regardless
\r
1381 * so that decrementing the index will always 'unget' the last character returned.
\r
1383 private char next() {
\r
1384 if (index == id.length) {
\r
1389 return id[index++];
\r
1393 * Advance index until the next terminator or id separator, and leave it there.
\r
1395 private void skipUntilTerminatorOrIDSeparator() {
\r
1396 while (!isTerminatorOrIDSeparator(next())) {
\r
1402 * Returns true if the character at index in the id is a terminator.
\r
1404 private boolean atTerminator() {
\r
1405 return index >= id.length || isTerminator(id[index]);
\r
1409 * Returns true if the character is an id separator (underscore or hyphen).
\r
1411 /* private boolean isIDSeparator(char c) {
\r
1412 return c == UNDERSCORE || c == HYPHEN;
\r
1416 * Returns true if the character is a terminator (keyword separator, dot, or DONE).
\r
1417 * Dot is a terminator because of the POSIX form, where dot precedes the codepage.
\r
1419 private boolean isTerminator(char c) {
\r
1420 // always terminate at DOT, even if not handling POSIX. It's an error...
\r
1421 return c == KEYWORD_SEPARATOR || c == DONE || c == DOT;
\r
1425 * Returns true if the character is a terminator or id separator.
\r
1427 private boolean isTerminatorOrIDSeparator(char c) {
\r
1428 return c == KEYWORD_SEPARATOR || c == UNDERSCORE || c == HYPHEN ||
\r
1429 c == DONE || c == DOT;
\r
1433 * Returns true if the start of the buffer has an experimental or private language
\r
1434 * prefix, the pattern '[ixIX][-_].' shows the syntax checked.
\r
1436 private boolean haveExperimentalLanguagePrefix() {
\r
1437 if (id.length > 2) {
\r
1439 if (c == HYPHEN || c == UNDERSCORE) {
\r
1441 return c == 'x' || c == 'X' || c == 'i' || c == 'I';
\r
1448 * Returns true if a value separator occurs at or after index.
\r
1450 private boolean haveKeywordAssign() {
\r
1451 // assume it is safe to start from index
\r
1452 for (int i = index; i < id.length; ++i) {
\r
1453 if (id[i] == KEYWORD_ASSIGN) {
\r
1461 * Advance index past language, and accumulate normalized language code in buffer.
\r
1462 * Index must be at 0 when this is called. Index is left at a terminator or id
\r
1463 * separator. Returns the start of the language code in the buffer.
\r
1465 private int parseLanguage() {
\r
1466 if (haveExperimentalLanguagePrefix()) {
\r
1467 append(Character.toLowerCase(id[0]));
\r
1473 while(!isTerminatorOrIDSeparator(c = next())) {
\r
1474 append(Character.toLowerCase(c));
\r
1479 initLanguageTables();
\r
1481 /* convert 3 character code to 2 character code if possible *CWB*/
\r
1482 String lang = getString(0);
\r
1483 int offset = findIndex(_languages3, lang);
\r
1484 if (offset >= 0) {
\r
1485 set(0, _languages[offset]);
\r
1487 offset = findIndex(_obsoleteLanguages3, lang);
\r
1488 if (offset >= 0) {
\r
1489 set(0, _obsoleteLanguages[offset]);
\r
1498 * Advance index past language. Index must be at 0 when this is called. Index
\r
1499 * is left at a terminator or id separator.
\r
1501 private void skipLanguage() {
\r
1502 if (haveExperimentalLanguagePrefix()) {
\r
1505 skipUntilTerminatorOrIDSeparator();
\r
1509 * Advance index past script, and accumulate normalized script in buffer.
\r
1510 * Index must be immediately after the language.
\r
1511 * If the item at this position is not a script (is not four characters
\r
1512 * long) leave index and buffer unchanged. Otherwise index is left at
\r
1513 * a terminator or id separator. Returns the start of the script code
\r
1514 * in the buffer (this may be equal to the buffer length, if there is no
\r
1517 private int parseScript() {
\r
1518 if (!atTerminator()) {
\r
1519 int oldIndex = index; // save original index
\r
1522 int oldBlen = blen; // get before append hyphen, if we truncate everything is undone
\r
1524 while(!isTerminatorOrIDSeparator(c = next())) {
\r
1525 if (blen == oldBlen) { // first pass
\r
1527 append(Character.toUpperCase(c));
\r
1529 append(Character.toLowerCase(c));
\r
1534 /* If it's not exactly 4 characters long, then it's not a script. */
\r
1535 if (index - oldIndex != 5) { // +1 to account for separator
\r
1539 oldBlen++; // index past hyphen, for clients who want to extract just the script
\r
1548 * Advance index past script.
\r
1549 * Index must be immediately after the language and IDSeparator.
\r
1550 * If the item at this position is not a script (is not four characters
\r
1551 * long) leave index. Otherwise index is left at a terminator or
\r
1554 private void skipScript() {
\r
1555 if (!atTerminator()) {
\r
1556 int oldIndex = index;
\r
1559 skipUntilTerminatorOrIDSeparator();
\r
1560 if (index - oldIndex != 5) { // +1 to account for separator
\r
1567 * Advance index past country, and accumulate normalized country in buffer.
\r
1568 * Index must be immediately after the script (if there is one, else language)
\r
1569 * and IDSeparator. Return the start of the country code in the buffer.
\r
1571 private int parseCountry() {
\r
1572 if (!atTerminator()) {
\r
1573 int oldIndex = index;
\r
1576 int oldBlen = blen;
\r
1578 while (!isTerminatorOrIDSeparator(c = next())) {
\r
1579 if (oldBlen == blen) { // first, add hyphen
\r
1580 hadCountry = true; // we have a country, let variant parsing know
\r
1582 ++oldBlen; // increment past hyphen
\r
1584 append(Character.toUpperCase(c));
\r
1588 int charsAppended = blen - oldBlen;
\r
1590 if (charsAppended == 0) {
\r
1593 else if (charsAppended < 2 || charsAppended > 3) {
\r
1594 // It's not a country, so return index and blen to
\r
1595 // their previous values.
\r
1599 hadCountry = false;
\r
1601 else if (charsAppended == 3) {
\r
1602 initCountryTables();
\r
1604 /* convert 3 character code to 2 character code if possible *CWB*/
\r
1605 int offset = findIndex(_countries3, getString(oldBlen));
\r
1606 if (offset >= 0) {
\r
1607 set(oldBlen, _countries[offset]);
\r
1609 offset = findIndex(_obsoleteCountries3, getString(oldBlen));
\r
1610 if (offset >= 0) {
\r
1611 set(oldBlen, _obsoleteCountries[offset]);
\r
1623 * Advance index past country.
\r
1624 * Index must be immediately after the script (if there is one, else language)
\r
1625 * and IDSeparator.
\r
1627 private void skipCountry() {
\r
1628 if (!atTerminator()) {
\r
1631 * Save the index point after the separator, since the format
\r
1632 * requires two separators if the country is not present.
\r
1634 int oldIndex = index;
\r
1636 skipUntilTerminatorOrIDSeparator();
\r
1637 int charsSkipped = index - oldIndex;
\r
1638 if (charsSkipped < 2 || charsSkipped > 3) {
\r
1645 * Advance index past variant, and accumulate normalized variant in buffer. This ignores
\r
1646 * the codepage information from POSIX ids. Index must be immediately after the country
\r
1647 * or script. Index is left at the keyword separator or at the end of the text. Return
\r
1648 * the start of the variant code in the buffer.
\r
1650 * In standard form, we can have the following forms:
\r
1656 * This also handles POSIX ids, which can have the following forms (pppp is code page id):
\r
1657 * ll_CC.pppp --> ll_CC
\r
1658 * ll_CC.pppp@VVVV --> ll_CC_VVVV
\r
1659 * ll_CC@VVVV --> ll_CC_VVVV
\r
1661 * We identify this use of '@' in POSIX ids by looking for an '=' following
\r
1662 * the '@'. If there is one, we consider '@' to start a keyword list, instead of
\r
1663 * being part of a POSIX id.
\r
1665 * Note: since it was decided that we want an option to not handle POSIX ids, this
\r
1666 * becomes a bit more complex.
\r
1668 private int parseVariant() {
\r
1669 int oldBlen = blen;
\r
1671 boolean start = true;
\r
1672 boolean needSeparator = true;
\r
1673 boolean skipping = false;
\r
1675 while ((c = next()) != DONE) {
\r
1679 } else if (c == KEYWORD_SEPARATOR) {
\r
1680 if (haveKeywordAssign()) {
\r
1685 needSeparator = true; // add another underscore if we have more text
\r
1686 } else if (start) {
\r
1688 } else if (!skipping) {
\r
1689 if (needSeparator) {
\r
1690 boolean incOldBlen = blen == oldBlen; // need to skip separators
\r
1691 needSeparator = false;
\r
1692 if (incOldBlen && !hadCountry) { // no country, we'll need two
\r
1694 ++oldBlen; // for sure
\r
1697 if (incOldBlen) { // only for the first separator
\r
1701 c = Character.toUpperCase(c);
\r
1702 if (c == HYPHEN || c == COMMA) {
\r
1713 // no need for skipvariant, to get the keywords we'll just scan directly for
\r
1714 // the keyword separator
\r
1717 * Returns the normalized language id, or the empty string.
\r
1719 public String getLanguage() {
\r
1721 return getString(parseLanguage());
\r
1725 * Returns the normalized script id, or the empty string.
\r
1727 public String getScript() {
\r
1730 return getString(parseScript());
\r
1734 * return the normalized country id, or the empty string.
\r
1736 public String getCountry() {
\r
1740 return getString(parseCountry());
\r
1744 * Returns the normalized variant id, or the empty string.
\r
1746 public String getVariant() {
\r
1751 return getString(parseVariant());
\r
1755 * Returns the language, script, country, and variant as separate strings.
\r
1757 public String[] getLanguageScriptCountryVariant() {
\r
1759 return new String[] {
\r
1760 getString(parseLanguage()),
\r
1761 getString(parseScript()),
\r
1762 getString(parseCountry()),
\r
1763 getString(parseVariant())
\r
1767 public void setBaseName(String baseName) {
\r
1768 this.baseName = baseName;
\r
1771 public void parseBaseName() {
\r
1772 if (baseName != null) {
\r
1781 // catch unwanted trailing underscore after country if there was no variant
\r
1782 if (blen > 1 && buffer[blen-1] == UNDERSCORE) {
\r
1789 * Returns the normalized base form of the locale id. The base
\r
1790 * form does not include keywords.
\r
1792 public String getBaseName() {
\r
1793 if (baseName != null) {
\r
1797 return getString(0);
\r
1801 * Returns the normalized full form of the locale id. The full
\r
1802 * form includes keywords if they are present.
\r
1804 public String getName() {
\r
1807 return getString(0);
\r
1810 // keyword utilities
\r
1813 * If we have keywords, advance index to the start of the keywords and return true,
\r
1814 * otherwise return false.
\r
1816 private boolean setToKeywordStart() {
\r
1817 for (int i = index; i < id.length; ++i) {
\r
1818 if (id[i] == KEYWORD_SEPARATOR) {
\r
1819 if (canonicalize) {
\r
1820 for (int j = ++i; j < id.length; ++j) { // increment i past separator for return
\r
1821 if (id[j] == KEYWORD_ASSIGN) {
\r
1827 if (++i < id.length) {
\r
1838 private static boolean isDoneOrKeywordAssign(char c) {
\r
1839 return c == DONE || c == KEYWORD_ASSIGN;
\r
1842 private static boolean isDoneOrItemSeparator(char c) {
\r
1843 return c == DONE || c == ITEM_SEPARATOR;
\r
1846 private String getKeyword() {
\r
1847 int start = index;
\r
1848 while (!isDoneOrKeywordAssign(next())) {
\r
1851 return new String(id, start, index-start).trim().toLowerCase();
\r
1854 private String getValue() {
\r
1855 int start = index;
\r
1856 while (!isDoneOrItemSeparator(next())) {
\r
1859 return new String(id, start, index-start).trim(); // leave case alone
\r
1862 private Comparator getKeyComparator() {
\r
1863 final Comparator comp = new Comparator() {
\r
1864 public int compare(Object lhs, Object rhs) {
\r
1865 return ((String)lhs).compareTo((String)rhs);
\r
1872 * Returns a map of the keywords and values, or null if there are none.
\r
1874 private Map getKeywordMap() {
\r
1875 if (keywords == null) {
\r
1877 if (setToKeywordStart()) {
\r
1878 // trim spaces and convert to lower case, both keywords and values.
\r
1880 String key = getKeyword();
\r
1881 if (key.length() == 0) {
\r
1885 if (c != KEYWORD_ASSIGN) {
\r
1886 // throw new IllegalArgumentException("key '" + key + "' missing a value.");
\r
1893 String value = getValue();
\r
1894 if (value.length() == 0) {
\r
1895 // throw new IllegalArgumentException("key '" + key + "' missing a value.");
\r
1899 m = new TreeMap(getKeyComparator());
\r
1900 } else if (m.containsKey(key)) {
\r
1901 // throw new IllegalArgumentException("key '" + key + "' already has a value.");
\r
1904 m.put(key, value);
\r
1905 } while (next() == ITEM_SEPARATOR);
\r
1907 keywords = m != null ? m : Collections.EMPTY_MAP;
\r
1914 * Parse the keywords and return start of the string in the buffer.
\r
1916 private int parseKeywords() {
\r
1917 int oldBlen = blen;
\r
1918 Map m = getKeywordMap();
\r
1919 if (!m.isEmpty()) {
\r
1920 Iterator iter = m.entrySet().iterator();
\r
1921 boolean first = true;
\r
1922 while (iter.hasNext()) {
\r
1923 append(first ? KEYWORD_SEPARATOR : ITEM_SEPARATOR);
\r
1925 Map.Entry e = (Map.Entry)iter.next();
\r
1926 append((String)e.getKey());
\r
1927 append(KEYWORD_ASSIGN);
\r
1928 append((String)e.getValue());
\r
1930 if (blen != oldBlen) {
\r
1938 * Returns an iterator over the keywords, or null if we have an empty map.
\r
1940 public Iterator getKeywords() {
\r
1941 Map m = getKeywordMap();
\r
1942 return m.isEmpty() ? null : m.keySet().iterator();
\r
1946 * Returns the value for the named keyword, or null if the keyword is not
\r
1949 public String getKeywordValue(String keywordName) {
\r
1950 Map m = getKeywordMap();
\r
1951 return m.isEmpty() ? null : (String)m.get(keywordName.trim().toLowerCase());
\r
1955 * Set the keyword value only if it is not already set to something else.
\r
1957 public void defaultKeywordValue(String keywordName, String value) {
\r
1958 setKeywordValue(keywordName, value, false);
\r
1962 * Set the value for the named keyword, or unset it if value is null. If
\r
1963 * keywordName itself is null, unset all keywords. If keywordName is not null,
\r
1964 * value must not be null.
\r
1966 public void setKeywordValue(String keywordName, String value) {
\r
1967 setKeywordValue(keywordName, value, true);
\r
1971 * Set the value for the named keyword, or unset it if value is null. If
\r
1972 * keywordName itself is null, unset all keywords. If keywordName is not null,
\r
1973 * value must not be null. If reset is true, ignore any previous value for
\r
1974 * the keyword, otherwise do not change the keyword (including removal of
\r
1975 * one or all keywords).
\r
1977 private void setKeywordValue(String keywordName, String value, boolean reset) {
\r
1978 if (keywordName == null) {
\r
1980 // force new map, ignore value
\r
1981 keywords = Collections.EMPTY_MAP;
\r
1984 keywordName = keywordName.trim().toLowerCase();
\r
1985 if (keywordName.length() == 0) {
\r
1986 throw new IllegalArgumentException("keyword must not be empty");
\r
1988 if (value != null) {
\r
1989 value = value.trim();
\r
1990 if (value.length() == 0) {
\r
1991 throw new IllegalArgumentException("value must not be empty");
\r
1994 Map m = getKeywordMap();
\r
1995 if (m.isEmpty()) { // it is EMPTY_MAP
\r
1996 if (value != null) {
\r
1998 keywords = new TreeMap(getKeyComparator());
\r
1999 keywords.put(keywordName, value.trim());
\r
2002 if (reset || !m.containsKey(keywordName)) {
\r
2003 if (value != null) {
\r
2004 m.put(keywordName, value);
\r
2006 m.remove(keywordName);
\r
2007 if (m.isEmpty()) {
\r
2009 keywords = Collections.EMPTY_MAP;
\r
2019 * linear search of the string array. the arrays are unfortunately ordered by the
\r
2020 * two-letter target code, not the three-letter search code, which seems backwards.
\r
2022 private static int findIndex(String[] array, String target){
\r
2023 for (int i = 0; i < array.length; i++) {
\r
2024 if (target.equals(array[i])) {
\r
2032 * Returns the canonical name for the specified locale ID. This is used to convert POSIX
\r
2033 * and other grandfathered IDs to standard ICU form.
\r
2034 * @param localeID the locale id
\r
2035 * @return the canonicalized id
\r
2038 public static String canonicalize(String localeID){
\r
2039 IDParser parser = new IDParser(localeID, true);
\r
2040 String baseName = parser.getBaseName();
\r
2041 boolean foundVariant = false;
\r
2043 // formerly, we always set to en_US_POSIX if the basename was empty, but
\r
2044 // now we require that the entire id be empty, so that "@foo=bar"
\r
2045 // will pass through unchanged.
\r
2046 // {dlf} I'd rather keep "" unchanged.
\r
2047 if (localeID.equals("")) {
\r
2049 // return "en_US_POSIX";
\r
2052 // we have an ID in the form xx_Yyyy_ZZ_KKKKK
\r
2054 initCANONICALIZE_MAP();
\r
2056 /* convert the variants to appropriate ID */
\r
2057 for (int i = 0; i < variantsToKeywords.length; i++) {
\r
2058 String[] vals = variantsToKeywords[i];
\r
2059 int idx = baseName.lastIndexOf("_" + vals[0]);
\r
2061 foundVariant = true;
\r
2063 baseName = baseName.substring(0, idx);
\r
2064 if (baseName.endsWith("_")) {
\r
2065 baseName = baseName.substring(0, --idx);
\r
2067 parser.setBaseName(baseName);
\r
2068 parser.defaultKeywordValue(vals[1], vals[2]);
\r
2073 /* See if this is an already known locale */
\r
2074 for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
\r
2075 if (CANONICALIZE_MAP[i][0].equals(baseName)) {
\r
2076 foundVariant = true;
\r
2078 String[] vals = CANONICALIZE_MAP[i];
\r
2079 parser.setBaseName(vals[1]);
\r
2080 if (vals[2] != null) {
\r
2081 parser.defaultKeywordValue(vals[2], vals[3]);
\r
2087 /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
\r
2088 if (!foundVariant) {
\r
2089 if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
\r
2090 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
\r
2094 return parser.getName();
\r
2098 * Given a keyword and a value, return a new locale with an updated
\r
2099 * keyword and value. If keyword is null, this removes all keywords from the locale id.
\r
2100 * Otherwise, if the value is null, this removes the value for this keyword from the
\r
2101 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
\r
2102 * The keyword and value must not be empty.
\r
2103 * @param keyword the keyword to add/remove, or null to remove all keywords.
\r
2104 * @param value the value to add/set, or null to remove this particular keyword.
\r
2105 * @return the updated locale
\r
2108 public ULocale setKeywordValue(String keyword, String value) {
\r
2109 return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
\r
2113 * Given a locale id, a keyword, and a value, return a new locale id with an updated
\r
2114 * keyword and value. If keyword is null, this removes all keywords from the locale id.
\r
2115 * Otherwise, if the value is null, this removes the value for this keyword from the
\r
2116 * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
\r
2117 * The keyword and value must not be empty.
\r
2118 * @param localeID the locale id to modify
\r
2119 * @param keyword the keyword to add/remove, or null to remove all keywords.
\r
2120 * @param value the value to add/set, or null to remove this particular keyword.
\r
2121 * @return the updated locale id
\r
2124 public static String setKeywordValue(String localeID, String keyword, String value) {
\r
2125 IDParser parser = new IDParser(localeID);
\r
2126 parser.setKeywordValue(keyword, value);
\r
2127 return parser.getName();
\r
2131 * Given a locale id, a keyword, and a value, return a new locale id with an updated
\r
2132 * keyword and value, if the keyword does not already have a value. The keyword and
\r
2133 * value must not be null or empty.
\r
2134 * @param localeID the locale id to modify
\r
2135 * @param keyword the keyword to add, if not already present
\r
2136 * @param value the value to add, if not already present
\r
2137 * @return the updated locale id
\r
2140 /* private static String defaultKeywordValue(String localeID, String keyword, String value) {
\r
2141 IDParser parser = new IDParser(localeID);
\r
2142 parser.defaultKeywordValue(keyword, value);
\r
2143 return parser.getName();
\r
2147 * Returns a three-letter abbreviation for this locale's language. If the locale
\r
2148 * doesn't specify a language, returns the empty string. Otherwise, returns
\r
2149 * a lowercase ISO 639-2/T language code.
\r
2150 * The ISO 639-2 language codes can be found on-line at
\r
2151 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
\r
2152 * @exception MissingResourceException Throws MissingResourceException if the
\r
2153 * three-letter language abbreviation is not available for this locale.
\r
2156 public String getISO3Language(){
\r
2157 return getISO3Language(localeID);
\r
2161 * Returns a three-letter abbreviation for this locale's language. If the locale
\r
2162 * doesn't specify a language, returns the empty string. Otherwise, returns
\r
2163 * a lowercase ISO 639-2/T language code.
\r
2164 * The ISO 639-2 language codes can be found on-line at
\r
2165 * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
\r
2166 * @exception MissingResourceException Throws MissingResourceException if the
\r
2167 * three-letter language abbreviation is not available for this locale.
\r
2170 public static String getISO3Language(String localeID){
\r
2171 initLanguageTables();
\r
2173 String language = getLanguage(localeID);
\r
2174 int offset = findIndex(_languages, language);
\r
2176 return _languages3[offset];
\r
2178 offset = findIndex(_obsoleteLanguages, language);
\r
2179 if (offset >= 0) {
\r
2180 return _obsoleteLanguages3[offset];
\r
2183 return EMPTY_STRING;
\r
2187 * Returns a three-letter abbreviation for this locale's country/region. If the locale
\r
2188 * doesn't specify a country, returns the empty string. Otherwise, returns
\r
2189 * an uppercase ISO 3166 3-letter country code.
\r
2190 * @exception MissingResourceException Throws MissingResourceException if the
\r
2191 * three-letter country abbreviation is not available for this locale.
\r
2194 public String getISO3Country(){
\r
2195 return getISO3Country(localeID);
\r
2198 * Returns a three-letter abbreviation for this locale's country/region. If the locale
\r
2199 * doesn't specify a country, returns the empty string. Otherwise, returns
\r
2200 * an uppercase ISO 3166 3-letter country code.
\r
2201 * @exception MissingResourceException Throws MissingResourceException if the
\r
2202 * three-letter country abbreviation is not available for this locale.
\r
2205 public static String getISO3Country(String localeID){
\r
2206 initCountryTables();
\r
2208 String country = getCountry(localeID);
\r
2209 int offset = findIndex(_countries, country);
\r
2211 return _countries3[offset];
\r
2213 offset = findIndex(_obsoleteCountries, country);
\r
2215 return _obsoleteCountries3[offset];
\r
2218 return EMPTY_STRING;
\r
2224 // * Utility to fetch locale display data from resource bundle tables.
\r
2226 // private static String getTableString(String tableName, String subtableName, String item, String displayLocaleID) {
\r
2227 // if (item.length() > 0) {
\r
2229 // ICUResourceBundle bundle = (ICUResourceBundle)UResourceBundle.
\r
2230 // getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, displayLocaleID);
\r
2231 // return getTableString(tableName, subtableName, item, bundle);
\r
2232 // } catch (Exception e) {
\r
2233 //// System.out.println("gtsu: " + e.getMessage());
\r
2240 // * Utility to fetch locale display data from resource bundle tables.
\r
2242 // private static String getTableString(String tableName, String subtableName, String item, ICUResourceBundle bundle) {
\r
2243 //// System.out.println("gts table: " + tableName +
\r
2244 //// " subtable: " + subtableName +
\r
2245 //// " item: " + item +
\r
2246 //// " bundle: " + bundle.getULocale());
\r
2249 // // special case currency
\r
2250 // if ("currency".equals(subtableName)) {
\r
2251 // ICUResourceBundle table = bundle.getWithFallback("Currencies");
\r
2252 // table = table.getWithFallback(item);
\r
2253 // return table.getString(1);
\r
2255 // ICUResourceBundle table = bundle.getWithFallback(tableName);
\r
2257 // if (subtableName != null) {
\r
2258 // table = table.getWithFallback(subtableName);
\r
2260 // return table.getStringWithFallback(item);
\r
2262 // catch (MissingResourceException e) {
\r
2264 // if(subtableName==null){
\r
2266 // // may be a deprecated code
\r
2267 // String currentName = null;
\r
2268 // if(tableName.equals("Countries")){
\r
2269 // currentName = getCurrentCountryID(item);
\r
2270 // }else if(tableName.equals("Languages")){
\r
2271 // currentName = getCurrentLanguageID(item);
\r
2273 // return table.getStringWithFallback(currentName);
\r
2274 // }catch (MissingResourceException ex){/* fall through*/}
\r
2277 // // still can't figure out ?.. try the fallback mechanism
\r
2278 // String fallbackLocale = table.getWithFallback("Fallback").getString();
\r
2279 // if (fallbackLocale.length() == 0) {
\r
2280 // fallbackLocale = "root";
\r
2282 //// System.out.println("bundle: " + bundle.getULocale() + " fallback: " + fallbackLocale);
\r
2283 // if(fallbackLocale.equals(table.getULocale().localeID)){
\r
2286 // bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
\r
2287 // fallbackLocale);
\r
2288 //// System.out.println("fallback from " + table.getULocale() + " to " + fallbackLocale +
\r
2289 //// ", got bundle " + bundle.getULocale());
\r
2294 // catch (Exception e) {
\r
2295 //// System.out.println("gtsi: " + e.getMessage());
\r
2301 * Returns this locale's language localized for display in the default locale.
\r
2302 * @return the localized language name.
\r
2305 public String getDisplayLanguage() {
\r
2306 return getDisplayLanguageInternal(localeID, getDefault().localeID);
\r
2310 * Returns this locale's language localized for display in the provided locale.
\r
2311 * @param displayLocale the locale in which to display the name.
\r
2312 * @return the localized language name.
\r
2315 public String getDisplayLanguage(ULocale displayLocale) {
\r
2316 return getDisplayLanguageInternal(localeID, displayLocale.localeID);
\r
2320 * Returns a locale's language localized for display in the provided locale.
\r
2321 * This is a cover for the ICU4C API.
\r
2322 * @param localeID the id of the locale whose language will be displayed
\r
2323 * @param displayLocaleID the id of the locale in which to display the name.
\r
2324 * @return the localized language name.
\r
2327 public static String getDisplayLanguage(String localeID, String displayLocaleID) {
\r
2328 return getDisplayLanguageInternal(localeID, getName(displayLocaleID));
\r
2332 * Returns a locale's language localized for display in the provided locale.
\r
2333 * This is a cover for the ICU4C API.
\r
2334 * @param localeID the id of the locale whose language will be displayed.
\r
2335 * @param displayLocale the locale in which to display the name.
\r
2336 * @return the localized language name.
\r
2339 public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
\r
2340 return getDisplayLanguageInternal(localeID, displayLocale.localeID);
\r
2343 static String getCurrentCountryID(String oldID){
\r
2344 initCountryTables();
\r
2345 int offset = findIndex(_deprecatedCountries, oldID);
\r
2346 if (offset >= 0) {
\r
2347 return _replacementCountries[offset];
\r
2351 static String getCurrentLanguageID(String oldID){
\r
2352 initLanguageTables();
\r
2353 int offset = findIndex(_obsoleteLanguages, oldID);
\r
2354 if (offset >= 0) {
\r
2355 return _replacementLanguages[offset];
\r
2361 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2362 private static String getDisplayLanguageInternal(String localeID, String displayLocaleID) {
\r
2363 // return getTableString("Languages", null, new IDParser(localeID).getLanguage(), displayLocaleID);
\r
2364 return toLocale(localeID).getDisplayLanguage(toLocale(displayLocaleID));
\r
2368 * Returns this locale's script localized for display in the default locale.
\r
2369 * @return the localized script name.
\r
2372 public String getDisplayScript() {
\r
2373 return getDisplayScriptInternal(localeID, getDefault().localeID);
\r
2377 * Returns this locale's script localized for display in the provided locale.
\r
2378 * @param displayLocale the locale in which to display the name.
\r
2379 * @return the localized script name.
\r
2382 public String getDisplayScript(ULocale displayLocale) {
\r
2383 return getDisplayScriptInternal(localeID, displayLocale.localeID);
\r
2387 * Returns a locale's script localized for display in the provided locale.
\r
2388 * This is a cover for the ICU4C API.
\r
2389 * @param localeID the id of the locale whose script will be displayed
\r
2390 * @param displayLocaleID the id of the locale in which to display the name.
\r
2391 * @return the localized script name.
\r
2394 public static String getDisplayScript(String localeID, String displayLocaleID) {
\r
2395 return getDisplayScriptInternal(localeID, getName(displayLocaleID));
\r
2399 * Returns a locale's script localized for display in the provided locale.
\r
2400 * @param localeID the id of the locale whose script will be displayed.
\r
2401 * @param displayLocale the locale in which to display the name.
\r
2402 * @return the localized script name.
\r
2405 public static String getDisplayScript(String localeID, ULocale displayLocale) {
\r
2406 return getDisplayScriptInternal(localeID, displayLocale.localeID);
\r
2409 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2410 private static String getDisplayScriptInternal(String localeID, String displayLocaleID) {
\r
2411 // return getTableString("Scripts", null, new IDParser(localeID).getScript(), displayLocaleID);
\r
2412 return new IDParser(localeID).getScript();
\r
2416 * Returns this locale's country localized for display in the default locale.
\r
2417 * @return the localized country name.
\r
2420 public String getDisplayCountry() {
\r
2421 return getDisplayCountryInternal(localeID, getDefault().localeID);
\r
2425 * Returns this locale's country localized for display in the provided locale.
\r
2426 * @param displayLocale the locale in which to display the name.
\r
2427 * @return the localized country name.
\r
2430 public String getDisplayCountry(ULocale displayLocale){
\r
2431 return getDisplayCountryInternal(localeID, displayLocale.localeID);
\r
2435 * Returns a locale's country localized for display in the provided locale.
\r
2436 * This is a cover for the ICU4C API.
\r
2437 * @param localeID the id of the locale whose country will be displayed
\r
2438 * @param displayLocaleID the id of the locale in which to display the name.
\r
2439 * @return the localized country name.
\r
2442 public static String getDisplayCountry(String localeID, String displayLocaleID) {
\r
2443 return getDisplayCountryInternal(localeID, getName(displayLocaleID));
\r
2447 * Returns a locale's country localized for display in the provided locale.
\r
2448 * This is a cover for the ICU4C API.
\r
2449 * @param localeID the id of the locale whose country will be displayed.
\r
2450 * @param displayLocale the locale in which to display the name.
\r
2451 * @return the localized country name.
\r
2454 public static String getDisplayCountry(String localeID, ULocale displayLocale) {
\r
2455 return getDisplayCountryInternal(localeID, displayLocale.localeID);
\r
2458 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2459 private static String getDisplayCountryInternal(String localeID, String displayLocaleID) {
\r
2460 // return getTableString("Countries", null, new IDParser(localeID).getCountry(), displayLocaleID);
\r
2461 return toLocale(localeID).getDisplayCountry(toLocale(displayLocaleID));
\r
2465 * Returns this locale's variant localized for display in the default locale.
\r
2466 * @return the localized variant name.
\r
2469 public String getDisplayVariant() {
\r
2470 return getDisplayVariantInternal(localeID, getDefault().localeID);
\r
2474 * Returns this locale's variant localized for display in the provided locale.
\r
2475 * @param displayLocale the locale in which to display the name.
\r
2476 * @return the localized variant name.
\r
2479 public String getDisplayVariant(ULocale displayLocale) {
\r
2480 return getDisplayVariantInternal(localeID, displayLocale.localeID);
\r
2484 * Returns a locale's variant localized for display in the provided locale.
\r
2485 * This is a cover for the ICU4C API.
\r
2486 * @param localeID the id of the locale whose variant will be displayed
\r
2487 * @param displayLocaleID the id of the locale in which to display the name.
\r
2488 * @return the localized variant name.
\r
2491 public static String getDisplayVariant(String localeID, String displayLocaleID){
\r
2492 return getDisplayVariantInternal(localeID, getName(displayLocaleID));
\r
2496 * Returns a locale's variant localized for display in the provided locale.
\r
2497 * This is a cover for the ICU4C API.
\r
2498 * @param localeID the id of the locale whose variant will be displayed.
\r
2499 * @param displayLocale the locale in which to display the name.
\r
2500 * @return the localized variant name.
\r
2503 public static String getDisplayVariant(String localeID, ULocale displayLocale) {
\r
2504 return getDisplayVariantInternal(localeID, displayLocale.localeID);
\r
2507 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2508 private static String getDisplayVariantInternal(String localeID, String displayLocaleID) {
\r
2509 // return getTableString("Variants", null, new IDParser(localeID).getVariant(), displayLocaleID);
\r
2510 return toLocale(localeID).getDisplayVariant(toLocale(displayLocaleID));
\r
2514 * Returns a keyword localized for display in the default locale.
\r
2515 * @param keyword the keyword to be displayed.
\r
2516 * @return the localized keyword name.
\r
2517 * @see #getKeywords()
\r
2520 public static String getDisplayKeyword(String keyword) {
\r
2521 return getDisplayKeywordInternal(keyword, getDefault().localeID);
\r
2525 * Returns a keyword localized for display in the specified locale.
\r
2526 * @param keyword the keyword to be displayed.
\r
2527 * @param displayLocaleID the id of the locale in which to display the keyword.
\r
2528 * @return the localized keyword name.
\r
2529 * @see #getKeywords(String)
\r
2532 public static String getDisplayKeyword(String keyword, String displayLocaleID) {
\r
2533 return getDisplayKeywordInternal(keyword, getName(displayLocaleID));
\r
2537 * Returns a keyword localized for display in the specified locale.
\r
2538 * @param keyword the keyword to be displayed.
\r
2539 * @param displayLocale the locale in which to display the keyword.
\r
2540 * @return the localized keyword name.
\r
2541 * @see #getKeywords(String)
\r
2544 public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
\r
2545 return getDisplayKeywordInternal(keyword, displayLocale.localeID);
\r
2548 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2549 private static String getDisplayKeywordInternal(String keyword, String displayLocaleID) {
\r
2550 // return getTableString("Keys", null, keyword.trim().toLowerCase(), displayLocaleID);
\r
2551 return keyword.trim().toLowerCase();
\r
2555 * Returns a keyword value localized for display in the default locale.
\r
2556 * @param keyword the keyword whose value is to be displayed.
\r
2557 * @return the localized value name.
\r
2560 public String getDisplayKeywordValue(String keyword) {
\r
2561 return getDisplayKeywordValueInternal(localeID, keyword, getDefault().localeID);
\r
2565 * Returns a keyword value localized for display in the specified locale.
\r
2566 * @param keyword the keyword whose value is to be displayed.
\r
2567 * @param displayLocale the locale in which to display the value.
\r
2568 * @return the localized value name.
\r
2571 public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
\r
2572 return getDisplayKeywordValueInternal(localeID, keyword, displayLocale.localeID);
\r
2576 * Returns a keyword value localized for display in the specified locale.
\r
2577 * This is a cover for the ICU4C API.
\r
2578 * @param localeID the id of the locale whose keyword value is to be displayed.
\r
2579 * @param keyword the keyword whose value is to be displayed.
\r
2580 * @param displayLocaleID the id of the locale in which to display the value.
\r
2581 * @return the localized value name.
\r
2584 public static String getDisplayKeywordValue(String localeID, String keyword, String displayLocaleID) {
\r
2585 return getDisplayKeywordValueInternal(localeID, keyword, getName(displayLocaleID));
\r
2589 * Returns a keyword value localized for display in the specified locale.
\r
2590 * This is a cover for the ICU4C API.
\r
2591 * @param localeID the id of the locale whose keyword value is to be displayed.
\r
2592 * @param keyword the keyword whose value is to be displayed.
\r
2593 * @param displayLocale the id of the locale in which to display the value.
\r
2594 * @return the localized value name.
\r
2597 public static String getDisplayKeywordValue(String localeID, String keyword, ULocale displayLocale) {
\r
2598 return getDisplayKeywordValueInternal(localeID, keyword, displayLocale.localeID);
\r
2601 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2602 private static String getDisplayKeywordValueInternal(String localeID, String keyword, String displayLocaleID) {
\r
2603 keyword = keyword.trim().toLowerCase();
\r
2604 String value = new IDParser(localeID).getKeywordValue(keyword);
\r
2605 // return getTableString("Types", keyword, value, displayLocaleID);
\r
2610 * Returns this locale name localized for display in the default locale.
\r
2611 * @return the localized locale name.
\r
2614 public String getDisplayName() {
\r
2615 return getDisplayNameInternal(localeID, getDefault().localeID);
\r
2619 * Returns this locale name localized for display in the provided locale.
\r
2620 * @param displayLocale the locale in which to display the locale name.
\r
2621 * @return the localized locale name.
\r
2624 public String getDisplayName(ULocale displayLocale) {
\r
2625 return getDisplayNameInternal(localeID, displayLocale.localeID);
\r
2629 * Returns the locale ID localized for display in the provided locale.
\r
2630 * This is a cover for the ICU4C API.
\r
2631 * @param localeID the locale whose name is to be displayed.
\r
2632 * @param displayLocaleID the id of the locale in which to display the locale name.
\r
2633 * @return the localized locale name.
\r
2636 public static String getDisplayName(String localeID, String displayLocaleID) {
\r
2637 return getDisplayNameInternal(localeID, getName(displayLocaleID));
\r
2641 * Returns the locale ID localized for display in the provided locale.
\r
2642 * This is a cover for the ICU4C API.
\r
2643 * @param localeID the locale whose name is to be displayed.
\r
2644 * @param displayLocale the locale in which to display the locale name.
\r
2645 * @return the localized locale name.
\r
2648 public static String getDisplayName(String localeID, ULocale displayLocale) {
\r
2649 return getDisplayNameInternal(localeID, displayLocale.localeID);
\r
2652 // displayLocaleID is canonical, localeID need not be since parsing will fix this.
\r
2653 private static String getDisplayNameInternal(String localeID, String displayLocaleID) {
\r
2655 // lang (script, country, variant, keyword=value, ...)
\r
2656 // script, country, variant, keyword=value, ...
\r
2658 // final String[] tableNames = { "Languages", "Scripts", "Countries", "Variants" };
\r
2660 // ICUResourceBundle bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, displayLocaleID);
\r
2662 StringBuffer buf = new StringBuffer();
\r
2664 IDParser parser = new IDParser(localeID);
\r
2665 String[] names = parser.getLanguageScriptCountryVariant();
\r
2667 Locale locale = toLocale(localeID);
\r
2668 Locale dispLocale = toLocale(displayLocaleID);
\r
2670 boolean haveLanguage = names[0].length() > 0;
\r
2671 boolean openParen = false;
\r
2672 for (int i = 0; i < names.length; ++i) {
\r
2673 String name = names[i];
\r
2674 if (name.length() > 0) {
\r
2675 // name = getTableString(tableNames[i], null, name, bundle);
\r
2678 case 0: name = locale.getDisplayLanguage(dispLocale); break;
\r
2680 case 2: name = locale.getDisplayCountry(dispLocale); break;
\r
2681 case 3: name = locale.getDisplayVariant(dispLocale); break;
\r
2684 if (buf.length() > 0) { // need a separator
\r
2685 if (haveLanguage & !openParen) {
\r
2696 Map m = parser.getKeywordMap();
\r
2697 if (!m.isEmpty()) {
\r
2698 Iterator keys = m.entrySet().iterator();
\r
2699 while (keys.hasNext()) {
\r
2700 if (buf.length() > 0) {
\r
2701 if (haveLanguage & !openParen) {
\r
2708 Map.Entry e = (Map.Entry)keys.next();
\r
2709 String key = (String)e.getKey();
\r
2710 String val = (String)e.getValue();
\r
2711 // buf.append(getTableString("Keys", null, key, bundle));
\r
2712 // buf.append("=");
\r
2713 // buf.append(getTableString("Types", key, val, bundle));
\r
2724 return buf.toString();
\r
2728 // * Returns this locale's layout orientation for characters. The possible
\r
2729 // * values are "left-to-right", "right-to-left", "top-to-bottom" or
\r
2730 // * "bottom-to-top".
\r
2731 // * @return The locale's layout orientation for characters.
\r
2732 // * @draft ICU 4.0
\r
2733 // * @provisional This API might change or be removed in a future release.
\r
2735 // public String getCharacterOrientation() {
\r
2736 // return getTableString("layout", null, "characters", getName());
\r
2740 // * Returns this locale's layout orientation for lines. The possible
\r
2741 // * values are "left-to-right", "right-to-left", "top-to-bottom" or
\r
2742 // * "bottom-to-top".
\r
2743 // * @return The locale's layout orientation for lines.
\r
2744 // * @draft ICU 4.0
\r
2745 // * @provisional This API might change or be removed in a future release.
\r
2747 // public String getLineOrientation() {
\r
2748 // return getTableString("layout", null, "lines", getName());
\r
2752 * Selector for <tt>getLocale()</tt> indicating the locale of the
\r
2753 * resource containing the data. This is always at or above the
\r
2754 * valid locale. If the valid locale does not contain the
\r
2755 * specific data being requested, then the actual locale will be
\r
2756 * above the valid locale. If the object was not constructed from
\r
2757 * locale data, then the valid locale is <i>null</i>.
\r
2759 * @draft ICU 2.8 (retain)
\r
2760 * @provisional This API might change or be removed in a future release.
\r
2762 public static Type ACTUAL_LOCALE = new Type();
\r
2765 * Selector for <tt>getLocale()</tt> indicating the most specific
\r
2766 * locale for which any data exists. This is always at or above
\r
2767 * the requested locale, and at or below the actual locale. If
\r
2768 * the requested locale does not correspond to any resource data,
\r
2769 * then the valid locale will be above the requested locale. If
\r
2770 * the object was not constructed from locale data, then the
\r
2771 * actual locale is <i>null</i>.
\r
2773 * <p>Note: The valid locale will be returned correctly in ICU
\r
2774 * 3.0 or later. In ICU 2.8, it is not returned correctly.
\r
2775 * @draft ICU 2.8 (retain)
\r
2776 * @provisional This API might change or be removed in a future release.
\r
2778 public static Type VALID_LOCALE = new Type();
\r
2781 * Opaque selector enum for <tt>getLocale()</tt>.
\r
2782 * @see com.ibm.icu.util.ULocale
\r
2783 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
\r
2784 * @see com.ibm.icu.util.ULocale#VALID_LOCALE
\r
2785 * @draft ICU 2.8 (retainAll)
\r
2786 * @provisional This API might change or be removed in a future release.
\r
2788 public static final class Type {
\r
2793 * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
\r
2794 * NullPointerException is thrown if acceptLanguageList or availableLocales is
\r
2795 * null. If fallback is non-null, it will contain true if a fallback locale (one
\r
2796 * not in the acceptLanguageList) was returned. The value on entry is ignored.
\r
2797 * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
\r
2798 * if a ROOT locale was used as a fallback (because nothing else in
\r
2799 * availableLocales matched). No ULocale array element should be null; behavior
\r
2800 * is undefined if this is the case.
\r
2801 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
\r
2802 * @param availableLocales list of available locales. One of these will be returned.
\r
2803 * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
\r
2804 * @return one of the locales from the availableLocales list, or null if none match
\r
2808 public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
\r
2809 boolean[] fallback) {
\r
2810 if (acceptLanguageList == null) {
\r
2811 throw new NullPointerException();
\r
2813 ULocale acceptList[] = null;
\r
2815 acceptList = parseAcceptLanguage(acceptLanguageList, true);
\r
2816 } catch (ParseException pe) {
\r
2817 acceptList = null;
\r
2819 if (acceptList == null) {
\r
2822 return acceptLanguage(acceptList, availableLocales, fallback);
\r
2826 * Based on a list of acceptable locales, determine an available locale for the user.
\r
2827 * NullPointerException is thrown if acceptLanguageList or availableLocales is
\r
2828 * null. If fallback is non-null, it will contain true if a fallback locale (one
\r
2829 * not in the acceptLanguageList) was returned. The value on entry is ignored.
\r
2830 * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
\r
2831 * if a ROOT locale was used as a fallback (because nothing else in
\r
2832 * availableLocales matched). No ULocale array element should be null; behavior
\r
2833 * is undefined if this is the case.
\r
2834 * @param acceptLanguageList list of acceptable locales
\r
2835 * @param availableLocales list of available locales. One of these will be returned.
\r
2836 * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
\r
2837 * @return one of the locales from the availableLocales list, or null if none match
\r
2841 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
\r
2842 availableLocales, boolean[] fallback) {
\r
2845 if(fallback != null) {
\r
2848 for(i=0;i<acceptLanguageList.length;i++) {
\r
2849 ULocale aLocale = acceptLanguageList[i];
\r
2850 boolean[] setFallback = fallback;
\r
2852 for(j=0;j<availableLocales.length;j++) {
\r
2853 if(availableLocales[j].equals(aLocale)) {
\r
2854 if(setFallback != null) {
\r
2855 setFallback[0]=false; // first time with this locale - not a fallback.
\r
2857 return availableLocales[j];
\r
2860 Locale loc = aLocale.toLocale();
\r
2861 // Locale parent = LocaleUtility.fallback(loc);
\r
2862 Locale parent = fallback(loc);
\r
2863 if(parent != null) {
\r
2864 aLocale = new ULocale(parent);
\r
2868 setFallback = null; // Do not set fallback in later iterations
\r
2869 } while (aLocale != null);
\r
2874 // copied from com.ibm.icu.impl.LocaleUtility
\r
2875 private static Locale fallback(Locale loc) {
\r
2877 // Split the locale into parts and remove the rightmost part
\r
2878 String[] parts = new String[]
\r
2879 { loc.getLanguage(), loc.getCountry(), loc.getVariant() };
\r
2881 for (i=2; i>=0; --i) {
\r
2882 if (parts[i].length() != 0) {
\r
2888 return null; // All parts were empty
\r
2890 return new Locale(parts[0], parts[1], parts[2]);
\r
2894 * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
\r
2895 * NullPointerException is thrown if acceptLanguageList or availableLocales is
\r
2896 * null. If fallback is non-null, it will contain true if a fallback locale (one
\r
2897 * not in the acceptLanguageList) was returned. The value on entry is ignored.
\r
2898 * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
\r
2899 * if a ROOT locale was used as a fallback (because nothing else in
\r
2900 * availableLocales matched). No ULocale array element should be null; behavior
\r
2901 * is undefined if this is the case.
\r
2902 * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
\r
2903 * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
\r
2904 * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
\r
2905 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
\r
2909 public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
\r
2910 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
\r
2915 * Based on an ordered array of acceptable locales, determine an available locale for the user.
\r
2916 * NullPointerException is thrown if acceptLanguageList or availableLocales is
\r
2917 * null. If fallback is non-null, it will contain true if a fallback locale (one
\r
2918 * not in the acceptLanguageList) was returned. The value on entry is ignored.
\r
2919 * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
\r
2920 * if a ROOT locale was used as a fallback (because nothing else in
\r
2921 * availableLocales matched). No ULocale array element should be null; behavior
\r
2922 * is undefined if this is the case.
\r
2923 * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
\r
2924 * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
\r
2925 * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
\r
2926 * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
\r
2930 public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[]
\r
2932 return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
\r
2937 * Package local method used for parsing Accept-Language string
\r
2938 * @internal ICU 3.8
\r
2940 static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient) throws ParseException {
\r
2942 * @internal ICU 3.4
\r
2944 class ULocaleAcceptLanguageQ implements Comparable {
\r
2946 private double serial;
\r
2947 public ULocaleAcceptLanguageQ(double theq, int theserial) {
\r
2949 serial = theserial;
\r
2951 public int compareTo(Object o) {
\r
2952 ULocaleAcceptLanguageQ other = (ULocaleAcceptLanguageQ) o;
\r
2953 if (q > other.q) { // reverse - to sort in descending order
\r
2955 } else if (q < other.q) {
\r
2958 if (serial < other.serial) {
\r
2960 } else if (serial > other.serial) {
\r
2963 return 0; // same object
\r
2968 // parse out the acceptLanguage into an array
\r
2969 TreeMap map = new TreeMap();
\r
2970 StringBuffer languageRangeBuf = new StringBuffer();
\r
2971 StringBuffer qvalBuf = new StringBuffer();
\r
2973 acceptLanguage += ","; // append comma to simplify the parsing code
\r
2975 boolean subTag = false;
\r
2976 boolean q1 = false;
\r
2977 for (n = 0; n < acceptLanguage.length(); n++) {
\r
2978 boolean gotLanguageQ = false;
\r
2979 char c = acceptLanguage.charAt(n);
\r
2981 case 0: // before language-range start
\r
2982 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
\r
2983 // in language-range
\r
2984 languageRangeBuf.append(c);
\r
2987 } else if (c == '*') {
\r
2988 languageRangeBuf.append(c);
\r
2990 } else if (c != ' ' && c != '\t') {
\r
2991 // invalid character
\r
2995 case 1: // in language-range
\r
2996 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
\r
2997 languageRangeBuf.append(c);
\r
2998 } else if (c == '-') {
\r
3000 languageRangeBuf.append(c);
\r
3001 } else if (c == '_') {
\r
3004 languageRangeBuf.append(c);
\r
3008 } else if ('0' <= c && c <= '9') {
\r
3010 languageRangeBuf.append(c);
\r
3012 // DIGIT is allowed only in language sub tag
\r
3015 } else if (c == ',') {
\r
3017 gotLanguageQ = true;
\r
3018 } else if (c == ' ' || c == '\t') {
\r
3019 // language-range end
\r
3021 } else if (c == ';') {
\r
3025 // invalid character for language-range
\r
3029 case 2: // saw wild card range
\r
3032 gotLanguageQ = true;
\r
3033 } else if (c == ' ' || c == '\t') {
\r
3034 // language-range end
\r
3036 } else if (c == ';') {
\r
3044 case 3: // language-range end
\r
3047 gotLanguageQ = true;
\r
3048 } else if (c == ';') {
\r
3051 } else if (c != ' ' && c != '\t') {
\r
3056 case 4: // before q
\r
3060 } else if (c != ' ' && c != '\t') {
\r
3065 case 5: // before equal
\r
3069 } else if (c != ' ' && c != '\t') {
\r
3074 case 6: // before q value
\r
3076 // q value start with 0
\r
3078 qvalBuf.append(c);
\r
3080 } else if (c == '1') {
\r
3081 // q value start with 1
\r
3082 qvalBuf.append(c);
\r
3084 } else if (c == '.') {
\r
3086 qvalBuf.append(c);
\r
3091 } else if (c != ' ' && c != '\t') {
\r
3096 case 7: // q value start
\r
3098 // before q value fraction part
\r
3099 qvalBuf.append(c);
\r
3101 } else if (c == ',') {
\r
3103 gotLanguageQ = true;
\r
3104 } else if (c == ' ' || c == '\t') {
\r
3112 case 8: // before q value fraction part
\r
3113 if ('0' <= c || c <= '9') {
\r
3114 if (q1 && c != '0' && !isLenient) {
\r
3115 // if q value starts with 1, the fraction part must be 0
\r
3118 // in q value fraction part
\r
3119 qvalBuf.append(c);
\r
3127 case 9: // in q value fraction part
\r
3128 if ('0' <= c && c <= '9') {
\r
3129 if (q1 && c != '0') {
\r
3130 // if q value starts with 1, the fraction part must be 0
\r
3133 qvalBuf.append(c);
\r
3135 } else if (c == ',') {
\r
3137 gotLanguageQ = true;
\r
3138 } else if (c == ' ' || c == '\t') {
\r
3146 case 10: // after q value
\r
3149 gotLanguageQ = true;
\r
3150 } else if (c != ' ' && c != '\t') {
\r
3156 if (state == -1) {
\r
3158 throw new ParseException("Invalid Accept-Language", n);
\r
3160 if (gotLanguageQ) {
\r
3162 if (qvalBuf.length() != 0) {
\r
3164 q = Double.parseDouble(qvalBuf.toString());
\r
3165 } catch (NumberFormatException nfe) {
\r
3166 // Already validated, so it should never happen
\r
3173 if (languageRangeBuf.charAt(0) != '*') {
\r
3174 int serial = map.size();
\r
3175 ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
\r
3176 map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString()))); // sort in reverse order.. 1.0, 0.9, 0.8 .. etc
\r
3179 // reset buffer and parse state
\r
3180 languageRangeBuf.setLength(0);
\r
3181 qvalBuf.setLength(0);
\r
3186 // Well, the parser should handle all cases. So just in case.
\r
3187 throw new ParseException("Invalid AcceptlLanguage", n);
\r
3190 // pull out the map
\r
3191 ULocale acceptList[] = (ULocale[])map.values().toArray(new ULocale[map.size()]);
\r
3192 return acceptList;
\r
3195 // private static final String UNDEFINED_LANGUAGE = "und";
\r
3196 // private static final String UNDEFINED_SCRIPT = "Zzzz";
\r
3197 // private static final String UNDEFINED_REGION = "ZZ";
\r
3200 // * Supply most likely subtags to the given locale
\r
3201 // * @param loc The input locale
\r
3202 // * @return A ULocale with most likely subtags filled in.
\r
3204 // * @deprecated This API is ICU internal only.
\r
3206 // public static ULocale addLikelySubtag(ULocale loc) {
\r
3207 // return addLikelySubtags(loc);
\r
3211 // * Add the likely subtags for a provided locale ID, per the algorithm described
\r
3212 // * in the following CLDR technical report:
\r
3214 // * http://www.unicode.org/reports/tr35/#Likely_Subtags
\r
3216 // * If the provided ULocale instance is already in the maximal form, or there is no
\r
3217 // * data available available for maximization, it will be returned. For example,
\r
3218 // * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
\r
3219 // * Otherwise, a new ULocale instance with the maximal form is returned.
\r
3223 // * "en" maximizes to "en_Latn_US"
\r
3225 // * "de" maximizes to "de_Latn_US"
\r
3227 // * "sr" maximizes to "sr_Cyrl_RS"
\r
3229 // * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
\r
3231 // * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
\r
3233 // * @param loc The ULocale to maximize
\r
3234 // * @return The maximized ULocale instance.
\r
3235 // * @draft ICU 4.0
\r
3236 // * @provisional This API might change or be removed in a future release.
\r
3238 // public static ULocale
\r
3239 // addLikelySubtags(ULocale loc)
\r
3241 // String[] tags = new String[3];
\r
3242 // String trailing = null;
\r
3244 // int trailingIndex = parseTagString(
\r
3248 // if (trailingIndex < loc.localeID.length()) {
\r
3249 // trailing = loc.localeID.substring(trailingIndex);
\r
3252 // String newLocaleID =
\r
3253 // createLikelySubtagsString(
\r
3254 // (String)tags[0],
\r
3255 // (String)tags[1],
\r
3256 // (String)tags[2],
\r
3259 // return newLocaleID == null ? loc : new ULocale(newLocaleID);
\r
3263 // * Minimize the subtags for a provided locale ID, per the algorithm described
\r
3264 // * in the following CLDR technical report:
\r
3266 // * http://www.unicode.org/reports/tr35/#Likely_Subtags
\r
3268 // * If the provided ULocale instance is already in the minimal form, or there
\r
3269 // * is no data available for minimization, it will be returned. Since the
\r
3270 // * minimization algorithm relies on proper maximization, see the comments
\r
3271 // * for addLikelySubtags for reasons why there might not be any data.
\r
3275 // * "en_Latn_US" minimizes to "en"
\r
3277 // * "de_Latn_US" minimizes to "de"
\r
3279 // * "sr_Cyrl_RS" minimizes to "sr"
\r
3281 // * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
\r
3282 // * script, and minimizing to "zh" would imply "zh_Hans_CN".)
\r
3284 // * @param loc The ULocale to minimize
\r
3285 // * @return The minimized ULocale instance.
\r
3286 // * @draft ICU 4.0
\r
3287 // * @provisional This API might change or be removed in a future release.
\r
3289 // public static ULocale
\r
3290 // minimizeSubtags(ULocale loc)
\r
3292 // String[] tags = new String[3];
\r
3294 // int trailingIndex = parseTagString(
\r
3298 // String originalLang = (String)tags[0];
\r
3299 // String originalScript = (String)tags[1];
\r
3300 // String originalRegion = (String)tags[2];
\r
3301 // String originalTrailing = null;
\r
3303 // if (trailingIndex < loc.localeID.length()) {
\r
3305 // * Create a String that contains everything
\r
3306 // * after the language, script, and region.
\r
3308 // originalTrailing = loc.localeID.substring(trailingIndex);
\r
3312 // * First, we need to first get the maximization
\r
3313 // * by adding any likely subtags.
\r
3315 // String maximizedLocaleID =
\r
3316 // createLikelySubtagsString(
\r
3318 // originalScript,
\r
3319 // originalRegion,
\r
3323 // * If maximization fails, there's nothing
\r
3326 // if (isEmptyString(maximizedLocaleID)) {
\r
3331 // * Start first with just the language.
\r
3334 // createLikelySubtagsString(
\r
3340 // if (tag.equals(maximizedLocaleID)) {
\r
3341 // String newLocaleID =
\r
3342 // createTagString(
\r
3346 // originalTrailing);
\r
3348 // return new ULocale(newLocaleID);
\r
3353 // * Next, try the language and region.
\r
3355 // if (originalRegion.length() != 0) {
\r
3358 // createLikelySubtagsString(
\r
3361 // originalRegion,
\r
3364 // if (tag.equals(maximizedLocaleID)) {
\r
3365 // String newLocaleID =
\r
3366 // createTagString(
\r
3369 // originalRegion,
\r
3370 // originalTrailing);
\r
3372 // return new ULocale(newLocaleID);
\r
3377 // * Finally, try the language and script. This is our last chance,
\r
3378 // * since trying with all three subtags would only yield the
\r
3379 // * maximal version that we already have.
\r
3381 // if (originalRegion.length() != 0 &&
\r
3382 // originalScript.length() != 0) {
\r
3385 // createLikelySubtagsString(
\r
3387 // originalScript,
\r
3391 // if (tag.equals(maximizedLocaleID)) {
\r
3392 // String newLocaleID =
\r
3393 // createTagString(
\r
3395 // originalScript,
\r
3397 // originalTrailing);
\r
3399 // return new ULocale(newLocaleID);
\r
3407 // * A trivial utility function that checks for a null
\r
3408 // * reference or checks the length of the supplied String.
\r
3410 // * @param string The string to check
\r
3412 // * @return true if the String is empty, or if the reference is null.
\r
3414 // private static boolean isEmptyString(String string) {
\r
3415 // return string == null || string.length() == 0;
\r
3419 // * Append a tag to a StringBuffer, adding the separator if necessary.The tag must
\r
3420 // * not be a zero-length string.
\r
3422 // * @param tag The tag to add.
\r
3423 // * @param buffer The output buffer.
\r
3425 // private static void
\r
3428 // StringBuffer buffer) {
\r
3430 // if (buffer.length() != 0) {
\r
3431 // buffer.append(UNDERSCORE);
\r
3434 // buffer.append(tag);
\r
3438 // * Create a tag string from the supplied parameters. The lang, script and region
\r
3439 // * parameters may be null references.
\r
3441 // * If any of the language, script or region parameters are empty, and the alternateTags
\r
3442 // * parameter is not null, it will be parsed for potential language, script and region tags
\r
3443 // * to be used when constructing the new tag. If the alternateTags parameter is null, or
\r
3444 // * it contains no language tag, the default tag for the unknown language is used.
\r
3446 // * @param lang The language tag to use.
\r
3447 // * @param script The script tag to use.
\r
3448 // * @param region The region tag to use.
\r
3449 // * @param trailing Any trailing data to append to the new tag.
\r
3450 // * @param alternateTags A string containing any alternate tags.
\r
3451 // * @return The new tag string.
\r
3453 // private static String
\r
3454 // createTagString(
\r
3458 // String trailing,
\r
3459 // String alternateTags) {
\r
3461 // IDParser parser = null;
\r
3462 // boolean regionAppended = false;
\r
3464 // StringBuffer tag = new StringBuffer();
\r
3466 // if (!isEmptyString(lang)) {
\r
3471 // else if (isEmptyString(alternateTags)) {
\r
3473 // * Append the value for an unknown language, if
\r
3474 // * we found no language.
\r
3477 // UNDEFINED_LANGUAGE,
\r
3481 // parser = new IDParser(alternateTags);
\r
3483 // String alternateLang = parser.getLanguage();
\r
3486 // * Append the value for an unknown language, if
\r
3487 // * we found no language.
\r
3490 // !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
\r
3494 // if (!isEmptyString(script)) {
\r
3499 // else if (!isEmptyString(alternateTags)) {
\r
3501 // * Parse the alternateTags string for the script.
\r
3503 // if (parser == null) {
\r
3504 // parser = new IDParser(alternateTags);
\r
3507 // String alternateScript = parser.getScript();
\r
3509 // if (!isEmptyString(alternateScript)) {
\r
3511 // alternateScript,
\r
3516 // if (!isEmptyString(region)) {
\r
3521 // regionAppended = true;
\r
3523 // else if (!isEmptyString(alternateTags)) {
\r
3525 // * Parse the alternateTags string for the region.
\r
3527 // if (parser == null) {
\r
3528 // parser = new IDParser(alternateTags);
\r
3531 // String alternateRegion = parser.getCountry();
\r
3533 // if (!isEmptyString(alternateRegion)) {
\r
3535 // alternateRegion,
\r
3538 // regionAppended = true;
\r
3542 // if (trailing != null && trailing.length() > 1) {
\r
3544 // * The current ICU format expects two underscores
\r
3545 // * will separate the variant from the preceeding
\r
3546 // * parts of the tag, if there is no region.
\r
3548 // int separators = 0;
\r
3550 // if (trailing.charAt(0) == UNDERSCORE) {
\r
3551 // if (trailing.charAt(1) == UNDERSCORE) {
\r
3552 // separators = 2;
\r
3556 // separators = 1;
\r
3559 // if (regionAppended) {
\r
3561 // * If we appended a region, we may need to strip
\r
3562 // * the extra separator from the variant portion.
\r
3564 // if (separators == 2) {
\r
3565 // tag.append(trailing.substring(1));
\r
3568 // tag.append(trailing);
\r
3573 // * If we did not append a region, we may need to add
\r
3574 // * an extra separator to the variant portion.
\r
3576 // if (separators == 1) {
\r
3577 // tag.append(UNDERSCORE);
\r
3579 // tag.append(trailing);
\r
3583 // return tag.toString();
\r
3587 // * Create a tag string from the supplied parameters. The lang, script and region
\r
3588 // * parameters may be null references.If the lang parameter is an empty string, the
\r
3589 // * default value for an unknown language is written to the output buffer.
\r
3591 // * @param lang The language tag to use.
\r
3592 // * @param script The script tag to use.
\r
3593 // * @param region The region tag to use.
\r
3594 // * @param trailing Any trailing data to append to the new tag.
\r
3595 // * @return The new String.
\r
3598 // createTagString(
\r
3602 // String trailing) {
\r
3604 // return createTagString(
\r
3613 // * Parse the language, script, and region subtags from a tag string, and return the results.
\r
3615 // * This function does not return the canonical strings for the unknown script and region.
\r
3617 // * @param localeID The locale ID to parse.
\r
3618 // * @param tags An array of three String references to return the subtag strings.
\r
3619 // * @return The number of chars of the localeID parameter consumed.
\r
3621 // private static int
\r
3622 // parseTagString(
\r
3623 // String localeID,
\r
3626 // IDParser parser = new IDParser(localeID);
\r
3628 // String lang = parser.getLanguage();
\r
3629 // String script = parser.getScript();
\r
3630 // String region = parser.getCountry();
\r
3632 // if (isEmptyString(lang)) {
\r
3633 // tags[0] = UNDEFINED_LANGUAGE;
\r
3636 // tags[0] = lang;
\r
3639 // if (script.equals(UNDEFINED_SCRIPT)) {
\r
3643 // tags[1] = script;
\r
3646 // if (region.equals(UNDEFINED_REGION)) {
\r
3650 // tags[2] = region;
\r
3654 // * Search for the variant. If there is one, then return the index of
\r
3655 // * the preceeding separator.
\r
3656 // * If there's no variant, search for the keyword delimiter,
\r
3657 // * and return its index. Otherwise, return the length of the
\r
3660 // * $TOTO(dbertoni) we need to take into account that we might
\r
3661 // * find a part of the language as the variant, since it can
\r
3662 // * can have a variant portion that is long enough to contain
\r
3663 // * the same characters as the variant.
\r
3665 // String variant = parser.getVariant();
\r
3667 // if (!isEmptyString(variant)){
\r
3668 // int index = localeID.indexOf(variant);
\r
3671 // return index > 0 ? index - 1 : index;
\r
3675 // int index = localeID.indexOf('@');
\r
3677 // return index == -1 ? localeID.length() : index;
\r
3681 // private static String
\r
3682 // lookupLikelySubtags(String localeId) {
\r
3683 // UResourceBundle bundle =
\r
3684 // UResourceBundle.getBundleInstance(
\r
3685 // ICUResourceBundle.ICU_BASE_NAME, "likelySubtags");
\r
3687 // return bundle.getString(localeId);
\r
3689 // catch(MissingResourceException e) {
\r
3694 // private static String
\r
3695 // createLikelySubtagsString(
\r
3699 // String variants) {
\r
3702 // * Try the language with the script and region first.
\r
3704 // if (!isEmptyString(script) && !isEmptyString(region)) {
\r
3706 // String searchTag =
\r
3707 // createTagString(
\r
3713 // String likelySubtags = lookupLikelySubtags(searchTag);
\r
3716 // if (likelySubtags == null) {
\r
3717 // if (likelySubtags2 != null) {
\r
3718 // System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
\r
3721 // else if (likelySubtags2 == null) {
\r
3722 // System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
\r
3724 // else if (!likelySubtags.equals(likelySubtags2)) {
\r
3725 // System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2 + "\"");
\r
3728 // if (likelySubtags != null) {
\r
3729 // // Always use the language tag from the
\r
3730 // // maximal string, since it may be more
\r
3731 // // specific than the one provided.
\r
3732 // return createTagString(
\r
3737 // likelySubtags);
\r
3742 // * Try the language with just the script.
\r
3744 // if (!isEmptyString(script)) {
\r
3746 // String searchTag =
\r
3747 // createTagString(
\r
3753 // String likelySubtags = lookupLikelySubtags(searchTag);
\r
3754 // if (likelySubtags != null) {
\r
3755 // // Always use the language tag from the
\r
3756 // // maximal string, since it may be more
\r
3757 // // specific than the one provided.
\r
3758 // return createTagString(
\r
3763 // likelySubtags);
\r
3768 // * Try the language with just the region.
\r
3770 // if (!isEmptyString(region)) {
\r
3772 // String searchTag =
\r
3773 // createTagString(
\r
3779 // String likelySubtags = lookupLikelySubtags(searchTag);
\r
3781 // if (likelySubtags != null) {
\r
3782 // // Always use the language tag from the
\r
3783 // // maximal string, since it may be more
\r
3784 // // specific than the one provided.
\r
3785 // return createTagString(
\r
3790 // likelySubtags);
\r
3795 // * Finally, try just the language.
\r
3798 // String searchTag =
\r
3799 // createTagString(
\r
3805 // String likelySubtags = lookupLikelySubtags(searchTag);
\r
3807 // if (likelySubtags != null) {
\r
3808 // // Always use the language tag from the
\r
3809 // // maximal string, since it may be more
\r
3810 // // specific than the one provided.
\r
3811 // return createTagString(
\r
3816 // likelySubtags);
\r