Frames | No Frames |
1: /* Locale.java -- i18n locales 2: Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.util; 40: 41: import gnu.classpath.SystemProperties; 42: 43: import gnu.java.lang.CPStringBuilder; 44: 45: import gnu.java.locale.LocaleHelper; 46: 47: import java.io.IOException; 48: import java.io.ObjectInputStream; 49: import java.io.ObjectOutputStream; 50: import java.io.Serializable; 51: 52: import java.util.spi.LocaleNameProvider; 53: 54: /** 55: * Locales represent a specific country and culture. Classes which can be 56: * passed a Locale object tailor their information for a given locale. For 57: * instance, currency number formatting is handled differently for the USA 58: * and France. 59: * 60: * <p>Locales are made up of a language code, a country code, and an optional 61: * set of variant strings. Language codes are represented by 62: * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt"> 63: * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989 64: * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997. 65: * 66: * <p>Country codes are represented by 67: * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html"> 68: * ISO 3166</a>. Variant strings are vendor and browser specific. Standard 69: * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and 70: * "MAC" for Macintosh. When there is more than one variant string, they must 71: * be separated by an underscore (U+005F). 72: * 73: * <p>The default locale is determined by the values of the system properties 74: * user.language, user.country (or user.region), and user.variant, defaulting 75: * to "en_US". Note that the locale does NOT contain the conversion and 76: * formatting capabilities (for that, use ResourceBundle and java.text). 77: * Rather, it is an immutable tag object for identifying a given locale, which 78: * is referenced by these other classes when they must make locale-dependent 79: * decisions. 80: * 81: * @see ResourceBundle 82: * @see java.text.Format 83: * @see java.text.NumberFormat 84: * @see java.text.Collator 85: * @author Jochen Hoenicke 86: * @author Paul Fisher 87: * @author Eric Blake (ebb9@email.byu.edu) 88: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 89: * @since 1.1 90: * @status updated to 1.4 91: */ 92: public final class Locale implements Serializable, Cloneable 93: { 94: /** Locale which represents the English language. */ 95: public static final Locale ENGLISH = getLocale("en"); 96: 97: /** Locale which represents the French language. */ 98: public static final Locale FRENCH = getLocale("fr"); 99: 100: /** Locale which represents the German language. */ 101: public static final Locale GERMAN = getLocale("de"); 102: 103: /** Locale which represents the Italian language. */ 104: public static final Locale ITALIAN = getLocale("it"); 105: 106: /** Locale which represents the Japanese language. */ 107: public static final Locale JAPANESE = getLocale("ja"); 108: 109: /** Locale which represents the Korean language. */ 110: public static final Locale KOREAN = getLocale("ko"); 111: 112: /** Locale which represents the Chinese language. */ 113: public static final Locale CHINESE = getLocale("zh"); 114: 115: /** Locale which represents the Chinese language as used in China. */ 116: public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN"); 117: 118: /** 119: * Locale which represents the Chinese language as used in Taiwan. 120: * Same as TAIWAN Locale. 121: */ 122: public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW"); 123: 124: /** Locale which represents France. */ 125: public static final Locale FRANCE = getLocale("fr", "FR"); 126: 127: /** Locale which represents Germany. */ 128: public static final Locale GERMANY = getLocale("de", "DE"); 129: 130: /** Locale which represents Italy. */ 131: public static final Locale ITALY = getLocale("it", "IT"); 132: 133: /** Locale which represents Japan. */ 134: public static final Locale JAPAN = getLocale("ja", "JP"); 135: 136: /** Locale which represents Korea. */ 137: public static final Locale KOREA = getLocale("ko", "KR"); 138: 139: /** 140: * Locale which represents China. 141: * Same as SIMPLIFIED_CHINESE Locale. 142: */ 143: public static final Locale CHINA = SIMPLIFIED_CHINESE; 144: 145: /** 146: * Locale which represents the People's Republic of China. 147: * Same as CHINA Locale. 148: */ 149: public static final Locale PRC = CHINA; 150: 151: /** 152: * Locale which represents Taiwan. 153: * Same as TRADITIONAL_CHINESE Locale. 154: */ 155: public static final Locale TAIWAN = TRADITIONAL_CHINESE; 156: 157: /** Locale which represents the United Kingdom. */ 158: public static final Locale UK = getLocale("en", "GB"); 159: 160: /** Locale which represents the United States. */ 161: public static final Locale US = getLocale("en", "US"); 162: 163: /** Locale which represents the English speaking portion of Canada. */ 164: public static final Locale CANADA = getLocale("en", "CA"); 165: 166: /** Locale which represents the French speaking portion of Canada. */ 167: public static final Locale CANADA_FRENCH = getLocale("fr", "CA"); 168: 169: /** The root locale, used as the base case in lookups by 170: * locale-sensitive operations. 171: */ 172: public static final Locale ROOT = new Locale("","",""); 173: 174: /** 175: * Compatible with JDK 1.1+. 176: */ 177: private static final long serialVersionUID = 9149081749638150636L; 178: 179: /** 180: * The language code, as returned by getLanguage(). 181: * 182: * @serial the languange, possibly "" 183: */ 184: private final String language; 185: 186: /** 187: * The country code, as returned by getCountry(). 188: * 189: * @serial the country, possibly "" 190: */ 191: private final String country; 192: 193: /** 194: * The variant code, as returned by getVariant(). 195: * 196: * @serial the variant, possibly "" 197: */ 198: private final String variant; 199: 200: /** 201: * This is the cached hashcode. When writing to stream, we write -1. 202: * 203: * @serial should be -1 in serial streams 204: */ 205: private int hashcode; 206: 207: /** 208: * Array storing all available locales. 209: */ 210: private static transient Locale[] availableLocales; 211: 212: /** 213: * Locale cache. Only created locale objects are stored. 214: * Contains all supported locales when getAvailableLocales() 215: * got called. 216: */ 217: private static transient HashMap localeMap; 218: 219: /** 220: * The default locale. Except for during bootstrapping, this should never be 221: * null. Note the logic in the main constructor, to detect when 222: * bootstrapping has completed. 223: */ 224: private static Locale defaultLocale; 225: 226: static { 227: String language = SystemProperties.getProperty("user.language", "en"); 228: String country = SystemProperties.getProperty("user.country", "US"); 229: String region = SystemProperties.getProperty("user.region", null); 230: String variant = SystemProperties.getProperty("user.variant", ""); 231: 232: defaultLocale = getLocale(language, 233: (region != null) ? region : country, 234: variant); 235: } 236: 237: /** 238: * Array storing all the available two-letter ISO639 languages. 239: */ 240: private static transient String[] languageCache; 241: 242: /** 243: * Array storing all the available two-letter ISO3166 country codes. 244: */ 245: private static transient String[] countryCache; 246: 247: /** 248: * Retrieves the locale with the specified language from the cache. 249: * 250: * @param language the language of the locale to retrieve. 251: * @return the locale. 252: */ 253: private static Locale getLocale(String language) 254: { 255: return getLocale(language, "", ""); 256: } 257: 258: /** 259: * Retrieves the locale with the specified language and country 260: * from the cache. 261: * 262: * @param language the language of the locale to retrieve. 263: * @param country the country of the locale to retrieve. 264: * @return the locale. 265: */ 266: private static Locale getLocale(String language, String country) 267: { 268: return getLocale(language, country, ""); 269: } 270: 271: /** 272: * Retrieves the locale with the specified language, country 273: * and variant from the cache. 274: * 275: * @param language the language of the locale to retrieve. 276: * @param country the country of the locale to retrieve. 277: * @param variant the variant of the locale to retrieve. 278: * @return the locale. 279: */ 280: private static Locale getLocale(String language, String country, String variant) 281: { 282: if (localeMap == null) 283: localeMap = new HashMap(256); 284: 285: String name = language + "_" + country + "_" + variant; 286: Locale locale = (Locale) localeMap.get(name); 287: 288: if (locale == null) 289: { 290: locale = new Locale(language, country, variant); 291: localeMap.put(name, locale); 292: } 293: 294: return locale; 295: } 296: 297: /** 298: * Convert new iso639 codes to the old ones. 299: * 300: * @param language the language to check 301: * @return the appropriate code 302: */ 303: private String convertLanguage(String language) 304: { 305: if (language.equals("")) 306: return language; 307: language = language.toLowerCase(); 308: int index = "he,id,yi".indexOf(language); 309: if (index != -1) 310: return "iw,in,ji".substring(index, index + 2); 311: return language; 312: } 313: 314: /** 315: * Creates a new locale for the given language and country. 316: * 317: * @param language lowercase two-letter ISO-639 A2 language code 318: * @param country uppercase two-letter ISO-3166 A2 contry code 319: * @param variant vendor and browser specific 320: * @throws NullPointerException if any argument is null 321: */ 322: public Locale(String language, String country, String variant) 323: { 324: // During bootstrap, we already know the strings being passed in are 325: // the correct capitalization, and not null. We can't call 326: // String.toUpperCase during this time, since that depends on the 327: // default locale. 328: if (defaultLocale != null) 329: { 330: language = convertLanguage(language); 331: country = country.toUpperCase(); 332: } 333: this.language = language.intern(); 334: this.country = country.intern(); 335: this.variant = variant.intern(); 336: hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); 337: } 338: 339: /** 340: * Creates a new locale for the given language and country. 341: * 342: * @param language lowercase two-letter ISO-639 A2 language code 343: * @param country uppercase two-letter ISO-3166 A2 country code 344: * @throws NullPointerException if either argument is null 345: */ 346: public Locale(String language, String country) 347: { 348: this(language, country, ""); 349: } 350: 351: /** 352: * Creates a new locale for a language. 353: * 354: * @param language lowercase two-letter ISO-639 A2 language code 355: * @throws NullPointerException if either argument is null 356: * @since 1.4 357: */ 358: public Locale(String language) 359: { 360: this(language, "", ""); 361: } 362: 363: /** 364: * Returns the default Locale. The default locale is generally once set 365: * on start up and then never changed. Normally you should use this locale 366: * for everywhere you need a locale. The initial setting matches the 367: * default locale, the user has chosen. 368: * 369: * @return the default locale for this virtual machine 370: */ 371: public static Locale getDefault() 372: { 373: return defaultLocale; 374: } 375: 376: /** 377: * Changes the default locale. Normally only called on program start up. 378: * Note that this doesn't change the locale for other programs. This has 379: * a security check, 380: * <code>PropertyPermission("user.language", "write")</code>, because of 381: * its potential impact to running code. 382: * 383: * @param newLocale the new default locale 384: * @throws NullPointerException if newLocale is null 385: * @throws SecurityException if permission is denied 386: */ 387: public static void setDefault(Locale newLocale) 388: { 389: if (newLocale == null) 390: throw new NullPointerException(); 391: SecurityManager sm = System.getSecurityManager(); 392: if (sm != null) 393: sm.checkPermission(new PropertyPermission("user.language", "write")); 394: defaultLocale = newLocale; 395: } 396: 397: /** 398: * Returns the list of available locales. 399: * 400: * @return the installed locales 401: */ 402: public static synchronized Locale[] getAvailableLocales() 403: { 404: if (availableLocales == null) 405: { 406: int len = LocaleHelper.getLocaleCount(); 407: availableLocales = new Locale[len]; 408: 409: for (int i = 0; i < len; i++) 410: { 411: String language; 412: String country = ""; 413: String variant = ""; 414: String name = LocaleHelper.getLocaleName(i); 415: 416: language = name.substring(0, 2); 417: 418: if (name.length() > 2) 419: country = name.substring(3); 420: 421: int index = country.indexOf("_"); 422: if (index > 0) 423: { 424: variant = country.substring(index + 1); 425: country = country.substring(0, index - 1); 426: } 427: 428: availableLocales[i] = getLocale(language, country, variant); 429: } 430: } 431: 432: return (Locale[]) availableLocales.clone(); 433: } 434: 435: /** 436: * Returns a list of all 2-letter uppercase country codes as defined 437: * in ISO 3166. 438: * 439: * @return a list of acceptable country codes 440: */ 441: public static String[] getISOCountries() 442: { 443: if (countryCache == null) 444: { 445: countryCache = getISOStrings("territories"); 446: } 447: 448: return (String[]) countryCache.clone(); 449: } 450: 451: /** 452: * Returns a list of all 2-letter lowercase language codes as defined 453: * in ISO 639 (both old and new variant). 454: * 455: * @return a list of acceptable language codes 456: */ 457: public static String[] getISOLanguages() 458: { 459: if (languageCache == null) 460: { 461: languageCache = getISOStrings("languages"); 462: } 463: return (String[]) languageCache.clone(); 464: } 465: 466: /** 467: * Returns the set of keys from the specified resource hashtable, filtered 468: * so that only two letter strings are returned. 469: * 470: * @param tableName the name of the table from which to retrieve the keys. 471: * @return an array of two-letter strings. 472: */ 473: private static String[] getISOStrings(String tableName) 474: { 475: int count = 0; 476: ResourceBundle bundle = 477: ResourceBundle.getBundle("gnu.java.locale.LocaleInformation"); 478: Enumeration e = bundle.getKeys(); 479: ArrayList tempList = new ArrayList(); 480: 481: while (e.hasMoreElements()) 482: { 483: String key = (String) e.nextElement(); 484: 485: if (key.startsWith(tableName + ".")) 486: { 487: String str = key.substring(tableName.length() + 1); 488: 489: if (str.length() == 2 490: && Character.isLetter(str.charAt(0)) 491: && Character.isLetter(str.charAt(1))) 492: { 493: tempList.add(str); 494: ++count; 495: } 496: } 497: } 498: 499: String[] strings = new String[count]; 500: 501: for (int a = 0; a < count; ++a) 502: strings[a] = (String) tempList.get(a); 503: 504: return strings; 505: } 506: 507: /** 508: * Returns the language code of this locale. Some language codes have changed 509: * as ISO 639 has evolved; this returns the old name, even if you built 510: * the locale with the new one. 511: * 512: * @return language code portion of this locale, or an empty String 513: */ 514: public String getLanguage() 515: { 516: return language; 517: } 518: 519: /** 520: * Returns the country code of this locale. 521: * 522: * @return country code portion of this locale, or an empty String 523: */ 524: public String getCountry() 525: { 526: return country; 527: } 528: 529: /** 530: * Returns the variant code of this locale. 531: * 532: * @return the variant code portion of this locale, or an empty String 533: */ 534: public String getVariant() 535: { 536: return variant; 537: } 538: 539: /** 540: * Gets the string representation of the current locale. This consists of 541: * the language, the country, and the variant, separated by an underscore. 542: * The variant is listed only if there is a language or country. Examples: 543: * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC". 544: * 545: * @return the string representation of this Locale 546: * @see #getDisplayName() 547: */ 548: public String toString() 549: { 550: if (language.length() == 0 && country.length() == 0) 551: return ""; 552: else if (country.length() == 0 && variant.length() == 0) 553: return language; 554: CPStringBuilder result = new CPStringBuilder(language); 555: result.append('_').append(country); 556: if (variant.length() != 0) 557: result.append('_').append(variant); 558: return result.toString(); 559: } 560: 561: /** 562: * Returns the three-letter ISO language abbrevation of this locale. 563: * 564: * @throws MissingResourceException if the three-letter code is not known 565: */ 566: public String getISO3Language() 567: { 568: // We know all strings are interned so we can use '==' for better performance. 569: if (language == "") 570: return ""; 571: int index 572: = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da," 573: + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw," 574: + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn," 575: + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne," 576: + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk," 577: + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr," 578: + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu") 579: .indexOf(language); 580: 581: if (index % 3 != 0 || language.length() != 2) 582: throw new MissingResourceException 583: ("Can't find ISO3 language for " + language, 584: "java.util.Locale", language); 585: 586: // Don't read this aloud. These are the three letter language codes. 587: return 588: ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu" 589: + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv" 590: + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur" 591: + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm" 592: + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi" 593: + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd" 594: + "uzbvievolwolxhoyidyorzhazhozul") 595: .substring(index, index + 3); 596: } 597: 598: /** 599: * Returns the three-letter ISO country abbrevation of the locale. 600: * 601: * @throws MissingResourceException if the three-letter code is not known 602: */ 603: public String getISO3Country() 604: { 605: // We know all strings are interned so we can use '==' for better performance. 606: if (country == "") 607: return ""; 608: int index 609: = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF," 610: + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK," 611: + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER," 612: + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ," 613: + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT," 614: + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS," 615: + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV," 616: + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG," 617: + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH," 618: + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN," 619: + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF," 620: + "WS,YE,YT,YU,ZA,ZM,ZR,ZW") 621: .indexOf(country); 622: 623: if (index % 3 != 0 || country.length() != 2) 624: throw new MissingResourceException 625: ("Can't find ISO3 country for " + country, 626: "java.util.Locale", country); 627: 628: // Don't read this aloud. These are the three letter country codes. 629: return 630: ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR" 631: + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR" 632: + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN" 633: + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM" 634: + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ" 635: + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO" 636: + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER" 637: + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW" 638: + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP" 639: + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI" 640: + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE") 641: .substring(index, index + 3); 642: } 643: 644: /** 645: * Gets the country name suitable for display to the user, formatted 646: * for the default locale. This has the same effect as 647: * <pre> 648: * getDisplayLanguage(Locale.getDefault()); 649: * </pre> 650: * 651: * @return the language name of this locale localized to the default locale, 652: * with the ISO code as backup 653: */ 654: public String getDisplayLanguage() 655: { 656: return getDisplayLanguage(defaultLocale); 657: } 658: 659: /** 660: * <p> 661: * Gets the name of the language specified by this locale, in a form suitable 662: * for display to the user. If possible, the display name will be localized 663: * to the specified locale. For example, if the locale instance is 664: * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>, 665: * the result would be 'German'. Using the German locale would instead give 666: * 'Deutsch'. If the display name can not be localized to the supplied 667: * locale, it will fall back on other output in the following order: 668: * </p> 669: * <ul> 670: * <li>the display name in the default locale</li> 671: * <li>the display name in English</li> 672: * <li>the ISO code</li> 673: * </ul> 674: * <p> 675: * If the language is unspecified by this locale, then the empty string is 676: * returned. 677: * </p> 678: * 679: * @param inLocale the locale to use for formatting the display string. 680: * @return the language name of this locale localized to the given locale, 681: * with the default locale, English and the ISO code as backups. 682: * @throws NullPointerException if the supplied locale is null. 683: */ 684: public String getDisplayLanguage(Locale inLocale) 685: { 686: if (language.isEmpty()) 687: return ""; 688: try 689: { 690: ResourceBundle res = 691: ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", 692: inLocale, 693: ClassLoader.getSystemClassLoader()); 694: 695: return res.getString("languages." + language); 696: } 697: catch (MissingResourceException e) 698: { 699: /* This means runtime support for the locale 700: * is not available, so we check providers. */ 701: } 702: for (LocaleNameProvider p : 703: ServiceLoader.load(LocaleNameProvider.class)) 704: { 705: for (Locale loc : p.getAvailableLocales()) 706: { 707: if (loc.equals(inLocale)) 708: { 709: String locLang = p.getDisplayLanguage(language, 710: inLocale); 711: if (locLang != null) 712: return locLang; 713: break; 714: } 715: } 716: } 717: if (inLocale.equals(Locale.ROOT)) // Base case 718: return language; 719: return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale)); 720: } 721: 722: /** 723: * Returns the country name of this locale localized to the 724: * default locale. If the localized is not found, the ISO code 725: * is returned. This has the same effect as 726: * <pre> 727: * getDisplayCountry(Locale.getDefault()); 728: * </pre> 729: * 730: * @return the country name of this locale localized to the given locale, 731: * with the ISO code as backup 732: */ 733: public String getDisplayCountry() 734: { 735: return getDisplayCountry(defaultLocale); 736: } 737: 738: /** 739: * <p> 740: * Gets the name of the country specified by this locale, in a form suitable 741: * for display to the user. If possible, the display name will be localized 742: * to the specified locale. For example, if the locale instance is 743: * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>, 744: * the result would be 'Germany'. Using the German locale would instead give 745: * 'Deutschland'. If the display name can not be localized to the supplied 746: * locale, it will fall back on other output in the following order: 747: * </p> 748: * <ul> 749: * <li>the display name in the default locale</li> 750: * <li>the display name in English</li> 751: * <li>the ISO code</li> 752: * </ul> 753: * <p> 754: * If the country is unspecified by this locale, then the empty string is 755: * returned. 756: * </p> 757: * 758: * @param inLocale the locale to use for formatting the display string. 759: * @return the country name of this locale localized to the given locale, 760: * with the default locale, English and the ISO code as backups. 761: * @throws NullPointerException if the supplied locale is null. 762: */ 763: public String getDisplayCountry(Locale inLocale) 764: { 765: if (country.isEmpty()) 766: return ""; 767: try 768: { 769: ResourceBundle res = 770: ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", 771: inLocale, 772: ClassLoader.getSystemClassLoader()); 773: 774: return res.getString("territories." + country); 775: } 776: catch (MissingResourceException e) 777: { 778: /* This means runtime support for the locale 779: * is not available, so we check providers. */ 780: } 781: for (LocaleNameProvider p : 782: ServiceLoader.load(LocaleNameProvider.class)) 783: { 784: for (Locale loc : p.getAvailableLocales()) 785: { 786: if (loc.equals(inLocale)) 787: { 788: String locCountry = p.getDisplayCountry(country, 789: inLocale); 790: if (locCountry != null) 791: return locCountry; 792: break; 793: } 794: } 795: } 796: if (inLocale.equals(Locale.ROOT)) // Base case 797: return country; 798: return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale)); 799: } 800: 801: /** 802: * Returns the variant name of this locale localized to the 803: * default locale. If the localized is not found, the variant code 804: * itself is returned. This has the same effect as 805: * <pre> 806: * getDisplayVariant(Locale.getDefault()); 807: * </pre> 808: * 809: * @return the variant code of this locale localized to the given locale, 810: * with the ISO code as backup 811: */ 812: public String getDisplayVariant() 813: { 814: return getDisplayVariant(defaultLocale); 815: } 816: 817: 818: /** 819: * <p> 820: * Gets the name of the variant specified by this locale, in a form suitable 821: * for display to the user. If possible, the display name will be localized 822: * to the specified locale. For example, if the locale instance is a revised 823: * variant, and the specified locale is <code>Locale.UK</code>, the result 824: * would be 'REVISED'. Using the German locale would instead give 825: * 'Revidiert'. If the display name can not be localized to the supplied 826: * locale, it will fall back on other output in the following order: 827: * </p> 828: * <ul> 829: * <li>the display name in the default locale</li> 830: * <li>the display name in English</li> 831: * <li>the ISO code</li> 832: * </ul> 833: * <p> 834: * If the variant is unspecified by this locale, then the empty string is 835: * returned. 836: * </p> 837: * 838: * @param inLocale the locale to use for formatting the display string. 839: * @return the variant name of this locale localized to the given locale, 840: * with the default locale, English and the ISO code as backups. 841: * @throws NullPointerException if the supplied locale is null. 842: */ 843: public String getDisplayVariant(Locale inLocale) 844: { 845: if (variant.isEmpty()) 846: return ""; 847: try 848: { 849: ResourceBundle res = 850: ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", 851: inLocale, 852: ClassLoader.getSystemClassLoader()); 853: 854: return res.getString("variants." + variant); 855: } 856: catch (MissingResourceException e) 857: { 858: /* This means runtime support for the locale 859: * is not available, so we check providers. */ 860: } 861: for (LocaleNameProvider p : 862: ServiceLoader.load(LocaleNameProvider.class)) 863: { 864: for (Locale loc : p.getAvailableLocales()) 865: { 866: if (loc.equals(inLocale)) 867: { 868: String locVar = p.getDisplayVariant(variant, 869: inLocale); 870: if (locVar != null) 871: return locVar; 872: break; 873: } 874: } 875: } 876: if (inLocale.equals(Locale.ROOT)) // Base case 877: return country; 878: return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale)); 879: } 880: 881: /** 882: * Gets all local components suitable for display to the user, formatted 883: * for the default locale. For the language component, getDisplayLanguage 884: * is called. For the country component, getDisplayCountry is called. 885: * For the variant set component, getDisplayVariant is called. 886: * 887: * <p>The returned String will be one of the following forms:<br> 888: * <pre> 889: * language (country, variant) 890: * language (country) 891: * language (variant) 892: * country (variant) 893: * language 894: * country 895: * variant 896: * </pre> 897: * 898: * @return String version of this locale, suitable for display to the user 899: */ 900: public String getDisplayName() 901: { 902: return getDisplayName(defaultLocale); 903: } 904: 905: /** 906: * Gets all local components suitable for display to the user, formatted 907: * for a specified locale. For the language component, 908: * getDisplayLanguage(Locale) is called. For the country component, 909: * getDisplayCountry(Locale) is called. For the variant set component, 910: * getDisplayVariant(Locale) is called. 911: * 912: * <p>The returned String will be one of the following forms:<br> 913: * <pre> 914: * language (country, variant) 915: * language (country) 916: * language (variant) 917: * country (variant) 918: * language 919: * country 920: * variant 921: * </pre> 922: * 923: * @param locale locale to use for formatting 924: * @return String version of this locale, suitable for display to the user 925: */ 926: public String getDisplayName(Locale locale) 927: { 928: CPStringBuilder result = new CPStringBuilder(); 929: int count = 0; 930: String[] delimiters = {"", " (", ","}; 931: if (language.length() != 0) 932: { 933: result.append(delimiters[count++]); 934: result.append(getDisplayLanguage(locale)); 935: } 936: if (country.length() != 0) 937: { 938: result.append(delimiters[count++]); 939: result.append(getDisplayCountry(locale)); 940: } 941: if (variant.length() != 0) 942: { 943: result.append(delimiters[count++]); 944: result.append(getDisplayVariant(locale)); 945: } 946: if (count > 1) 947: result.append(")"); 948: return result.toString(); 949: } 950: 951: /** 952: * Does the same as <code>Object.clone()</code> but does not throw 953: * a <code>CloneNotSupportedException</code>. Why anyone would 954: * use this method is a secret to me, since this class is immutable. 955: * 956: * @return the clone 957: */ 958: public Object clone() 959: { 960: // This class is final, so no need to use native super.clone(). 961: return new Locale(language, country, variant); 962: } 963: 964: /** 965: * Return the hash code for this locale. The hashcode is the logical 966: * xor of the hash codes of the language, the country and the variant. 967: * The hash code is precomputed, since <code>Locale</code>s are often 968: * used in hash tables. 969: * 970: * @return the hashcode 971: */ 972: public int hashCode() 973: { 974: return hashcode; 975: } 976: 977: /** 978: * Compares two locales. To be equal, obj must be a Locale with the same 979: * language, country, and variant code. 980: * 981: * @param obj the other locale 982: * @return true if obj is equal to this 983: */ 984: public boolean equals(Object obj) 985: { 986: if (this == obj) 987: return true; 988: if (! (obj instanceof Locale)) 989: return false; 990: Locale l = (Locale) obj; 991: 992: return (language == l.language 993: && country == l.country 994: && variant == l.variant); 995: } 996: 997: /** 998: * Write the locale to an object stream. 999: * 1000: * @param s the stream to write to 1001: * @throws IOException if the write fails 1002: * @serialData The first three fields are Strings representing language, 1003: * country, and variant. The fourth field is a placeholder for 1004: * the cached hashcode, but this is always written as -1, and 1005: * recomputed when reading it back. 1006: */ 1007: private void writeObject(ObjectOutputStream s) 1008: throws IOException 1009: { 1010: ObjectOutputStream.PutField fields = s.putFields(); 1011: fields.put("hashcode", -1); 1012: s.defaultWriteObject(); 1013: } 1014: 1015: /** 1016: * Reads a locale from the input stream. 1017: * 1018: * @param s the stream to read from 1019: * @throws IOException if reading fails 1020: * @throws ClassNotFoundException if reading fails 1021: * @serialData the hashCode is always invalid and must be recomputed 1022: */ 1023: private void readObject(ObjectInputStream s) 1024: throws IOException, ClassNotFoundException 1025: { 1026: s.defaultReadObject(); 1027: hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); 1028: } 1029: } // class Locale