1. /*
  2. * @(#)Locale.java 1.79 04/05/10
  3. *
  4. * Copyright 2004 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.loc.gov/standards/iso639-2/englangn.html">
  50. * <code>http://www.loc.gov/standards/iso639-2/englangn.html</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.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">
  57. * <code>http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.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. * @see ResourceBundle
  132. * @see java.text.Format
  133. * @see java.text.NumberFormat
  134. * @see java.text.Collator
  135. * @author Mark Davis
  136. * @since 1.1
  137. */
  138. public final class Locale implements Cloneable, Serializable {
  139. /** Useful constant for language.
  140. */
  141. static public final Locale ENGLISH = new Locale("en","","");
  142. /** Useful constant for language.
  143. */
  144. static public final Locale FRENCH = new Locale("fr","","");
  145. /** Useful constant for language.
  146. */
  147. static public final Locale GERMAN = new Locale("de","","");
  148. /** Useful constant for language.
  149. */
  150. static public final Locale ITALIAN = new Locale("it","","");
  151. /** Useful constant for language.
  152. */
  153. static public final Locale JAPANESE = new Locale("ja","","");
  154. /** Useful constant for language.
  155. */
  156. static public final Locale KOREAN = new Locale("ko","","");
  157. /** Useful constant for language.
  158. */
  159. static public final Locale CHINESE = new Locale("zh","","");
  160. /** Useful constant for language.
  161. */
  162. static public final Locale SIMPLIFIED_CHINESE = new Locale("zh","CN","");
  163. /** Useful constant for language.
  164. */
  165. static public final Locale TRADITIONAL_CHINESE = new Locale("zh","TW","");
  166. /** Useful constant for country.
  167. */
  168. static public final Locale FRANCE = new Locale("fr","FR","");
  169. /** Useful constant for country.
  170. */
  171. static public final Locale GERMANY = new Locale("de","DE","");
  172. /** Useful constant for country.
  173. */
  174. static public final Locale ITALY = new Locale("it","IT","");
  175. /** Useful constant for country.
  176. */
  177. static public final Locale JAPAN = new Locale("ja","JP","");
  178. /** Useful constant for country.
  179. */
  180. static public final Locale KOREA = new Locale("ko","KR","");
  181. /** Useful constant for country.
  182. */
  183. static public final Locale CHINA = new Locale("zh","CN","");
  184. /** Useful constant for country.
  185. */
  186. static public final Locale PRC = new Locale("zh","CN","");
  187. /** Useful constant for country.
  188. */
  189. static public final Locale TAIWAN = new Locale("zh","TW","");
  190. /** Useful constant for country.
  191. */
  192. static public final Locale UK = new Locale("en","GB","");
  193. /** Useful constant for country.
  194. */
  195. static public final Locale US = new Locale("en","US","");
  196. /** Useful constant for country.
  197. */
  198. static public final Locale CANADA = new Locale("en","CA","");
  199. /** Useful constant for country.
  200. */
  201. static public final Locale CANADA_FRENCH = new Locale("fr","CA","");
  202. /** serialization ID
  203. */
  204. static final long serialVersionUID = 9149081749638150636L;
  205. /**
  206. * Construct a locale from language, country, variant.
  207. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  208. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  209. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  210. * API on Locale will return only the OLD codes.
  211. * @param language lowercase two-letter ISO-639 code.
  212. * @param country uppercase two-letter ISO-3166 code.
  213. * @param variant vendor and browser specific code. See class description.
  214. * @exception NullPointerException thrown if any argument is null.
  215. */
  216. public Locale(String language, String country, String variant) {
  217. this.language = convertOldISOCodes(language);
  218. this.country = toUpperCase(country).intern();
  219. this.variant = variant.intern();
  220. }
  221. /**
  222. * Construct a locale from language, country.
  223. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  224. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  225. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  226. * API on Locale will return only the OLD codes.
  227. * @param language lowercase two-letter ISO-639 code.
  228. * @param country uppercase two-letter ISO-3166 code.
  229. * @exception NullPointerException thrown if either argument is null.
  230. */
  231. public Locale(String language, String country) {
  232. this(language, country, "");
  233. }
  234. /**
  235. * Construct a locale from a language code.
  236. * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
  237. * (specifically iw, ji, and in) have changed. This constructor accepts both the
  238. * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
  239. * API on Locale will return only the OLD codes.
  240. * @param language lowercase two-letter ISO-639 code.
  241. * @exception NullPointerException thrown if argument is null.
  242. * @since 1.4
  243. */
  244. public Locale(String language) {
  245. this(language, "", "");
  246. }
  247. /**
  248. * Gets the current value of the default locale for this instance
  249. * of the Java Virtual Machine.
  250. * <p>
  251. * The Java Virtual Machine sets the default locale during startup
  252. * based on the host environment. It is used by many locale-sensitive
  253. * methods if no locale is explicitly specified.
  254. * It can be changed using the
  255. * {@link #setDefault(java.util.Locale) setDefault} method.
  256. *
  257. * @return the default locale for this instance of the Java Virtual Machine
  258. */
  259. public static Locale getDefault() {
  260. // do not synchronize this method - see 4071298
  261. // it's OK if more than one default locale happens to be created
  262. if (defaultLocale == null) {
  263. String language, region, country, variant;
  264. language = (String) AccessController.doPrivileged(
  265. new GetPropertyAction("user.language", "en"));
  266. // for compatibility, check for old user.region property
  267. region = (String) AccessController.doPrivileged(
  268. new GetPropertyAction("user.region"));
  269. if (region != null) {
  270. // region can be of form country, country_variant, or _variant
  271. int i = region.indexOf('_');
  272. if (i >= 0) {
  273. country = region.substring(0, i);
  274. variant = region.substring(i + 1);
  275. } else {
  276. country = region;
  277. variant = "";
  278. }
  279. } else {
  280. country = (String) AccessController.doPrivileged(
  281. new GetPropertyAction("user.country", ""));
  282. variant = (String) AccessController.doPrivileged(
  283. new GetPropertyAction("user.variant", ""));
  284. }
  285. defaultLocale = new Locale(language, country, variant);
  286. }
  287. return defaultLocale;
  288. }
  289. /**
  290. * Sets the default locale for this instance of the Java Virtual Machine.
  291. * This does not affect the host locale.
  292. * <p>
  293. * If there is a security manager, its <code>checkPermission</code>
  294. * method is called with a <code>PropertyPermission("user.language", "write")</code>
  295. * permission before the default locale is changed.
  296. * <p>
  297. * The Java Virtual Machine sets the default locale during startup
  298. * based on the host environment. It is used by many locale-sensitive
  299. * methods if no locale is explicitly specified.
  300. * <p>
  301. * Since changing the default locale may affect many different areas
  302. * of functionality, this method should only be used if the caller
  303. * is prepared to reinitialize locale-sensitive code running
  304. * within the same Java Virtual Machine, such as the user interface.
  305. *
  306. * @throws SecurityException
  307. * if a security manager exists and its
  308. * <code>checkPermission</code> method doesn't allow the operation.
  309. * @throws NullPointerException if <code>newLocale</code> is null
  310. * @param newLocale the new default locale
  311. * @see SecurityManager#checkPermission
  312. * @see java.util.PropertyPermission
  313. */
  314. public static synchronized void setDefault(Locale newLocale) {
  315. if (newLocale == null)
  316. throw new NullPointerException("Can't set default locale to NULL");
  317. SecurityManager sm = System.getSecurityManager();
  318. if (sm != null) sm.checkPermission(new PropertyPermission
  319. ("user.language", "write"));
  320. defaultLocale = newLocale;
  321. }
  322. /**
  323. * Returns an array of all installed locales.
  324. * The array returned must contain at least a <code>Locale</code>
  325. * instance equal to {@link java.util.Locale#US Locale.US}.
  326. *
  327. * @return An array of installed locales.
  328. */
  329. public static Locale[] getAvailableLocales() {
  330. return LocaleData.getAvailableLocales("LocaleString");
  331. }
  332. /**
  333. * Returns a list of all 2-letter country codes defined in ISO 3166.
  334. * Can be used to create Locales.
  335. */
  336. public static String[] getISOCountries() {
  337. if (isoCountries == null) {
  338. isoCountries = new String[compressedIsoCountries.length() / 6];
  339. for (int i = 0; i < isoCountries.length; i++)
  340. isoCountries[i] = compressedIsoCountries.substring((i * 6) + 1, (i * 6) + 3);
  341. }
  342. String[] result = new String[isoCountries.length];
  343. System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
  344. return result;
  345. }
  346. /**
  347. * Returns a list of all 2-letter language codes defined in ISO 639.
  348. * Can be used to create Locales.
  349. * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
  350. * The list this function returns includes both the new and the old codes for the
  351. * languages whose codes have changed.]
  352. */
  353. public static String[] getISOLanguages() {
  354. if (isoLanguages == null) {
  355. isoLanguages = new String[compressedIsoLanguages.length() / 6];
  356. for (int i = 0; i < isoLanguages.length; i++)
  357. isoLanguages[i] = compressedIsoLanguages.substring((i * 6) + 1, (i * 6) + 3);
  358. }
  359. String[] result = new String[isoLanguages.length];
  360. System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
  361. return result;
  362. }
  363. /**
  364. * Returns the language code for this locale, which will either be the empty string
  365. * or a lowercase ISO 639 code.
  366. * <p>NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
  367. * Locale's constructor recognizes both the new and the old codes for the languages
  368. * whose codes have changed, but this function always returns the old code. If you
  369. * want to check for a specific language whose code has changed, don't do <pre>
  370. * if (locale.getLanguage().equals("he")
  371. * ...
  372. * </pre>Instead, do<pre>
  373. * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage())
  374. * ...</pre>
  375. * @see #getDisplayLanguage
  376. */
  377. public String getLanguage() {
  378. return language;
  379. }
  380. /**
  381. * Returns the country/region code for this locale, which will
  382. * either be the empty string or an uppercase ISO 3166 2-letter code.
  383. * @see #getDisplayCountry
  384. */
  385. public String getCountry() {
  386. return country;
  387. }
  388. /**
  389. * Returns the variant code for this locale.
  390. * @see #getDisplayVariant
  391. */
  392. public String getVariant() {
  393. return variant;
  394. }
  395. /**
  396. * Getter for the programmatic name of the entire locale,
  397. * with the language, country and variant separated by underbars.
  398. * Language is always lower case, and country is always upper case.
  399. * If the language is missing, the string will begin with an underbar.
  400. * If both the language and country fields are missing, this function
  401. * will return the empty string, even if the variant field is filled in
  402. * (you can't have a locale with just a variant-- the variant must accompany
  403. * a valid language or country code).
  404. * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
  405. * @see #getDisplayName
  406. */
  407. public final String toString() {
  408. boolean l = language.length() != 0;
  409. boolean c = country.length() != 0;
  410. boolean v = variant.length() != 0;
  411. StringBuffer result = new StringBuffer(language);
  412. if (c||(l&&v)) {
  413. result.append('_').append(country); // This may just append '_'
  414. }
  415. if (v&&(l||c)) {
  416. result.append('_').append(variant);
  417. }
  418. return result.toString();
  419. }
  420. /**
  421. * Returns a three-letter abbreviation for this locale's language. If the locale
  422. * doesn't specify a language, this will be the empty string. Otherwise, this will
  423. * be a lowercase ISO 639-2/T language code.
  424. * The ISO 639-2 language codes can be found on-line at
  425. * <a href="http://www.loc.gov/standards/iso639-2/englangn.html"><code>http://www.loc.gov/standards/iso639-2/englangn.html</code></a>
  426. * @exception MissingResourceException Throws MissingResourceException if the
  427. * three-letter language abbreviation is not available for this locale.
  428. */
  429. public String getISO3Language() throws MissingResourceException {
  430. int length = language.length();
  431. if (length == 0) {
  432. return "";
  433. }
  434. int index = compressedIsoLanguages.indexOf("," + language);
  435. if (index == -1 || length != 2) {
  436. throw new MissingResourceException("Couldn't find 3-letter language code for "
  437. + language, "LocaleElements_" + toString(), "ShortLanguage");
  438. }
  439. return compressedIsoLanguages.substring(index + 3, index + 6);
  440. }
  441. /**
  442. * Returns a three-letter abbreviation for this locale's country. If the locale
  443. * doesn't specify a country, this will be the empty string. Otherwise, this will
  444. * be an uppercase ISO 3166 3-letter country code.
  445. * The ISO 3166-2 country codes can be found on-line at
  446. * <a href="http://www.davros.org/misc/iso3166.txt"><code>http://www.davros.org/misc/iso3166.txt</code></a>
  447. * @exception MissingResourceException Throws MissingResourceException if the
  448. * three-letter country abbreviation is not available for this locale.
  449. */
  450. public String getISO3Country() throws MissingResourceException {
  451. int length = country.length();
  452. if (length == 0) {
  453. return "";
  454. }
  455. int index = compressedIsoCountries.indexOf("," + country);
  456. if (index == -1 || length != 2) {
  457. throw new MissingResourceException("Couldn't find 3-letter country code for "
  458. + country, "LocaleElements_" + toString(), "ShortCountry");
  459. }
  460. return compressedIsoCountries.substring(index + 3, index + 6);
  461. }
  462. /**
  463. * Returns a name for the locale's language that is appropriate for display to the
  464. * user.
  465. * If possible, the name returned will be localized for the default locale.
  466. * For example, if the locale is fr_FR and the default locale
  467. * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  468. * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
  469. * If the name returned cannot be localized for the default locale,
  470. * (say, we don't have a Japanese name for Croatian),
  471. * this function falls back on the English name, and uses the ISO code as a last-resort
  472. * value. If the locale doesn't specify a language, this function returns the empty string.
  473. */
  474. public final String getDisplayLanguage() {
  475. return getDisplayLanguage(getDefault());
  476. }
  477. /**
  478. * Returns a name for the locale's language that is appropriate for display to the
  479. * user.
  480. * If possible, the name returned will be localized according to inLocale.
  481. * For example, if the locale is fr_FR and inLocale
  482. * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
  483. * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
  484. * If the name returned cannot be localized according to inLocale,
  485. * (say, we don't have a Japanese name for Croatian),
  486. * this function falls back on the default locale, on the English name, and finally
  487. * on the ISO code as a last-resort value. If the locale doesn't specify a language,
  488. * this function returns the empty string.
  489. */
  490. public String getDisplayLanguage(Locale inLocale) {
  491. String langCode = language;
  492. if (langCode.length() == 0)
  493. return "";
  494. Locale workingLocale = (Locale)inLocale.clone();
  495. String result = null;
  496. int phase = 0;
  497. boolean done = false;
  498. if (workingLocale.variant.length() == 0)
  499. phase = 1;
  500. if (workingLocale.country.length() == 0)
  501. phase = 2;
  502. while (!done) {
  503. try {
  504. ResourceBundle bundle = LocaleData.getLocaleElements(workingLocale);
  505. result = findStringMatch((String[][])bundle.getObject("Languages"),
  506. langCode, langCode);
  507. if (result.length() != 0)
  508. done = true;
  509. }
  510. catch (Exception e) {
  511. // just fall through
  512. }
  513. if (!done) {
  514. switch (phase) {
  515. case 0:
  516. workingLocale = new Locale(workingLocale.language,
  517. workingLocale.country,
  518. "");
  519. break;
  520. case 1:
  521. workingLocale = new Locale(workingLocale.language,
  522. "",
  523. workingLocale.variant);
  524. break;
  525. case 2:
  526. workingLocale = getDefault();
  527. break;
  528. case 3:
  529. workingLocale = new Locale("", "", "");
  530. break;
  531. default:
  532. return langCode;
  533. }
  534. phase++;
  535. }
  536. }
  537. return result;
  538. }
  539. /**
  540. * Returns a name for the locale's country that is appropriate for display to the
  541. * user.
  542. * If possible, the name returned will be localized for the default locale.
  543. * For example, if the locale is fr_FR and the default locale
  544. * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  545. * the default locale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  546. * If the name returned cannot be localized for the default locale,
  547. * (say, we don't have a Japanese name for Croatia),
  548. * this function falls back on the English name, and uses the ISO code as a last-resort
  549. * value. If the locale doesn't specify a country, this function returns the empty string.
  550. */
  551. public final String getDisplayCountry() {
  552. return getDisplayCountry(getDefault());
  553. }
  554. /**
  555. * Returns a name for the locale's country that is appropriate for display to the
  556. * user.
  557. * If possible, the name returned will be localized according to inLocale.
  558. * For example, if the locale is fr_FR and inLocale
  559. * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
  560. * inLocale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
  561. * If the name returned cannot be localized according to inLocale.
  562. * (say, we don't have a Japanese name for Croatia),
  563. * this function falls back on the default locale, on the English name, and finally
  564. * on the ISO code as a last-resort value. If the locale doesn't specify a country,
  565. * this function returns the empty string.
  566. */
  567. public String getDisplayCountry(Locale inLocale) {
  568. String ctryCode = country;
  569. if (ctryCode.length() == 0)
  570. return "";
  571. Locale workingLocale = (Locale)inLocale.clone();
  572. String result = null;
  573. int phase = 0;
  574. boolean done = false;
  575. if (workingLocale.variant.length() == 0)
  576. phase = 1;
  577. if (workingLocale.country.length() == 0)
  578. phase = 2;
  579. while (!done) {
  580. try {
  581. ResourceBundle bundle = LocaleData.getLocaleElements(workingLocale);
  582. result = findStringMatch((String[][])bundle.getObject("Countries"),
  583. ctryCode, ctryCode);
  584. if (result.length() != 0)
  585. done = true;
  586. }
  587. catch (Exception e) {
  588. // just fall through
  589. }
  590. if (!done) {
  591. switch (phase) {
  592. case 0:
  593. workingLocale = new Locale(workingLocale.language,
  594. workingLocale.country,
  595. "");
  596. break;
  597. case 1:
  598. workingLocale = new Locale(workingLocale.language,
  599. "",
  600. workingLocale.variant);
  601. break;
  602. case 2:
  603. workingLocale = getDefault();
  604. break;
  605. case 3:
  606. workingLocale = new Locale("", "", "");
  607. break;
  608. default:
  609. return ctryCode;
  610. }
  611. phase++;
  612. }
  613. }
  614. return result;
  615. }
  616. /**
  617. * Returns a name for the locale's variant code that is appropriate for display to the
  618. * user. If possible, the name will be localized for the default locale. If the locale
  619. * doesn't specify a variant code, this function returns the empty string.
  620. */
  621. public final String getDisplayVariant() {
  622. return getDisplayVariant(getDefault());
  623. }
  624. /**
  625. * Returns a name for the locale's variant code that is appropriate for display to the
  626. * user. If possible, the name will be localized for inLocale. If the locale
  627. * doesn't specify a variant code, this function returns the empty string.
  628. */
  629. public String getDisplayVariant(Locale inLocale) {
  630. if (variant.length() == 0)
  631. return "";
  632. ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
  633. String names[] = getDisplayVariantArray(bundle);
  634. // Get the localized patterns for formatting a list, and use
  635. // them to format the list.
  636. String[] patterns;
  637. try {
  638. patterns = (String[])bundle.getObject("LocaleNamePatterns");
  639. }
  640. catch (MissingResourceException e) {
  641. patterns = null;
  642. }
  643. return formatList(patterns, names);
  644. }
  645. /**
  646. * Returns a name for the locale that is appropriate for display to the
  647. * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  648. * and getDisplayVariant() assembled into a single string. The display name will have
  649. * one of the following forms:<p><blockquote>
  650. * language (country, variant)<p>
  651. * language (country)<p>
  652. * language (variant)<p>
  653. * country (variant)<p>
  654. * language<p>
  655. * country<p>
  656. * variant<p></blockquote>
  657. * depending on which fields are specified in the locale. If the language, country,
  658. * and variant fields are all empty, this function returns the empty string.
  659. */
  660. public final String getDisplayName() {
  661. return getDisplayName(getDefault());
  662. }
  663. /**
  664. * Returns a name for the locale that is appropriate for display to the
  665. * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
  666. * and getDisplayVariant() assembled into a single string. The display name will have
  667. * one of the following forms:<p><blockquote>
  668. * language (country, variant)<p>
  669. * language (country)<p>
  670. * language (variant)<p>
  671. * country (variant)<p>
  672. * language<p>
  673. * country<p>
  674. * variant<p></blockquote>
  675. * depending on which fields are specified in the locale. If the language, country,
  676. * and variant fields are all empty, this function returns the empty string.
  677. */
  678. public String getDisplayName(Locale inLocale) {
  679. ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
  680. String languageName = getDisplayLanguage(inLocale);
  681. String countryName = getDisplayCountry(inLocale);
  682. String[] variantNames = getDisplayVariantArray(bundle);
  683. // Get the localized patterns for formatting a display name.
  684. String[] patterns;
  685. try {
  686. patterns = (String[])bundle.getObject("LocaleNamePatterns");
  687. }
  688. catch (MissingResourceException e) {
  689. patterns = null;
  690. }
  691. // The display name consists of a main name, followed by qualifiers.
  692. // Typically, the format is "MainName (Qualifier, Qualifier)" but this
  693. // depends on what pattern is stored in the display locale.
  694. String mainName = null;
  695. String[] qualifierNames = null;
  696. // The main name is the language, or if there is no language, the country.
  697. // If there is neither language nor country (an anomalous situation) then
  698. // the display name is simply the variant's display name.
  699. if (languageName.length() != 0) {
  700. mainName = languageName;
  701. if (countryName.length() != 0) {
  702. qualifierNames = new String[variantNames.length + 1];
  703. System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
  704. qualifierNames[0] = countryName;
  705. }
  706. else qualifierNames = variantNames;
  707. }
  708. else if (countryName.length() != 0) {
  709. mainName = countryName;
  710. qualifierNames = variantNames;
  711. }
  712. else {
  713. return formatList(patterns, variantNames);
  714. }
  715. // Create an array whose first element is the number of remaining
  716. // elements. This serves as a selector into a ChoiceFormat pattern from
  717. // the resource. The second and third elements are the main name and
  718. // the qualifier; if there are no qualifiers, the third element is
  719. // unused by the format pattern.
  720. Object[] displayNames = {
  721. new Integer(qualifierNames.length != 0 ? 2 : 1),
  722. mainName,
  723. // We could also just call formatList() and have it handle the empty
  724. // list case, but this is more efficient, and we want it to be
  725. // efficient since all the language-only locales will not have any
  726. // qualifiers.
  727. qualifierNames.length != 0 ? formatList(patterns, qualifierNames) : null
  728. };
  729. if (patterns != null) {
  730. return new MessageFormat(patterns[0]).format(displayNames);
  731. }
  732. else {
  733. // If we cannot get the message format pattern, then we use a simple
  734. // hard-coded pattern. This should not occur in practice unless the
  735. // installation is missing some core files (LocaleElements etc.).
  736. StringBuffer result = new StringBuffer();
  737. result.append((String)displayNames[1]);
  738. if (displayNames.length > 2) {
  739. result.append(" (");
  740. result.append((String)displayNames[2]);
  741. result.append(")");
  742. }
  743. return result.toString();
  744. }
  745. }
  746. /**
  747. * Overrides Cloneable
  748. */
  749. public Object clone()
  750. {
  751. try {
  752. Locale that = (Locale)super.clone();
  753. return that;
  754. } catch (CloneNotSupportedException e) {
  755. throw new InternalError();
  756. }
  757. }
  758. /**
  759. * Override hashCode.
  760. * Since Locales are often used in hashtables, caches the value
  761. * for speed.
  762. */
  763. public int hashCode() {
  764. int hc = hashCodeValue;
  765. if (hc == 0) {
  766. hc = (language.hashCode() << 8) ^ country.hashCode() ^ (variant.hashCode() << 4);
  767. hashCodeValue = hc;
  768. }
  769. return hc;
  770. }
  771. // Overrides
  772. /**
  773. * Returns true if this Locale is equal to another object. A Locale is
  774. * deemed equal to another Locale with identical language, country,
  775. * and variant, and unequal to all other objects.
  776. *
  777. * @return true if this Locale is equal to the specified object.
  778. */
  779. public boolean equals(Object obj) {
  780. if (this == obj) // quick check
  781. return true;
  782. if (!(obj instanceof Locale))
  783. return false;
  784. Locale other = (Locale) obj;
  785. return language == other.language
  786. && country == other.country
  787. && variant == other.variant;
  788. }
  789. // ================= privates =====================================
  790. // XXX instance and class variables. For now keep these separate, since it is
  791. // faster to match. Later, make into single string.
  792. /**
  793. * @serial
  794. * @see #getLanguage
  795. */
  796. private final String language;
  797. /**
  798. * @serial
  799. * @see #getCountry
  800. */
  801. private final String country;
  802. /**
  803. * @serial
  804. * @see #getVariant
  805. */
  806. private final String variant;
  807. /**
  808. * Placeholder for the object's hash code. Always -1.
  809. * @serial
  810. */
  811. private volatile int hashcode = -1; // lazy evaluate
  812. /**
  813. * Calculated hashcode to fix 4518797.
  814. */
  815. private transient volatile int hashCodeValue = 0;
  816. private static Locale defaultLocale = null;
  817. /**
  818. * Return an array of the display names of the variant.
  819. * @param bundle the ResourceBundle to use to get the display names
  820. * @return an array of display names, possible of zero length.
  821. */
  822. private String[] getDisplayVariantArray(ResourceBundle bundle) {
  823. // Split the variant name into tokens separated by '_'.
  824. StringTokenizer tokenizer = new StringTokenizer(variant, "_");
  825. String[] names = new String[tokenizer.countTokens()];
  826. // For each variant token, lookup the display name. If
  827. // not found, use the variant name itself.
  828. for (int i=0; i<names.length; ++i) {
  829. String token = tokenizer.nextToken();
  830. try {
  831. names[i] = (String)bundle.getObject("%%" + token);
  832. }
  833. catch (MissingResourceException e) {
  834. names[i] = token;
  835. }
  836. }
  837. return names;
  838. }
  839. /**
  840. * Format a list with an array of patterns.
  841. * @param patterns an array of three patterns. The first pattern is not
  842. * used. The second pattern should create a MessageFormat taking 0-3 arguments
  843. * and formatting them into a list. The third pattern should take 2 arguments
  844. * and is used by composeList. If patterns is null, then a the list is
  845. * formatted by concatenation with the delimiter ','.
  846. * @param stringList the list of strings to be formatted.
  847. * @return a string representing the list.
  848. */
  849. private static String formatList(String[] patterns, String[] stringList) {
  850. // If we have no list patterns, compose the list in a simple,
  851. // non-localized way.
  852. if (patterns == null) {
  853. StringBuffer result = new StringBuffer();
  854. for (int i=0; i<stringList.length; ++i) {
  855. if (i>0) result.append(',');
  856. result.append(stringList[i]);
  857. }
  858. return result.toString();
  859. }
  860. // Compose the list down to three elements if necessary
  861. if (stringList.length > 3) {
  862. MessageFormat format = new MessageFormat(patterns[2]);
  863. stringList = composeList(format, stringList);
  864. }
  865. // Rebuild the argument list with the list length as the first element
  866. Object[] args = new Object[stringList.length + 1];
  867. System.arraycopy(stringList, 0, args, 1, stringList.length);
  868. args[0] = new Integer(stringList.length);
  869. // Format it using the pattern in the resource
  870. MessageFormat format = new MessageFormat(patterns[1]);
  871. return format.format(args);
  872. }
  873. /**
  874. * Given a list of strings, return a list shortened to three elements.
  875. * Shorten it by applying the given format to the first two elements
  876. * recursively.
  877. * @param format a format which takes two arguments
  878. * @param list a list of strings
  879. * @return if the list is three elements or shorter, the same list;
  880. * otherwise, a new list of three elements.
  881. */
  882. private static String[] composeList(MessageFormat format, String[] list) {
  883. if (list.length <= 3) return list;
  884. // Use the given format to compose the first two elements into one
  885. String[] listItems = { list[0], list[1] };
  886. String newItem = format.format(listItems);
  887. // Form a new list one element shorter
  888. String[] newList = new String[list.length-1];
  889. System.arraycopy(list, 2, newList, 1, newList.length-1);
  890. newList[0] = newItem;
  891. // Recurse
  892. return composeList(format, newList);
  893. }
  894. /**
  895. * Replace the deserialized Locale object with a newly
  896. * created object. Older language codes are replaced with newer ISO
  897. * codes. The country and variant codes are replaced with internalized
  898. * String copies.
  899. */
  900. private Object readResolve() throws java.io.ObjectStreamException {
  901. return new Locale(language, country, variant);
  902. }
  903. /**
  904. * List of all 2-letter language codes currently defined in ISO 639.
  905. * (Because the Java VM specification turns an array constant into executable code
  906. * that generates the array element by element, we keep the array in compressed
  907. * form in a single string and build the array from it at run time when requested.)
  908. * [We're now also using this table to store a mapping from 2-letter ISO language codes
  909. * to 3-letter ISO language codes. Each group of characters consists of a comma, a
  910. * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
  911. * a comma followed by a 2-letter code and then getting the three letters following
  912. * the 2-letter code.]
  913. */
  914. private static String[] isoLanguages = null;
  915. private static final String compressedIsoLanguages =
  916. ",aaaar,ababk,aeave,afafr,akaka,amamh,anarg,arara,asasm,avava"
  917. + ",ayaym,azaze,babak,bebel,bgbul,bhbih,bibis,bmbam,bnben,bobod"
  918. + ",brbre,bsbos,cacat,ceche,chcha,cocos,crcre,csces,cuchu,cvchv"
  919. + ",cycym,dadan,dedeu,dvdiv,dzdzo,eeewe,elell,eneng,eoepo,esspa"
  920. + ",etest,eueus,fafas,ffful,fifin,fjfij,fofao,frfra,fyfry,gagle"
  921. + ",gdgla,glglg,gngrn,guguj,gvglv,hahau,heheb,hihin,hohmo,hrhrv"
  922. + ",hthat,huhun,hyhye,hzher,iaina,idind,ieile,igibo,iiiii,ikipk"
  923. + ",inind,ioido,isisl,itita,iuiku,iwheb,jajpn,jiyid,jvjav,kakat"
  924. + ",kgkon,kikik,kjkua,kkkaz,klkal,kmkhm,knkan,kokor,krkau,kskas"
  925. + ",kukur,kvkom,kwcor,kykir,lalat,lbltz,lglug,lilim,lnlin,lolao"
  926. + ",ltlit,lulub,lvlav,mgmlg,mhmah,mimri,mkmkd,mlmal,mnmon,momol"
  927. + ",mrmar,msmsa,mtmlt,mymya,nanau,nbnob,ndnde,nenep,ngndo,nlnld"
  928. + ",nnnno,nonor,nrnbl,nvnav,nynya,ococi,ojoji,omorm,orori,ososs"
  929. + ",papan,pipli,plpol,pspus,ptpor,quque,rmroh,rnrun,roron,rurus"
  930. + ",rwkin,sasan,scsrd,sdsnd,sesme,sgsag,sisin,skslk,slslv,smsmo"
  931. + ",snsna,sosom,sqsqi,srsrp,ssssw,stsot,susun,svswe,swswa,tatam"
  932. + ",tetel,tgtgk,ththa,titir,tktuk,tltgl,tntsn,toton,trtur,tstso"
  933. + ",tttat,twtwi,tytah,uguig,ukukr,ururd,uzuzb,veven,vivie,vovol"
  934. + ",wawln,wowol,xhxho,yiyid,yoyor,zazha,zhzho,zuzul";
  935. /**
  936. * List of all 2-letter country codes currently defined in ISO 3166.
  937. * (Because the Java VM specification turns an array constant into executable code
  938. * that generates the array element by element, we keep the array in compressed
  939. * form in a single string and build the array from it at run time when requested.)
  940. * [We're now also using this table to store a mapping from 2-letter ISO country codes
  941. * to 3-letter ISO country codes. Each group of characters consists of a comma, a
  942. * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
  943. * a comma followed by a 2-letter code and then getting the three letters following
  944. * the 2-letter code.]
  945. */
  946. private static String[] isoCountries = null;
  947. private static final String compressedIsoCountries =
  948. ",ADAND,AEARE,AFAFG,AGATG,AIAIA,ALALB,AMARM,ANANT,AOAGO,AQATA"
  949. + ",ARARG,ASASM,ATAUT,AUAUS,AWABW,AXALA,AZAZE,BABIH,BBBRB,BDBGD,BEBEL"
  950. + ",BFBFA,BGBGR,BHBHR,BIBDI,BJBEN,BMBMU,BNBRN,BOBOL,BRBRA,BSBHS"
  951. + ",BTBTN,BVBVT,BWBWA,BYBLR,BZBLZ,CACAN,CCCCK,CDCOD,CFCAF,CGCOG"
  952. + ",CHCHE,CICIV,CKCOK,CLCHL,CMCMR,CNCHN,COCOL,CRCRI,CSSCG,CUCUB"
  953. + ",CVCPV,CXCXR,CYCYP,CZCZE,DEDEU,DJDJI,DKDNK,DMDMA,DODOM,DZDZA"
  954. + ",ECECU,EEEST,EGEGY,EHESH,ERERI,ESESP,ETETH,FIFIN,FJFJI,FKFLK"
  955. + ",FMFSM,FOFRO,FRFRA,GAGAB,GBGBR,GDGRD,GEGEO,GFGUF,GHGHA,GIGIB"
  956. + ",GLGRL,GMGMB,GNGIN,GPGLP,GQGNQ,GRGRC,GSSGS,GTGTM,GUGUM,GWGNB"
  957. + ",GYGUY,HKHKG,HMHMD,HNHND,HRHRV,HTHTI,HUHUN,IDIDN,IEIRL,ILISR"
  958. + ",ININD,IOIOT,IQIRQ,IRIRN,ISISL,ITITA,JMJAM,JOJOR,JPJPN,KEKEN"
  959. + ",KGKGZ,KHKHM,KIKIR,KMCOM,KNKNA,KPPRK,KRKOR,KWKWT,KYCYM,KZKAZ"
  960. + ",LALAO,LBLBN,LCLCA,LILIE,LKLKA,LRLBR,LSLSO,LTLTU,LULUX,LVLVA"
  961. + ",LYLBY,MAMAR,MCMCO,MDMDA,MGMDG,MHMHL,MKMKD,MLMLI,MMMMR,MNMNG"
  962. + ",MOMAC,MPMNP,MQMTQ,MRMRT,MSMSR,MTMLT,MUMUS,MVMDV,MWMWI,MXMEX"
  963. + ",MYMYS,MZMOZ,NANAM,NCNCL,NENER,NFNFK,NGNGA,NINIC,NLNLD,NONOR"
  964. + ",NPNPL,NRNRU,NUNIU,NZNZL,OMOMN,PAPAN,PEPER,PFPYF,PGPNG,PHPHL"
  965. + ",PKPAK,PLPOL,PMSPM,PNPCN,PRPRI,PSPSE,PTPRT,PWPLW,PYPRY,QAQAT"
  966. + ",REREU,ROROU,RURUS,RWRWA,SASAU,SBSLB,SCSYC,SDSDN,SESWE,SGSGP"
  967. + ",SHSHN,SISVN,SJSJM,SKSVK,SLSLE,SMSMR,SNSEN,SOSOM,SRSUR,STSTP"
  968. + ",SVSLV,SYSYR,SZSWZ,TCTCA,TDTCD,TFATF,TGTGO,THTHA,TJTJK,TKTKL"
  969. + ",TLTLS,TMTKM,TNTUN,TOTON,TRTUR,TTTTO,TVTUV,TWTWN,TZTZA,UAUKR"
  970. + ",UGUGA,UMUMI,USUSA,UYURY,UZUZB,VAVAT,VCVCT,VEVEN,VGVGB,VIVIR"
  971. + ",VNVNM,VUVUT,WFWLF,WSWSM,YEYEM,YTMYT,ZAZAF,ZMZMB,ZWZWE";
  972. /*
  973. * Locale needs its own, locale insensitive version of toLowerCase to
  974. * avoid circularity problems between Locale and String.
  975. * The most straightforward algorithm is used. Look at optimizations later.
  976. */
  977. private String toLowerCase(String str) {
  978. char[] buf = new char[str.length()];
  979. for (int i = 0; i < buf.length; i++) {
  980. buf[i] = Character.toLowerCase(str.charAt(i));
  981. }
  982. return new String( buf );
  983. }
  984. /*
  985. * Locale needs its own, locale insensitive version of toUpperCase to
  986. * avoid circularity problems between Locale and String.
  987. * The most straightforward algorithm is used. Look at optimizations later.
  988. */
  989. private String toUpperCase(String str) {
  990. char[] buf = new char[str.length()];
  991. for (int i = 0; i < buf.length; i++) {
  992. buf[i] = Character.toUpperCase(str.charAt(i));
  993. }
  994. return new String( buf );
  995. }
  996. private String findStringMatch(String[][] languages,
  997. String desiredLanguage, String fallbackLanguage)
  998. {
  999. for (int i = 0; i < languages.length; ++i)
  1000. if (desiredLanguage.equals(languages[i][0]))
  1001. return languages[i][1];
  1002. if (!fallbackLanguage.equals(desiredLanguage))
  1003. for (int i = 0; i < languages.length; ++i)
  1004. if (fallbackLanguage.equals(languages[i][0]))
  1005. return languages[i][1];
  1006. if (!"EN".equals(desiredLanguage) && "EN".equals(fallbackLanguage))
  1007. for (int i = 0; i < languages.length; ++i)
  1008. if ("EN".equals(languages[i][0]))
  1009. return languages[i][1];
  1010. return "";
  1011. }
  1012. private String convertOldISOCodes(String language) {
  1013. // we accept both the old and the new ISO codes for the languages whose ISO
  1014. // codes have changed, but we always store the OLD code, for backward compatibility
  1015. language = toLowerCase(language).intern();
  1016. if (language == "he") {
  1017. return "iw";
  1018. } else if (language == "yi") {
  1019. return "ji";
  1020. } else if (language == "id") {
  1021. return "in";
  1022. } else {
  1023. return language;
  1024. }
  1025. }
  1026. }