1. /*
  2. * @(#)Locale.java 1.69 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  9. * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  10. *
  11. * The original version of this source code and documentation
  12. * is copyrighted and owned by Taligent, Inc., a wholly-owned
  13. * subsidiary of IBM. These materials are provided under terms
  14. * of a License Agreement between Taligent and Sun. This technology
  15. * is protected by multiple US and International patents.
  16. *
  17. * This notice and attribution to Taligent may not be removed.
  18. * Taligent is a registered trademark of Taligent, Inc.
  19. *
  20. */
  21. package java.util;
  22. import java.io.*;
  23. import java.security.AccessController;
  24. import java.text.MessageFormat;
  25. import sun.security.action.GetPropertyAction;
  26. import sun.text.resources.LocaleData;
  27. /**
  28. *
  29. * A <code>Locale</code> object represents a specific geographical, political,
  30. * or cultural region. An operation that requires a <code>Locale</code> to perform
  31. * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
  32. * to tailor information for the user. For example, displaying a number
  33. * is a locale-sensitive operation--the number should be formatted
  34. * according to the customs/conventions of the user's native country,
  35. * region, or culture.
  36. *
  37. * <P>
  38. * Create a <code>Locale</code> object using the constructors in this class:
  39. * <blockquote>
  40. * <pre>
  41. * Locale(String language)
  42. * Locale(String language, String country)
  43. * Locale(String language, String country, String variant)
  44. * </pre>
  45. * </blockquote>
  46. * The language argument is a valid <STRONG>ISO Language Code.</STRONG>
  47. * These codes are the lower-case, two-letter codes as defined by ISO-639.
  48. * You can find a full list of these codes at a number of sites, such as:
  49. * <BR><a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
  50. * <code>http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</code></a>
  51. *
  52. * <P>
  53. * The country argument is a valid <STRONG>ISO Country Code.</STRONG> These
  54. * codes are the upper-case, two-letter codes as defined by ISO-3166.
  55. * You can find a full list of these codes at a number of sites, such as:
  56. * <BR><a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
  57. * <code>http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html</code></a>
  58. *
  59. * <P>
  60. * The variant argument is a vendor or browser-specific code.
  61. * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
  62. * Where there are two variants, separate them with an underscore, and
  63. * put the most important one first. For example, a Traditional Spanish collation
  64. * might construct a locale with parameters for language, country and variant as:
  65. * "es", "ES", "Traditional_WIN".
  66. *
  67. * <P>
  68. * Because a <code>Locale</code> object is just an identifier for a region,
  69. * no validity check is performed when you construct a <code>Locale</code>.
  70. * If you want to see whether particular resources are available for the
  71. * <code>Locale</code> you construct, you must query those resources. For
  72. * example, ask the <code>NumberFormat</code> for the locales it supports
  73. * using its <code>getAvailableLocales</code> method.
  74. * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
  75. * locale, you get back the best available match, not necessarily
  76. * precisely what you asked for. For more information, look at
  77. * {@link ResourceBundle}.
  78. *
  79. * <P>
  80. * The <code>Locale</code> class provides a number of convenient constants
  81. * that you can use to create <code>Locale</code> objects for commonly used
  82. * locales. For example, the following creates a <code>Locale</code> object
  83. * for the United States:
  84. * <blockquote>
  85. * <pre>
  86. * Locale.US
  87. * </pre>
  88. * </blockquote>
  89. *
  90. * <P>
  91. * Once you've created a <code>Locale</code> you can query it for information about
  92. * itself. Use <code>getCountry</code> to get the ISO Country Code and
  93. * <code>getLanguage</code> to get the ISO Language Code. You can
  94. * use <code>getDisplayCountry</code> to get the
  95. * name of the country suitable for displaying to the user. Similarly,
  96. * you can use <code>getDisplayLanguage</code> to get the name of
  97. * the language suitable for displaying to the user. Interestingly,
  98. * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
  99. * and have two versions: one that uses the default locale and one
  100. * that uses the locale specified as an argument.
  101. *
  102. * <P>
  103. * The Java 2 platform provides a number of classes that perform locale-sensitive
  104. * operations. For example, the <code>NumberFormat</code> class formats
  105. * numbers, currency, or percentages in a locale-sensitive manner. Classes
  106. * such as <code>NumberFormat</code> have a number of convenience methods
  107. * for creating a default object of that type. For example, the
  108. * <code>NumberFormat</code> class provides these three convenience methods
  109. * for creating a default <code>NumberFormat</code> object:
  110. * <blockquote>
  111. * <pre>
  112. * NumberFormat.getInstance()
  113. * NumberFormat.getCurrencyInstance()
  114. * NumberFormat.getPercentInstance()
  115. * </pre>
  116. * </blockquote>
  117. * These methods have two variants; one with an explicit locale
  118. * and one without; the latter using the default locale.
  119. * <blockquote>
  120. * <pre>
  121. * NumberFormat.getInstance(myLocale)
  122. * NumberFormat.getCurrencyInstance(myLocale)
  123. * NumberFormat.getPercentInstance(myLocale)
  124. * </pre>
  125. * </blockquote>
  126. * A <code>Locale</code> is the mechanism for identifying the kind of object
  127. * (<code>NumberFormat</code>) that you would like to get. The locale is
  128. * <STRONG>just</STRONG> a mechanism for identifying objects,
  129. * <STRONG>not</STRONG> a container for the objects themselves.
  130. *
  131. * <P>
  132. * Each class that performs locale-sensitive operations allows you
  133. * to get all the available objects of that type. You can sift
  134. * through these objects by language, country, or variant,
  135. * and use the display names to present a menu to the user.
  136. * For example, you can create a menu of all the collation objects
  137. * suitable for a given language. Such classes must implement these
  138. * three class methods:
  139. * <blockquote>
  140. * <pre>
  141. * public static Locale[] getAvailableLocales()
  142. * public static String getDisplayName(Locale objectLocale,
  143. * Locale displayLocale)
  144. * public static final String getDisplayName(Locale objectLocale)
  145. * // getDisplayName will throw MissingResourceException if the locale
  146. * // is not one of the available locales.
  147. * </pre>
  148. * </blockquote>
  149. *
  150. * @see ResourceBundle
  151. * @see java.text.Format
  152. * @see java.text.NumberFormat
  153. * @see java.text.Collator
  154. * @version 1.69, 01/23/03
  155. * @author Mark Davis
  156. * @since 1.1
  157. */
  158. public final class Locale implements Cloneable, Serializable {
  159. /** Useful constant for language.
  160. */
  161. static public final Locale ENGLISH = new Locale("en","","");
  162. /** Useful constant for language.
  163. */
  164. static public final Locale FRENCH = new Locale("fr","","");
  165. /** Useful constant for language.
  166. */
  167. static public final Locale GERMAN = new Locale("de","","");
  168. /** Useful constant for language.
  169. */
  170. static public final Locale ITALIAN = new Locale("it","","");
  171. /** Useful constant for language.
  172. */
  173. static public final Locale JAPANESE = new Locale("ja","","");
  174. /** Useful constant for language.
  175. */
  176. static public final Locale KOREAN = new Locale("ko","","");
  177. /** Useful constant for language.
  178. */
  179. static public final Locale CHINESE = new Locale("zh","","");
  180. /** Useful constant for language.
  181. */
  182. static public final Locale SIMPLIFIED_CHINESE = new Locale("zh","CN","");
  183. /** Useful constant for language.
  184. */
  185. static public final Locale TRADITIONAL_CHINESE = new Locale("zh","TW","");
  186. /** Useful constant for country.
  187. */
  188. static public final Locale FRANCE = new Locale("fr","FR","");
  189. /** Useful constant for country.
  190. */
  191. static public final Locale GERMANY = new Locale("de","DE","");
  192. /** Useful constant for country.
  193. */
  194. static public final Locale ITALY = new Locale("it","IT","");
  195. /** Useful constant for country.
  196. */
  197. static public final Locale JAPAN = new Locale("ja","JP","");
  198. /** Useful constant for country.
  199. */
  200. static public final Locale KOREA = new Locale("ko","KR","");
  201. /** Useful constant for country.
  202. */
  203. static public final Locale CHINA = new Locale("zh","CN","");
  204. /** Useful constant for country.
  205. */
  206. static public final Locale PRC = new Locale("zh","CN","");
  207. /** Useful constant for country.
  208. */
  209. static public final Locale TAIWAN = new Locale("zh","TW","");
  210. /** Useful constant for country.
  211. */
  212. static public final Locale UK = new Locale("en","GB","");
  213. /** Useful constant for country.
  214. */
  215. static public final Locale US = new Locale("en","US","");
  216. /** Useful constant for country.
  217. */
  218. static public final Locale CANADA = new Locale("en","CA","");
  219. /** Useful constant for country.
  220. */
  221. static public final Locale CANADA_FRENCH = new Locale("fr","CA","");
  222. /** serialization ID
  223. */
  224. static final long serialVersionUID = 9149081749638150636L;
  225. /**
  226. * Construct a locale from language, country, variant.
  227. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  228. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  229. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  230. * API on Locale will return only the OLD codes.
  231. * @param language lowercase two-letter ISO-639 code.
  232. * @param country uppercase two-letter ISO-3166 code.
  233. * @param variant vendor and browser specific code. See class description.
  234. * @exception NullPointerException thrown if any argument is null.
  235. */
  236. public Locale(String language, String country, String variant) {
  237. this.language = convertOldISOCodes(language);
  238. this.country = toUpperCase(country).intern();
  239. this.variant = variant.intern();
  240. }
  241. /**
  242. * Construct a locale from language, country.
  243. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  244. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  245. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  246. * API on Locale will return only the OLD codes.
  247. * @param language lowercase two-letter ISO-639 code.
  248. * @param country uppercase two-letter ISO-3166 code.
  249. * @exception NullPointerException thrown if either argument is null.
  250. */
  251. public Locale(String language, String country) {
  252. this(language, country, "");
  253. }
  254. /**
  255. * Construct a locale from a language code.
  256. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  257. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  258. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  259. * API on Locale will return only the OLD codes.
  260. * @param language lowercase two-letter ISO-639 code.
  261. * @exception NullPointerException thrown if argument is null.
  262. * @since 1.4
  263. */
  264. public Locale(String language) {
  265. this(language, "", "");
  266. }
  267. /**
  268. * Gets the current value of the default locale for this instance
  269. * of the Java Virtual Machine.
  270. * <p>
  271. * The Java Virtual Machine sets the default locale during startup
  272. * based on the host environment. It is used by many locale-sensitive
  273. * methods if no locale is explicitly specified.
  274. * It can be changed using the
  275. * {@link #setDefault(java.util.Locale) setDefault} method.
  276. *
  277. * @return the default locale for this instance of the Java Virtual Machine
  278. */
  279. public static Locale getDefault() {
  280. // do not synchronize this method - see 4071298
  281. // it's OK if more than one default locale happens to be created
  282. if (defaultLocale == null) {
  283. String language, region, country, variant;
  284. language = (String) AccessController.doPrivileged(
  285. new GetPropertyAction("user.language", "en"));
  286. // for compatibility, check for old user.region property
  287. region = (String) AccessController.doPrivileged(
  288. new GetPropertyAction("user.region"));
  289. if (region != null) {
  290. // region can be of form country, country_variant, or _variant
  291. int i = region.indexOf('_');
  292. if (i >= 0) {
  293. country = region.substring(0, i);
  294. variant = region.substring(i + 1);
  295. } else {
  296. country = region;
  297. variant = "";
  298. }
  299. } else {
  300. country = (String) AccessController.doPrivileged(
  301. new GetPropertyAction("user.country", ""));
  302. variant = (String) AccessController.doPrivileged(
  303. new GetPropertyAction("user.variant", ""));
  304. }
  305. defaultLocale = new Locale(language, country, variant);
  306. }
  307. return defaultLocale;
  308. }
  309. /**
  310. * Sets the default locale for this instance of the Java Virtual Machine.
  311. * This does not affect the host locale.
  312. * <p>
  313. * If there is a security manager, its <code>checkPermission</code>
  314. * method is called with a <code>PropertyPermission("user.language", "write")</code>
  315. * permission before the default locale is changed.
  316. * <p>
  317. * The Java Virtual Machine sets the default locale during startup
  318. * based on the host environment. It is used by many locale-sensitive
  319. * methods if no locale is explicitly specified.
  320. * <p>
  321. * Since changing the default locale may affect many different areas
  322. * of functionality, this method should only be used if the caller
  323. * is prepared to reinitialize locale-sensitive code running
  324. * within the same Java Virtual Machine, such as the user interface.
  325. *
  326. * @throws SecurityException
  327. * if a security manager exists and its
  328. * <code>checkPermission</code> method doesn't allow the operation.
  329. * @throws NullPointerException if <code>newLocale</code> is null
  330. * @param newLocale the new default locale
  331. * @see SecurityManager#checkPermission
  332. * @see java.util.PropertyPermission
  333. */
  334. public static synchronized void setDefault(Locale newLocale) {
  335. if (newLocale == null)
  336. throw new NullPointerException("Can't set default locale to NULL");
  337. SecurityManager sm = System.getSecurityManager();
  338. if (sm != null) sm.checkPermission(new PropertyPermission
  339. ("user.language", "write"));
  340. defaultLocale = newLocale;
  341. }
  342. /**
  343. * Returns a list of all installed locales.
  344. */
  345. public static Locale[] getAvailableLocales() {
  346. return LocaleData.getAvailableLocales("LocaleString");
  347. }
  348. /**
  349. * Returns a list of all 2-letter country codes defined in ISO 3166.
  350. * Can be used to create Locales.
  351. */
  352. public static String[] getISOCountries() {
  353. if (isoCountries == null) {
  354. isoCountries = new String[compressedIsoCountries.length() / 6];
  355. for (int i = 0; i < isoCountries.length; i++)
  356. isoCountries[i] = compressedIsoCountries.substring((i * 6) + 1, (i * 6) + 3);
  357. }
  358. String[] result = new String[isoCountries.length];
  359. System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
  360. return result;
  361. }
  362. /**
  363. * Returns a list of all 2-letter language codes defined in ISO 639.
  364. * Can be used to create Locales.
  365. * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
  366. * The list this function returns includes both the new and the old codes for the
  367. * languages whose codes have changed.]
  368. */
  369. public static String[] getISOLanguages() {
  370. if (isoLanguages == null) {
  371. isoLanguages = new String[compressedIsoLanguages.length() / 6];
  372. for (int i = 0; i < isoLanguages.length; i++)
  373. isoLanguages[i] = compressedIsoLanguages.substring((i * 6) + 1, (i * 6) + 3);
  374. }
  375. String[] result = new String[isoLanguages.length];
  376. System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
  377. return result;
  378. }
  379. /**
  380. * Returns the language code for this locale, which will either be the empty string
  381. * or a lowercase ISO 639 code.
  382. * <p>NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
  383. * Locale's constructor recognizes both the new and the old codes for the languages
  384. * whose codes have changed, but this function always returns the old code. If you
  385. * want to check for a specific language whose code has changed, don't do <pre>
  386. * if (locale.getLanguage().equals("he")
  387. * ...
  388. * </pre>Instead, do<pre>
  389. * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage())
  390. * ...</pre>
  391. * @see #getDisplayLanguage
  392. */
  393. public String getLanguage() {
  394. return language;
  395. }
  396. /**
  397. * Returns the country/region code for this locale, which will either be the empty string
  398. * or an upercase ISO 3166 2-letter code.
  399. * @see #getDisplayCountry
  400. */
  401. public String getCountry() {
  402. return country;
  403. }
  404. /**
  405. * Returns the variant code for this locale.
  406. * @see #getDisplayVariant
  407. */
  408. public String getVariant() {
  409. return variant;
  410. }
  411. /**
  412. * Getter for the programmatic name of the entire locale,
  413. * with the language, country and variant separated by underbars.
  414. * Language is always lower case, and country is always upper case.
  415. * If the language is missing, the string will begin with an underbar.
  416. * If both the language and country fields are missing, this function
  417. * will return the empty string, even if the variant field is filled in
  418. * (you can't have a locale with just a variant-- the variant must accompany
  419. * a valid language or country code).
  420. * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
  421. * @see #getDisplayName
  422. */
  423. public final String toString() {
  424. boolean l = language.length() != 0;
  425. boolean c = country.length() != 0;
  426. boolean v = variant.length() != 0;
  427. StringBuffer result = new StringBuffer(language);
  428. if (c||(l&&v)) {
  429. result.append('_').append(country); // This may just append '_'
  430. }
  431. if (v&&(l||c)) {
  432. result.append('_').append(variant);
  433. }
  434. return result.toString();
  435. }
  436. /**
  437. * Returns a three-letter abbreviation for this locale's language. If the locale
  438. * doesn't specify a language, this will be the empty string. Otherwise, this will
  439. * be a lowercase ISO 639-2/T language code.
  440. * The ISO 639-2 language codes can be found on-line at
  441. * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
  442. * @exception MissingResourceException Throws MissingResourceException if the
  443. * three-letter language abbreviation is not available for this locale.
  444. */
  445. public String getISO3Language() throws MissingResourceException {
  446. int length = language.length();
  447. if (length == 0) {
  448. return "";
  449. }
  450. int index = compressedIsoLanguages.indexOf("," + language);
  451. if (index == -1 || length != 2) {
  452. throw new MissingResourceException("Couldn't find 3-letter language code for "
  453. + language, "LocaleElements_" + toString(), "ShortLanguage");
  454. }
  455. return compressedIsoLanguages.substring(index + 3, index + 6);
  456. }
  457. /**
  458. * Returns a three-letter abbreviation for this locale's country. If the locale
  459. * doesn't specify a country, this will be tbe the empty string. Otherwise, this will
  460. * be an uppercase ISO 3166 3-letter country code.
  461. * @exception MissingResourceException Throws MissingResourceException if the
  462. * three-letter country abbreviation is not available for this locale.
  463. */
  464. public String getISO3Country() throws MissingResourceException {
  465. int length = country.length();
  466. if (length == 0) {
  467. return "";
  468. }
  469. int index = compressedIsoCountries.indexOf("," + country);
  470. if (index == -1 || length != 2) {
  471. throw new MissingResourceException("Couldn't find 3-letter country code for "
  472. + country, "LocaleElements_" + toString(), "ShortCountry");
  473. }
  474. return compressedIsoCountries.substring(index + 3, index + 6);
  475. }
  476. /**
  477. * Returns a name for the locale's language that is appropriate for display to the
  478. * user.
  479. * If possible, the name returned will be localized for the default locale.
  480. * For example, if the locale is fr_FR and the default locale
  481. * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  482. * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
  483. * If the name returned cannot be localized for the default locale,
  484. * (say, we don't have a Japanese name for Croatian),
  485. * this function falls back on the English name, and uses the ISO code as a last-resort
  486. * value. If the locale doesn't specify a language, this function returns the empty string.
  487. */
  488. public final String getDisplayLanguage() {
  489. return getDisplayLanguage(getDefault());
  490. }
  491. /**
  492. * Returns a name for the locale's language that is appropriate for display to the
  493. * user.
  494. * If possible, the name returned will be localized according to inLocale.
  495. * For example, if the locale is fr_FR and inLocale
  496. * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  497. * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
  498. * If the name returned cannot be localized according to inLocale,
  499. * (say, we don't have a Japanese name for Croatian),
  500. * this function falls back on the default locale, on the English name, and finally
  501. * on the ISO code as a last-resort value. If the locale doesn't specify a language,
  502. * this function returns the empty string.
  503. */
  504. public String getDisplayLanguage(Locale inLocale) {
  505. String langCode = language;
  506. if (langCode.length() == 0)
  507. return "";
  508. Locale workingLocale = (Locale)inLocale.clone();
  509. String result = null;
  510. int phase = 0;
  511. boolean done = false;
  512. if (workingLocale.variant.length() == 0)
  513. phase = 1;
  514. if (workingLocale.country.length() == 0)
  515. phase = 2;
  516. while (!done) {
  517. try {
  518. ResourceBundle bundle = LocaleData.getLocaleElements(workingLocale);
  519. result = findStringMatch((String[][])bundle.getObject("Languages"),
  520. langCode, langCode);
  521. if (result.length() != 0)
  522. done = true;
  523. }
  524. catch (Exception e) {
  525. // just fall through
  526. }
  527. if (!done) {
  528. switch (phase) {
  529. case 0:
  530. workingLocale.variant = "";
  531. break;
  532. case 1:
  533. workingLocale.country = "";
  534. break;
  535. case 2:
  536. workingLocale = getDefault();
  537. break;
  538. case 3:
  539. workingLocale = new Locale("", "", "");
  540. break;
  541. default:
  542. return langCode;
  543. }
  544. phase++;
  545. }
  546. }
  547. return result;
  548. }
  549. /**
  550. * Returns a name for the locale's country that is appropriate for display to the
  551. * user.
  552. * If possible, the name returned will be localized for the default locale.
  553. * For example, if the locale is fr_FR and the default locale
  554. * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  555. * the default locale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  556. * If the name returned cannot be localized for the default locale,
  557. * (say, we don't have a Japanese name for Croatia),
  558. * this function falls back on the English name, and uses the ISO code as a last-resort
  559. * value. If the locale doesn't specify a country, this function returns the empty string.
  560. */
  561. public final String getDisplayCountry() {
  562. return getDisplayCountry(getDefault());
  563. }
  564. /**
  565. * Returns a name for the locale's country that is appropriate for display to the
  566. * user.
  567. * If possible, the name returned will be localized according to inLocale.
  568. * For example, if the locale is fr_FR and inLocale
  569. * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  570. * inLocale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  571. * If the name returned cannot be localized according to inLocale.
  572. * (say, we don't have a Japanese name for Croatia),
  573. * this function falls back on the default locale, on the English name, and finally
  574. * on the ISO code as a last-resort value. If the locale doesn't specify a country,
  575. * this function returns the empty string.
  576. */
  577. public String getDisplayCountry(Locale inLocale) {
  578. String ctryCode = country;
  579. if (ctryCode.length() == 0)
  580. return "";
  581. Locale workingLocale = (Locale)inLocale.clone();
  582. String result = null;
  583. int phase = 0;
  584. boolean done = false;
  585. if (workingLocale.variant.length() == 0)
  586. phase = 1;
  587. if (workingLocale.country.length() == 0)
  588. phase = 2;
  589. while (!done) {
  590. try {
  591. ResourceBundle bundle = LocaleData.getLocaleElements(workingLocale);
  592. result = findStringMatch((String[][])bundle.getObject("Countries"),
  593. ctryCode, ctryCode);
  594. if (result.length() != 0)
  595. done = true;
  596. }
  597. catch (Exception e) {
  598. // just fall through
  599. }
  600. if (!done) {
  601. switch (phase) {
  602. case 0:
  603. workingLocale.variant = "";
  604. break;
  605. case 1:
  606. workingLocale.country = "";
  607. break;
  608. case 2:
  609. workingLocale = getDefault();
  610. break;
  611. case 3:
  612. workingLocale = new Locale("", "", "");
  613. break;
  614. default:
  615. return ctryCode;
  616. }
  617. phase++;
  618. }
  619. }
  620. return result;
  621. }
  622. /**
  623. * Returns a name for the locale's variant code that is appropriate for display to the
  624. * user. If possible, the name will be localized for the default locale. If the locale
  625. * doesn't specify a variant code, this function returns the empty string.
  626. */
  627. public final String getDisplayVariant() {
  628. return getDisplayVariant(getDefault());
  629. }
  630. /**
  631. * Returns a name for the locale's variant code that is appropriate for display to the
  632. * user. If possible, the name will be localized for inLocale. If the locale
  633. * doesn't specify a variant code, this function returns the empty string.
  634. */
  635. public String getDisplayVariant(Locale inLocale) {
  636. if (variant.length() == 0)
  637. return "";
  638. ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
  639. String names[] = getDisplayVariantArray(bundle);
  640. // Get the localized patterns for formatting a list, and use
  641. // them to format the list.
  642. String[] patterns;
  643. try {
  644. patterns = (String[])bundle.getObject("LocaleNamePatterns");
  645. }
  646. catch (MissingResourceException e) {
  647. patterns = null;
  648. }
  649. return formatList(patterns, names);
  650. }
  651. /**
  652. * Returns a name for the locale that is appropriate for display to the
  653. * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  654. * and getDisplayVariant() assembled into a single string. The display name will have
  655. * one of the following forms:<p><blockquote>
  656. * language (country, variant)<p>
  657. * language (country)<p>
  658. * language (variant)<p>
  659. * country (variant)<p>
  660. * language<p>
  661. * country<p>
  662. * variant<p></blockquote>
  663. * depending on which fields are specified in the locale. If the language, country,
  664. * and variant fields are all empty, this function returns the empty string.
  665. */
  666. public final String getDisplayName() {
  667. return getDisplayName(getDefault());
  668. }
  669. /**
  670. * Returns a name for the locale that is appropriate for display to the
  671. * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  672. * and getDisplayVariant() assembled into a single string. The display name will have
  673. * one of the following forms:<p><blockquote>
  674. * language (country, variant)<p>
  675. * language (country)<p>
  676. * language (variant)<p>
  677. * country (variant)<p>
  678. * language<p>
  679. * country<p>
  680. * variant<p></blockquote>
  681. * depending on which fields are specified in the locale. If the language, country,
  682. * and variant fields are all empty, this function returns the empty string.
  683. */
  684. public String getDisplayName(Locale inLocale) {
  685. ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
  686. String languageName = getDisplayLanguage(inLocale);
  687. String countryName = getDisplayCountry(inLocale);
  688. String[] variantNames = getDisplayVariantArray(bundle);
  689. // Get the localized patterns for formatting a display name.
  690. String[] patterns;
  691. try {
  692. patterns = (String[])bundle.getObject("LocaleNamePatterns");
  693. }
  694. catch (MissingResourceException e) {
  695. patterns = null;
  696. }
  697. // The display name consists of a main name, followed by qualifiers.
  698. // Typically, the format is "MainName (Qualifier, Qualifier)" but this
  699. // depends on what pattern is stored in the display locale.
  700. String mainName = null;
  701. String[] qualifierNames = null;
  702. // The main name is the language, or if there is no language, the country.
  703. // If there is neither language nor country (an anomalous situation) then
  704. // the display name is simply the variant's display name.
  705. if (languageName.length() != 0) {
  706. mainName = languageName;
  707. if (countryName.length() != 0) {
  708. qualifierNames = new String[variantNames.length + 1];
  709. System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
  710. qualifierNames[0] = countryName;
  711. }
  712. else qualifierNames = variantNames;
  713. }
  714. else if (countryName.length() != 0) {
  715. mainName = countryName;
  716. qualifierNames = variantNames;
  717. }
  718. else {
  719. return formatList(patterns, variantNames);
  720. }
  721. // Create an array whose first element is the number of remaining
  722. // elements. This serves as a selector into a ChoiceFormat pattern from
  723. // the resource. The second and third elements are the main name and
  724. // the qualifier; if there are no qualifiers, the third element is
  725. // unused by the format pattern.
  726. Object[] displayNames = {
  727. new Integer(qualifierNames.length != 0 ? 2 : 1),
  728. mainName,
  729. // We could also just call formatList() and have it handle the empty
  730. // list case, but this is more efficient, and we want it to be
  731. // efficient since all the language-only locales will not have any
  732. // qualifiers.
  733. qualifierNames.length != 0 ? formatList(patterns, qualifierNames) : null
  734. };
  735. if (patterns != null) {
  736. return new MessageFormat(patterns[0]).format(displayNames);
  737. }
  738. else {
  739. // If we cannot get the message format pattern, then we use a simple
  740. // hard-coded pattern. This should not occur in practice unless the
  741. // installation is missing some core files (LocaleElements etc.).
  742. StringBuffer result = new StringBuffer();
  743. result.append((String)displayNames[1]);
  744. if (displayNames.length > 2) {
  745. result.append(" (");
  746. result.append((String)displayNames[2]);
  747. result.append(")");
  748. }
  749. return result.toString();
  750. }
  751. }
  752. /**
  753. * Overrides Cloneable
  754. */
  755. public Object clone()
  756. {
  757. try {
  758. Locale that = (Locale)super.clone();
  759. return that;
  760. } catch (CloneNotSupportedException e) {
  761. throw new InternalError();
  762. }
  763. }
  764. /**
  765. * Override hashCode.
  766. * Since Locales are often used in hashtables, caches the value
  767. * for speed.
  768. */
  769. // XXX Depending on performance of synchronized, may want to
  770. // XXX just compute in constructor.
  771. public synchronized int hashCode() {
  772. if (hashcode == -1) {
  773. hashcode =
  774. language.hashCode() ^
  775. country.hashCode() ^
  776. variant.hashCode();
  777. }
  778. return hashcode;
  779. }
  780. // Overrides
  781. /**
  782. * Returns true if this Locale is equal to another object. A Locale is
  783. * deemed equal to another Locale with identical language, country,
  784. * and variant, and unequal to all other objects.
  785. *
  786. * @return true if this Locale is equal to the specified object.
  787. */
  788. public boolean equals(Object obj) {
  789. if (this == obj) // quick check
  790. return true;
  791. if (!(obj instanceof Locale)) // (1) same object?
  792. return false;
  793. Locale other = (Locale) obj;
  794. if (hashCode() != other.hashCode()) return false; // quick check
  795. if (language != other.language) return false;
  796. if (country != other.country) return false;
  797. if (variant != other.variant) return false;
  798. return true; // we made it through the guantlet.
  799. // (1) We don't check super.equals since it is Object.
  800. // Since Locale is final, we don't have to check both directions.
  801. }
  802. // ================= privates =====================================
  803. // XXX instance and class variables. For now keep these separate, since it is
  804. // faster to match. Later, make into single string.
  805. /**
  806. * @serial
  807. * @see #getLanguage
  808. */
  809. private String language = "";
  810. /**
  811. * @serial
  812. * @see #getCountry
  813. */
  814. private String country = "";
  815. /**
  816. * @serial
  817. * @see #getVariant
  818. */
  819. private String variant = "";
  820. /**
  821. * Placeholder for the object's hash code. Always -1.
  822. * @serial
  823. */
  824. private int hashcode = -1; // lazy evaluated
  825. private static Locale defaultLocale = null;
  826. /**
  827. * Return an array of the display names of the variant.
  828. * @param bundle the ResourceBundle to use to get the display names
  829. * @return an array of display names, possible of zero length.
  830. */
  831. private String[] getDisplayVariantArray(ResourceBundle bundle) {
  832. // Split the variant name into tokens separated by '_'.
  833. StringTokenizer tokenizer = new StringTokenizer(variant, "_");
  834. String[] names = new String[tokenizer.countTokens()];
  835. // For each variant token, lookup the display name. If
  836. // not found, use the variant name itself.
  837. for (int i=0; i<names.length; ++i) {
  838. String token = tokenizer.nextToken();
  839. try {
  840. names[i] = (String)bundle.getObject("%%" + token);
  841. }
  842. catch (MissingResourceException e) {
  843. names[i] = token;
  844. }
  845. }
  846. return names;
  847. }
  848. /**
  849. * Format a list with an array of patterns.
  850. * @param patterns an array of three patterns. The first pattern is not
  851. * used. The second pattern should create a MessageFormat taking 0-3 arguments
  852. * and formatting them into a list. The third pattern should take 2 arguments
  853. * and is used by composeList. If patterns is null, then a the list is
  854. * formatted by concatenation with the delimiter ','.
  855. * @param stringList the list of strings to be formatted.
  856. * @return a string representing the list.
  857. */
  858. private static String formatList(String[] patterns, String[] stringList) {
  859. // If we have no list patterns, compose the list in a simple,
  860. // non-localized way.
  861. if (patterns == null) {
  862. StringBuffer result = new StringBuffer();
  863. for (int i=0; i<stringList.length; ++i) {
  864. if (i>0) result.append(',');
  865. result.append(stringList[i]);
  866. }
  867. return result.toString();
  868. }
  869. // Compose the list down to three elements if necessary
  870. if (stringList.length > 3) {
  871. MessageFormat format = new MessageFormat(patterns[2]);
  872. stringList = composeList(format, stringList);
  873. }
  874. // Rebuild the argument list with the list length as the first element
  875. Object[] args = new Object[stringList.length + 1];
  876. System.arraycopy(stringList, 0, args, 1, stringList.length);
  877. args[0] = new Integer(stringList.length);
  878. // Format it using the pattern in the resource
  879. MessageFormat format = new MessageFormat(patterns[1]);
  880. return format.format(args);
  881. }
  882. /**
  883. * Given a list of strings, return a list shortened to three elements.
  884. * Shorten it by applying the given format to the first two elements
  885. * recursively.
  886. * @param format a format which takes two arguments
  887. * @param list a list of strings
  888. * @return if the list is three elements or shorter, the same list;
  889. * otherwise, a new list of three elements.
  890. */
  891. private static String[] composeList(MessageFormat format, String[] list) {
  892. if (list.length <= 3) return list;
  893. // Use the given format to compose the first two elements into one
  894. String[] listItems = { list[0], list[1] };
  895. String newItem = format.format(listItems);
  896. // Form a new list one element shorter
  897. String[] newList = new String[list.length-1];
  898. System.arraycopy(list, 2, newList, 1, newList.length-1);
  899. newList[0] = newItem;
  900. // Recurse
  901. return composeList(format, newList);
  902. }
  903. /**
  904. * @serialData The first three fields are three <code>String</code> objects:
  905. * the first is a 2-letter ISO 639 code representing the locale's language,
  906. * the second is a 2-letter ISO 3166 code representing the locale's region or
  907. * country, and the third is an optional chain of variant codes defined by this
  908. * library. Any of the fields may be the empty string. The fourth field is an
  909. * <code>int</code> whose value is always -1. This is a sentinel value indicating
  910. * the <code>Locale</code>'s hash code must be recomputed.
  911. */
  912. private void writeObject(ObjectOutputStream out) throws IOException {
  913. // hashcode is semantically transient. We couldn't define it as transient
  914. // because versions of this class that DIDN'T declare it as transient have
  915. // already shipped. What we're doing here is making sure that the written-out
  916. // version of hashcode is always -1, regardless of what's really stored there
  917. // (we hold onto the original value just in case someone might want it again).
  918. // Writing -1 ensures that version 1.1 Locales will always recalculate their
  919. // hash codes after being streamed back in. This is necessary because
  920. // String.hashCode() calculates its hash code differently in 1.2 than it did
  921. // in 1.1.
  922. int temp = hashcode;
  923. hashcode = -1;
  924. out.defaultWriteObject();
  925. hashcode = temp;
  926. }
  927. /**
  928. * @serialData The first three fields are three <code>String</code> objects:
  929. * the first is a 2-letter ISO 639 code representing the locale's language,
  930. * the second is a 2-letter ISO 3166 code representing the locale's region or
  931. * country, and the third is an optional chain of variant codes defined by this
  932. * library. Any of the fields may be the empty string. The fourth field is an
  933. * <code>int</code>representing the locale's hash code, but is ignored by
  934. * <code>readObject()</code>. Whatever this field's value, the hash code is
  935. * initialized to -1, a sentinel value that indicates the hash code must be
  936. * recomputed.
  937. */
  938. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  939. // hashcode is semantically transient. We couldn't define it as transient
  940. // because versions of this class that DIDN'T declare is as transient have
  941. // already shipped. This code makes sure that whatever value for hashcode
  942. // was written on the stream, we ignore it and recalculate it on demand. This
  943. // is necessary because String.hashCode() calculates is hash code differently
  944. // in version 1.2 than it did in 1.1.
  945. in.defaultReadObject();
  946. hashcode = -1;
  947. language = convertOldISOCodes(language);
  948. country = country.intern();
  949. variant = variant.intern();
  950. }
  951. /**
  952. * List of all 2-letter language codes currently defined in ISO 639.
  953. * (Because the Java VM specification turns an array constant into executable code
  954. * that generates the array element by element, we keep the array in compressed
  955. * form in a single string and build the array from it at run time when requested.)
  956. * [We're now also using this table to store a mapping from 2-letter ISO language codes
  957. * to 3-letter ISO language codes. Each group of characters consists of a comma, a
  958. * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
  959. * a comma followed by a 2-letter code and then getting the three letters following
  960. * the 2-letter code.]
  961. */
  962. private static String[] isoLanguages = null;
  963. private static final String compressedIsoLanguages =
  964. ",aaaar,ababk,afafr,amamh,arara,asasm,ayaym,azaze,babak,bebel,bgbul,bhbih,bibis,bnben,"
  965. + "bobod,brbre,cacat,cocos,csces,cycym,dadan,dedeu,dzdzo,elell,eneng,eoepo,esspa,"
  966. + "etest,eueus,fafas,fifin,fjfij,fofao,frfra,fyfry,gagai,gdgdh,glglg,gngrn,guguj,"
  967. + "hahau,heheb,hihin,hrhrv,huhun,hyhye,iaina,idind,ieile,ikipk,inind,isisl,itita,"
  968. + "iuiku,iwheb,jajpn,jiyid,jwjaw,kakat,kkkaz,klkal,kmkhm,knkan,kokor,kskas,kukur,"
  969. + "kykir,lalat,lnlin,lolao,ltlit,lvlav,mgmlg,mimri,mkmkd,mlmal,mnmon,momol,mrmar,"
  970. + "msmsa,mtmlt,mymya,nanau,nenep,nlnld,nonor,ococi,omorm,orori,papan,plpol,pspus,"
  971. + "ptpor,quque,rmroh,rnrun,roron,rurus,rwkin,sasan,sdsnd,sgsag,shsrp,sisin,skslk,"
  972. + "slslv,smsmo,snsna,sosom,sqsqi,srsrp,ssssw,stsot,susun,svswe,swswa,tatam,tetel,"
  973. + "tgtgk,ththa,titir,tktuk,tltgl,tntsn,toton,trtur,tstso,tttat,twtwi,uguig,ukukr,"
  974. + "ururd,uzuzb,vivie,vovol,wowol,xhxho,yiyid,yoyor,zazha,zhzho,zuzul";
  975. /**
  976. * List of all 2-letter country codes currently defined in ISO 3166.
  977. * (Because the Java VM specification turns an array constant into executable code
  978. * that generates the array element by element, we keep the array in compressed
  979. * form in a single string and build the array from it at run time when requested.)
  980. * [We're now also using this table to store a mapping from 2-letter ISO country codes
  981. * to 3-letter ISO country codes. Each group of characters consists of a comma, a
  982. * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
  983. * a comma followed by a 2-letter code and then getting the three letters following
  984. * the 2-letter code.]
  985. */
  986. private static String[] isoCountries = null;
  987. private static final String compressedIsoCountries =
  988. ",ADAND,AEARE,AFAFG,AGATG,AIAIA,ALALB,AMARM,ANANT,AOAGO,AQATA,ARARG,ASASM,ATAUT,"
  989. + "AUAUS,AWABW,AZAZE,BABIH,BBBRB,BDBGD,BEBEL,BFBFA,BGBGR,BHBHR,BIBDI,BJBEN,BMBMU,"
  990. + "BNBRN,BOBOL,BRBRA,BSBHS,BTBTN,BVBVT,BWBWA,BYBLR,BZBLZ,CACAN,CCCCK,CFCAF,CGCOG,"
  991. + "CHCHE,CICIV,CKCOK,CLCHL,CMCMR,CNCHN,COCOL,CRCRI,CUCUB,CVCPV,CXCXR,CYCYP,CZCZE,"
  992. + "DEDEU,DJDJI,DKDNK,DMDMA,DODOM,DZDZA,ECECU,EEEST,EGEGY,EHESH,ERERI,ESESP,ETETH,"
  993. + "FIFIN,FJFJI,FKFLK,FMFSM,FOFRO,FRFRA,FXFXX,GAGAB,GBGBR,GDGRD,GEGEO,GFGUF,GHGHA,"
  994. + "GIGIB,GLGRL,GMGMB,GNGIN,GPGLP,GQGNQ,GRGRC,GSSGS,GTGTM,GUGUM,GWGNB,GYGUY,HKHKG,"
  995. + "HMHMD,HNHND,HRHRV,HTHTI,HUHUN,IDIDN,IEIRL,ILISR,ININD,IOIOT,IQIRQ,IRIRN,ISISL,"
  996. + "ITITA,JMJAM,JOJOR,JPJPN,KEKEN,KGKGZ,KHKHM,KIKIR,KMCOM,KNKNA,KPPRK,KRKOR,KWKWT,"
  997. + "KYCYM,KZKAZ,LALAO,LBLBN,LCLCA,LILIE,LKLKA,LRLBR,LSLSO,LTLTU,LULUX,LVLVA,LYLBY,"
  998. + "MAMAR,MCMCO,MDMDA,MGMDG,MHMHL,MKMKD,MLMLI,MMMMR,MNMNG,MOMAC,MPMNP,MQMTQ,MRMRT,"
  999. + "MSMSR,MTMLT,MUMUS,MVMDV,MWMWI,MXMEX,MYMYS,MZMOZ,NANAM,NCNCL,NENER,NFNFK,NGNGA,"
  1000. + "NINIC,NLNLD,NONOR,NPNPL,NRNRU,NUNIU,NZNZL,OMOMN,PAPAN,PEPER,PFPYF,PGPNG,PHPHL,"
  1001. + "PKPAK,PLPOL,PMSPM,PNPCN,PRPRI,PTPRT,PWPLW,PYPRY,QAQAT,REREU,ROROM,RURUS,RWRWA,"
  1002. + "SASAU,SBSLB,SCSYC,SDSDN,SESWE,SGSGP,SHSHN,SISVN,SJSJM,SKSVK,SLSLE,SMSMR,SNSEN,"
  1003. + "SOSOM,SRSUR,STSTP,SVSLV,SYSYR,SZSWZ,TCTCA,TDTCD,TFATF,TGTGO,THTHA,TJTJK,TKTKL,"
  1004. + "TMTKM,TNTUN,TOTON,TPTMP,TRTUR,TTTTO,TVTUV,TWTWN,TZTZA,UAUKR,UGUGA,UMUMI,USUSA,"
  1005. + "UYURY,UZUZB,VAVAT,VCVCT,VEVEN,VGVGB,VIVIR,VNVNM,VUVUT,WFWLF,WSWSM,YEYEM,YTMYT,"
  1006. + "YUYUG,ZAZAF,ZMZMB,ZRZAR,ZWZWE";
  1007. /*
  1008. * Locale needs its own, locale insenitive version of toLowerCase to
  1009. * avoid circularity problems between Locale and String.
  1010. * The most straightforward algorithm is used. Look at optimizations later.
  1011. */
  1012. private String toLowerCase(String str) {
  1013. char[] buf = str.toCharArray();
  1014. for (int i = 0; i < buf.length; i++) {
  1015. buf[i] = Character.toLowerCase( buf[i] );
  1016. }
  1017. return new String( buf );
  1018. }
  1019. /*
  1020. * Locale needs its own, locale insensitive version of toUpperCase to
  1021. * avoid circularity problems between Locale and String.
  1022. * The most straightforward algorithm is used. Look at optimizations later.
  1023. */
  1024. private String toUpperCase(String str) {
  1025. char[] buf = str.toCharArray();
  1026. for (int i = 0; i < buf.length; i++) {
  1027. buf[i] = Character.toUpperCase( buf[i] );
  1028. }
  1029. return new String( buf );
  1030. }
  1031. private String findStringMatch(String[][] languages,
  1032. String desiredLanguage, String fallbackLanguage)
  1033. {
  1034. for (int i = 0; i < languages.length; ++i)
  1035. if (desiredLanguage.equals(languages[i][0]))
  1036. return languages[i][1];
  1037. if (!fallbackLanguage.equals(desiredLanguage))
  1038. for (int i = 0; i < languages.length; ++i)
  1039. if (fallbackLanguage.equals(languages[i][0]))
  1040. return languages[i][1];
  1041. if (!"EN".equals(desiredLanguage) && "EN".equals(fallbackLanguage))
  1042. for (int i = 0; i < languages.length; ++i)
  1043. if ("EN".equals(languages[i][0]))
  1044. return languages[i][1];
  1045. return "";
  1046. }
  1047. private String convertOldISOCodes(String language) {
  1048. // we accept both the old and the new ISO codes for the languages whose ISO
  1049. // codes have changed, but we always store the OLD code, for backward compatibility
  1050. language = toLowerCase(language).intern();
  1051. if (language == "he") {
  1052. return "iw";
  1053. } else if (language == "yi") {
  1054. return "ji";
  1055. } else if (language == "id") {
  1056. return "in";
  1057. } else {
  1058. return language;
  1059. }
  1060. }
  1061. }