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