0001: /*
0002: *
0003: * @(#)Locale.java 1.64 06/10/10
0004: *
0005: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights
0006: * Reserved. Use is subject to license terms.
0007: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0008: *
0009: * This program is free software; you can redistribute it and/or
0010: * modify it under the terms of the GNU General Public License version
0011: * 2 only, as published by the Free Software Foundation.
0012: *
0013: * This program is distributed in the hope that it will be useful, but
0014: * WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016: * General Public License version 2 for more details (a copy is
0017: * included at /legal/license.txt).
0018: *
0019: * You should have received a copy of the GNU General Public License
0020: * version 2 along with this work; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0022: * 02110-1301 USA
0023: *
0024: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0025: * Clara, CA 95054 or visit www.sun.com if you need additional
0026: * information or have any questions.
0027: */
0028:
0029: /*
0030: * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
0031: * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
0032: *
0033: * The original version of this source code and documentation
0034: * is copyrighted and owned by Taligent, Inc., a wholly-owned
0035: * subsidiary of IBM. These materials are provided under terms
0036: * of a License Agreement between Taligent and Sun. This technology
0037: * is protected by multiple US and International patents.
0038: *
0039: * This notice and attribution to Taligent may not be removed.
0040: * Taligent is a registered trademark of Taligent, Inc.
0041: *
0042: */
0043:
0044: package java.util;
0045:
0046: import java.io.*;
0047: import java.security.AccessController;
0048: import java.text.MessageFormat;
0049: import sun.security.action.GetPropertyAction;
0050: import sun.text.resources.LocaleData;
0051:
0052: /**
0053: *
0054: * A <code>Locale</code> object represents a specific geographical, political,
0055: * or cultural region. An operation that requires a <code>Locale</code> to perform
0056: * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
0057: * to tailor information for the user. For example, displaying a number
0058: * is a locale-sensitive operation--the number should be formatted
0059: * according to the customs/conventions of the user's native country,
0060: * region, or culture.
0061: *
0062: * <P>
0063: * Create a <code>Locale</code> object using the constructors in this class:
0064: * <blockquote>
0065: * <pre>
0066: * Locale(String language)
0067: * Locale(String language, String country)
0068: * Locale(String language, String country, String variant)
0069: * </pre>
0070: * </blockquote>
0071: * The language argument is a valid <STRONG>ISO Language Code.</STRONG>
0072: * These codes are the lower-case, two-letter codes as defined by ISO-639.
0073: * You can find a full list of these codes at a number of sites, such as:
0074: * <BR><a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
0075: * <code>http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</code></a>
0076: *
0077: * <P>
0078: * The country argument is a valid <STRONG>ISO Country Code.</STRONG> These
0079: * codes are the upper-case, two-letter codes as defined by ISO-3166.
0080: * You can find a full list of these codes at a number of sites, such as:
0081: * <BR><a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
0082: * <code>http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html</code></a>
0083: *
0084: * <P>
0085: * The variant argument is a vendor or browser-specific code.
0086: * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
0087: * Where there are two variants, separate them with an underscore, and
0088: * put the most important one first. For example, a Traditional Spanish collation
0089: * might construct a locale with parameters for language, country and variant as:
0090: * "es", "ES", "Traditional_WIN".
0091: *
0092: * <P>
0093: * Because a <code>Locale</code> object is just an identifier for a region,
0094: * no validity check is performed when you construct a <code>Locale</code>.
0095: * If you want to see whether particular resources are available for the
0096: * <code>Locale</code> you construct, you must query those resources. For
0097: * example, ask the <code>NumberFormat</code> for the locales it supports
0098: * using its <code>getAvailableLocales</code> method.
0099: * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
0100: * locale, you get back the best available match, not necessarily
0101: * precisely what you asked for. For more information, look at
0102: * {@link ResourceBundle}.
0103: *
0104: * <P>
0105: * The <code>Locale</code> class provides a number of convenient constants
0106: * that you can use to create <code>Locale</code> objects for commonly used
0107: * locales. For example, the following creates a <code>Locale</code> object
0108: * for the United States:
0109: * <blockquote>
0110: * <pre>
0111: * Locale.US
0112: * </pre>
0113: * </blockquote>
0114: *
0115: * <P>
0116: * Once you've created a <code>Locale</code> you can query it for information about
0117: * itself. Use <code>getCountry</code> to get the ISO Country Code and
0118: * <code>getLanguage</code> to get the ISO Language Code. You can
0119: * use <code>getDisplayCountry</code> to get the
0120: * name of the country suitable for displaying to the user. Similarly,
0121: * you can use <code>getDisplayLanguage</code> to get the name of
0122: * the language suitable for displaying to the user. Interestingly,
0123: * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
0124: * and have two versions: one that uses the default locale and one
0125: * that uses the locale specified as an argument.
0126: *
0127: * <P>
0128: * The Java 2 platform provides a number of classes that perform locale-sensitive
0129: * operations. For example, the <code>NumberFormat</code> class formats
0130: * numbers, currency, or percentages in a locale-sensitive manner. Classes
0131: * such as <code>NumberFormat</code> have a number of convenience methods
0132: * for creating a default object of that type. For example, the
0133: * <code>NumberFormat</code> class provides these three convenience methods
0134: * for creating a default <code>NumberFormat</code> object:
0135: * <blockquote>
0136: * <pre>
0137: * NumberFormat.getInstance()
0138: * NumberFormat.getCurrencyInstance()
0139: * NumberFormat.getPercentInstance()
0140: * </pre>
0141: * </blockquote>
0142: * These methods have two variants; one with an explicit locale
0143: * and one without; the latter using the default locale.
0144: * <blockquote>
0145: * <pre>
0146: * NumberFormat.getInstance(myLocale)
0147: * NumberFormat.getCurrencyInstance(myLocale)
0148: * NumberFormat.getPercentInstance(myLocale)
0149: * </pre>
0150: * </blockquote>
0151: * A <code>Locale</code> is the mechanism for identifying the kind of object
0152: * (<code>NumberFormat</code>) that you would like to get. The locale is
0153: * <STRONG>just</STRONG> a mechanism for identifying objects,
0154: * <STRONG>not</STRONG> a container for the objects themselves.
0155: *
0156: * <P>
0157: * Each class that performs locale-sensitive operations allows you
0158: * to get all the available objects of that type. You can sift
0159: * through these objects by language, country, or variant,
0160: * and use the display names to present a menu to the user.
0161: * For example, you can create a menu of all the collation objects
0162: * suitable for a given language. Such classes must implement these
0163: * three class methods:
0164: * <blockquote>
0165: * <pre>
0166: * public static Locale[] getAvailableLocales()
0167: * public static String getDisplayName(Locale objectLocale,
0168: * Locale displayLocale)
0169: * public static final String getDisplayName(Locale objectLocale)
0170: * // getDisplayName will throw MissingResourceException if the locale
0171: * // is not one of the available locales.
0172: * </pre>
0173: * </blockquote>
0174: *
0175: * @see ResourceBundle
0176: * @see java.text.Format
0177: * @see java.text.NumberFormat
0178: * @see java.text.Collator
0179: * @version 1.55, 01/19/00
0180: * @author Mark Davis
0181: * @since 1.1
0182: */
0183:
0184: public final class Locale implements Cloneable, Serializable {
0185:
0186: /** Useful constant for language.
0187: */
0188: static public final Locale ENGLISH = new Locale("en", "", "");
0189:
0190: /** Useful constant for language.
0191: */
0192: static public final Locale FRENCH = new Locale("fr", "", "");
0193:
0194: /** Useful constant for language.
0195: */
0196: static public final Locale GERMAN = new Locale("de", "", "");
0197:
0198: /** Useful constant for language.
0199: */
0200: static public final Locale ITALIAN = new Locale("it", "", "");
0201:
0202: /** Useful constant for language.
0203: */
0204: static public final Locale JAPANESE = new Locale("ja", "", "");
0205:
0206: /** Useful constant for language.
0207: */
0208: static public final Locale KOREAN = new Locale("ko", "", "");
0209:
0210: /** Useful constant for language.
0211: */
0212: static public final Locale CHINESE = new Locale("zh", "", "");
0213:
0214: /** Useful constant for language.
0215: */
0216: static public final Locale SIMPLIFIED_CHINESE = new Locale("zh",
0217: "CN", "");
0218:
0219: /** Useful constant for language.
0220: */
0221: static public final Locale TRADITIONAL_CHINESE = new Locale("zh",
0222: "TW", "");
0223:
0224: /** Useful constant for country.
0225: */
0226: static public final Locale FRANCE = new Locale("fr", "FR", "");
0227:
0228: /** Useful constant for country.
0229: */
0230: static public final Locale GERMANY = new Locale("de", "DE", "");
0231:
0232: /** Useful constant for country.
0233: */
0234: static public final Locale ITALY = new Locale("it", "IT", "");
0235:
0236: /** Useful constant for country.
0237: */
0238: static public final Locale JAPAN = new Locale("ja", "JP", "");
0239:
0240: /** Useful constant for country.
0241: */
0242: static public final Locale KOREA = new Locale("ko", "KR", "");
0243:
0244: /** Useful constant for country.
0245: */
0246: static public final Locale CHINA = new Locale("zh", "CN", "");
0247:
0248: /** Useful constant for country.
0249: */
0250: static public final Locale PRC = new Locale("zh", "CN", "");
0251:
0252: /** Useful constant for country.
0253: */
0254: static public final Locale TAIWAN = new Locale("zh", "TW", "");
0255:
0256: /** Useful constant for country.
0257: */
0258: static public final Locale UK = new Locale("en", "GB", "");
0259:
0260: /** Useful constant for country.
0261: */
0262: static public final Locale US = new Locale("en", "US", "");
0263:
0264: /** Useful constant for country.
0265: */
0266: static public final Locale CANADA = new Locale("en", "CA", "");
0267:
0268: /** Useful constant for country.
0269: */
0270: static public final Locale CANADA_FRENCH = new Locale("fr", "CA",
0271: "");
0272:
0273: /** serialization ID
0274: */
0275: static final long serialVersionUID = 9149081749638150636L;
0276:
0277: /**
0278: * Construct a locale from language, country, variant.
0279: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0280: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0281: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0282: * API on Locale will return only the OLD codes.
0283: * @param language lowercase two-letter ISO-639 code.
0284: * @param country uppercase two-letter ISO-3166 code.
0285: * @param variant vendor and browser specific code. See class description.
0286: * @exception NullPointerException thrown if any argument is null.
0287: */
0288: public Locale(String language, String country, String variant) {
0289: this .language = convertOldISOCodes(language);
0290: this .country = toUpperCase(country).intern();
0291: this .variant = variant.intern();
0292: }
0293:
0294: /**
0295: * Construct a locale from language, country.
0296: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0297: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0298: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0299: * API on Locale will return only the OLD codes.
0300: * @param language lowercase two-letter ISO-639 code.
0301: * @param country uppercase two-letter ISO-3166 code.
0302: * @exception NullPointerException thrown if either argument is null.
0303: */
0304: public Locale(String language, String country) {
0305: this (language, country, "");
0306: }
0307:
0308: /**
0309: * Construct a locale from a language code.
0310: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0311: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0312: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0313: * API on Locale will return only the OLD codes.
0314: * @param language lowercase two-letter ISO-639 code.
0315: * @exception NullPointerException thrown if argument is null.
0316: * @since 1.4
0317: */
0318: public Locale(String language) {
0319: this (language, "", "");
0320: }
0321:
0322: /**
0323: * Gets the current value of the default locale for this instance
0324: * of the Java Virtual Machine.
0325: * <p>
0326: * The Java Virtual Machine sets the default locale during startup
0327: * based on the host environment. It is used by many locale-sensitive
0328: * methods if no locale is explicitly specified.
0329: * It can be changed using the
0330: * {@link #setDefault(java.util.Locale) setDefault} method.
0331: *
0332: * @return the default locale for this instance of the Java Virtual Machine
0333: */
0334: public static Locale getDefault() {
0335: // do not synchronize this method - see 4071298
0336: // it's OK if more than one default locale happens to be created
0337: if (defaultLocale == null) {
0338: String language, region, country, variant;
0339: language = (String) AccessController
0340: .doPrivileged(new GetPropertyAction(
0341: "user.language", "en"));
0342: // for compatibility, check for old user.region property
0343: region = (String) AccessController
0344: .doPrivileged(new GetPropertyAction("user.region"));
0345: if (region != null) {
0346: // region can be of form country, country_variant, or _variant
0347: int i = region.indexOf('_');
0348: if (i >= 0) {
0349: country = region.substring(0, i);
0350: variant = region.substring(i + 1);
0351: } else {
0352: country = region;
0353: variant = "";
0354: }
0355: } else {
0356: country = (String) AccessController
0357: .doPrivileged(new GetPropertyAction(
0358: "user.country", ""));
0359: variant = (String) AccessController
0360: .doPrivileged(new GetPropertyAction(
0361: "user.variant", ""));
0362: }
0363: defaultLocale = new Locale(language, country, variant);
0364: }
0365: return defaultLocale;
0366: }
0367:
0368: /**
0369: * Sets the default locale for this instance of the Java Virtual Machine.
0370: * This does not affect the host locale.
0371: * <p>
0372: * If there is a security manager, its <code>checkPermission</code>
0373: * method is called with a <code>PropertyPermission("user.language", "write")</code>
0374: * permission before the default locale is changed.
0375: * <p>
0376: * The Java Virtual Machine sets the default locale during startup
0377: * based on the host environment. It is used by many locale-sensitive
0378: * methods if no locale is explicitly specified.
0379: * <p>
0380: * Since changing the default locale may affect many different areas
0381: * of functionality, this method should only be used if the caller
0382: * is prepared to reinitialize locale-sensitive code running
0383: * within the same Java Virtual Machine, such as the user interface.
0384: *
0385: * @throws SecurityException
0386: * if a security manager exists and its
0387: * <code>checkPermission</code> method doesn't allow the operation.
0388: * @throws NullPointerException if <code>newLocale</code> is null
0389: * @param newLocale the new default locale
0390: * @see SecurityManager#checkPermission
0391: * @see java.util.PropertyPermission
0392: */
0393: public static synchronized void setDefault(Locale newLocale) {
0394: if (newLocale == null)
0395: throw new NullPointerException(
0396: "Can't set default locale to NULL");
0397:
0398: SecurityManager sm = System.getSecurityManager();
0399: if (sm != null)
0400: sm.checkPermission(new PropertyPermission("user.language",
0401: "write"));
0402: defaultLocale = newLocale;
0403: }
0404:
0405: /**
0406: * Returns a list of all installed locales.
0407: */
0408: public static Locale[] getAvailableLocales() {
0409: return LocaleData.getAvailableLocales("LocaleString");
0410: }
0411:
0412: /**
0413: * Returns a list of all 2-letter country codes defined in ISO 3166.
0414: * Can be used to create Locales.
0415: */
0416: public static String[] getISOCountries() {
0417: if (isoCountries == null) {
0418: isoCountries = new String[compressedIsoCountries.length() / 6];
0419: for (int i = 0; i < isoCountries.length; i++)
0420: isoCountries[i] = compressedIsoCountries.substring(
0421: (i * 6) + 1, (i * 6) + 3);
0422: }
0423: String[] result = new String[isoCountries.length];
0424: System.arraycopy(isoCountries, 0, result, 0,
0425: isoCountries.length);
0426: return result;
0427: }
0428:
0429: /**
0430: * Returns a list of all 2-letter language codes defined in ISO 639.
0431: * Can be used to create Locales.
0432: * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
0433: * The list this function returns includes both the new and the old codes for the
0434: * languages whose codes have changed.]
0435: */
0436: public static String[] getISOLanguages() {
0437: if (isoLanguages == null) {
0438: isoLanguages = new String[compressedIsoLanguages.length() / 6];
0439: for (int i = 0; i < isoLanguages.length; i++)
0440: isoLanguages[i] = compressedIsoLanguages.substring(
0441: (i * 6) + 1, (i * 6) + 3);
0442: }
0443: String[] result = new String[isoLanguages.length];
0444: System.arraycopy(isoLanguages, 0, result, 0,
0445: isoLanguages.length);
0446: return result;
0447: }
0448:
0449: /**
0450: * Returns the language code for this locale, which will either be the empty string
0451: * or a lowercase ISO 639 code.
0452: * <p>NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
0453: * Locale's constructor recognizes both the new and the old codes for the languages
0454: * whose codes have changed, but this function always returns the old code. If you
0455: * want to check for a specific language whose code has changed, don't do <pre>
0456: * if (locale.getLanguage().equals("he")
0457: * ...
0458: * </pre>Instead, do<pre>
0459: * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage())
0460: * ...</pre>
0461: * @see #getDisplayLanguage
0462: */
0463: public String getLanguage() {
0464: return language;
0465: }
0466:
0467: /**
0468: * Returns the country/region code for this locale, which will either be the empty string
0469: * or an upercase ISO 3166 2-letter code.
0470: * @see #getDisplayCountry
0471: */
0472: public String getCountry() {
0473: return country;
0474: }
0475:
0476: /**
0477: * Returns the variant code for this locale.
0478: * @see #getDisplayVariant
0479: */
0480: public String getVariant() {
0481: return variant;
0482: }
0483:
0484: /**
0485: * Getter for the programmatic name of the entire locale,
0486: * with the language, country and variant separated by underbars.
0487: * Language is always lower case, and country is always upper case.
0488: * If the language is missing, the string will begin with an underbar.
0489: * If both the language and country fields are missing, this function
0490: * will return the empty string, even if the variant field is filled in
0491: * (you can't have a locale with just a variant-- the variant must accompany
0492: * a valid language or country code).
0493: * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
0494: * @see #getDisplayName
0495: */
0496: public final String toString() {
0497: boolean l = language.length() != 0;
0498: boolean c = country.length() != 0;
0499: boolean v = variant.length() != 0;
0500: StringBuffer result = new StringBuffer(language);
0501: if (c || (l && v)) {
0502: result.append('_').append(country); // This may just append '_'
0503: }
0504: if (v && (l || c)) {
0505: result.append('_').append(variant);
0506: }
0507: return result.toString();
0508: }
0509:
0510: /**
0511: * Returns a three-letter abbreviation for this locale's language. If the locale
0512: * doesn't specify a language, this will be the empty string. Otherwise, this will
0513: * be a lowercase ISO 639-2/T language code.
0514: * The ISO 639-2 language codes can be found on-line at
0515: * <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
0516: * @exception MissingResourceException Throws MissingResourceException if the
0517: * three-letter language abbreviation is not available for this locale.
0518: */
0519: public String getISO3Language() throws MissingResourceException {
0520: int length = language.length();
0521:
0522: if (length == 0) {
0523: return "";
0524: }
0525:
0526: int index = compressedIsoLanguages.indexOf("," + language);
0527: if (index == -1 || length != 2) {
0528: throw new MissingResourceException(
0529: "Couldn't find 3-letter language code for "
0530: + language, "LocaleElements_" + toString(),
0531: "ShortLanguage");
0532: }
0533: return compressedIsoLanguages.substring(index + 3, index + 6);
0534: }
0535:
0536: /**
0537: * Returns a three-letter abbreviation for this locale's country. If the locale
0538: * doesn't specify a country, this will be tbe the empty string. Otherwise, this will
0539: * be an uppercase ISO 3166 3-letter country code.
0540: * @exception MissingResourceException Throws MissingResourceException if the
0541: * three-letter country abbreviation is not available for this locale.
0542: */
0543: public String getISO3Country() throws MissingResourceException {
0544: int length = country.length();
0545:
0546: if (length == 0) {
0547: return "";
0548: }
0549:
0550: int index = compressedIsoCountries.indexOf("," + country);
0551: if (index == -1 || length != 2) {
0552: throw new MissingResourceException(
0553: "Couldn't find 3-letter country code for "
0554: + country, "LocaleElements_" + toString(),
0555: "ShortCountry");
0556: }
0557: return compressedIsoCountries.substring(index + 3, index + 6);
0558: }
0559:
0560: /**
0561: * Returns a name for the locale's language that is appropriate for display to the
0562: * user.
0563: * If possible, the name returned will be localized for the default locale.
0564: * For example, if the locale is fr_FR and the default locale
0565: * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
0566: * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
0567: * If the name returned cannot be localized for the default locale,
0568: * (say, we don't have a Japanese name for Croatian),
0569: * this function falls back on the English name, and uses the ISO code as a last-resort
0570: * value. If the locale doesn't specify a language, this function returns the empty string.
0571: */
0572: public final String getDisplayLanguage() {
0573: return getDisplayLanguage(getDefault());
0574: }
0575:
0576: /**
0577: * Returns a name for the locale's language that is appropriate for display to the
0578: * user.
0579: * If possible, the name returned will be localized according to inLocale.
0580: * For example, if the locale is fr_FR and inLocale
0581: * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
0582: * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
0583: * If the name returned cannot be localized according to inLocale,
0584: * (say, we don't have a Japanese name for Croatian),
0585: * this function falls back on the default locale, on the English name, and finally
0586: * on the ISO code as a last-resort value. If the locale doesn't specify a language,
0587: * this function returns the empty string.
0588: */
0589: public String getDisplayLanguage(Locale inLocale) {
0590: String langCode = language;
0591: if (langCode.length() == 0)
0592: return "";
0593:
0594: Locale workingLocale = (Locale) inLocale.clone();
0595: String result = null;
0596: int phase = 0;
0597: boolean done = false;
0598:
0599: if (workingLocale.variant.length() == 0)
0600: phase = 1;
0601: if (workingLocale.country.length() == 0)
0602: phase = 2;
0603:
0604: while (!done) {
0605: try {
0606: ResourceBundle bundle = LocaleData
0607: .getLocaleElements(workingLocale);
0608: result = findStringMatch((String[][]) bundle
0609: .getObject("Languages"), langCode, langCode);
0610: if (result.length() != 0)
0611: done = true;
0612: } catch (Exception e) {
0613: // just fall through
0614: }
0615:
0616: if (!done) {
0617: switch (phase) {
0618: case 0:
0619: workingLocale.variant = "";
0620: break;
0621:
0622: case 1:
0623: workingLocale.country = "";
0624: break;
0625:
0626: case 2:
0627: workingLocale = getDefault();
0628: break;
0629:
0630: case 3:
0631: workingLocale = new Locale("", "", "");
0632: break;
0633:
0634: default:
0635: return langCode;
0636: }
0637: phase++;
0638: }
0639: }
0640: return result;
0641: }
0642:
0643: /**
0644: * Returns a name for the locale's country that is appropriate for display to the
0645: * user.
0646: * If possible, the name returned will be localized for the default locale.
0647: * For example, if the locale is fr_FR and the default locale
0648: * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
0649: * the default locale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
0650: * If the name returned cannot be localized for the default locale,
0651: * (say, we don't have a Japanese name for Croatia),
0652: * this function falls back on the English name, and uses the ISO code as a last-resort
0653: * value. If the locale doesn't specify a country, this function returns the empty string.
0654: */
0655: public final String getDisplayCountry() {
0656: return getDisplayCountry(getDefault());
0657: }
0658:
0659: /**
0660: * Returns a name for the locale's country that is appropriate for display to the
0661: * user.
0662: * If possible, the name returned will be localized according to inLocale.
0663: * For example, if the locale is fr_FR and inLocale
0664: * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
0665: * inLocale is fr_FR, getDisplayLanguage() will return "Etats-Unis".
0666: * If the name returned cannot be localized according to inLocale.
0667: * (say, we don't have a Japanese name for Croatia),
0668: * this function falls back on the default locale, on the English name, and finally
0669: * on the ISO code as a last-resort value. If the locale doesn't specify a country,
0670: * this function returns the empty string.
0671: */
0672: public String getDisplayCountry(Locale inLocale) {
0673: String ctryCode = country;
0674: if (ctryCode.length() == 0)
0675: return "";
0676:
0677: Locale workingLocale = (Locale) inLocale.clone();
0678: String result = null;
0679: int phase = 0;
0680: boolean done = false;
0681:
0682: if (workingLocale.variant.length() == 0)
0683: phase = 1;
0684: if (workingLocale.country.length() == 0)
0685: phase = 2;
0686:
0687: while (!done) {
0688: try {
0689: ResourceBundle bundle = LocaleData
0690: .getLocaleElements(workingLocale);
0691: result = findStringMatch((String[][]) bundle
0692: .getObject("Countries"), ctryCode, ctryCode);
0693: if (result.length() != 0)
0694: done = true;
0695: } catch (Exception e) {
0696: // just fall through
0697: }
0698:
0699: if (!done) {
0700: switch (phase) {
0701: case 0:
0702: workingLocale.variant = "";
0703: break;
0704:
0705: case 1:
0706: workingLocale.country = "";
0707: break;
0708:
0709: case 2:
0710: workingLocale = getDefault();
0711: break;
0712:
0713: case 3:
0714: workingLocale = new Locale("", "", "");
0715: break;
0716:
0717: default:
0718: return ctryCode;
0719: }
0720: phase++;
0721: }
0722: }
0723: return result;
0724: }
0725:
0726: /**
0727: * Returns a name for the locale's variant code that is appropriate for display to the
0728: * user. If possible, the name will be localized for the default locale. If the locale
0729: * doesn't specify a variant code, this function returns the empty string.
0730: */
0731: public final String getDisplayVariant() {
0732: return getDisplayVariant(getDefault());
0733: }
0734:
0735: /**
0736: * Returns a name for the locale's variant code that is appropriate for display to the
0737: * user. If possible, the name will be localized for inLocale. If the locale
0738: * doesn't specify a variant code, this function returns the empty string.
0739: */
0740: public String getDisplayVariant(Locale inLocale) {
0741: if (variant.length() == 0)
0742: return "";
0743:
0744: ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
0745:
0746: String names[] = getDisplayVariantArray(bundle);
0747:
0748: // Get the localized patterns for formatting a list, and use
0749: // them to format the list.
0750: String[] patterns;
0751: try {
0752: patterns = (String[]) bundle
0753: .getObject("LocaleNamePatterns");
0754: } catch (MissingResourceException e) {
0755: patterns = null;
0756: }
0757: return formatList(patterns, names);
0758: }
0759:
0760: /**
0761: * Returns a name for the locale that is appropriate for display to the
0762: * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
0763: * and getDisplayVariant() assembled into a single string. The display name will have
0764: * one of the following forms:<p><blockquote>
0765: * language (country, variant)<p>
0766: * language (country)<p>
0767: * language (variant)<p>
0768: * country (variant)<p>
0769: * language<p>
0770: * country<p>
0771: * variant<p></blockquote>
0772: * depending on which fields are specified in the locale. If the language, country,
0773: * and variant fields are all empty, this function returns the empty string.
0774: */
0775: public final String getDisplayName() {
0776: return getDisplayName(getDefault());
0777: }
0778:
0779: /**
0780: * Returns a name for the locale that is appropriate for display to the
0781: * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
0782: * and getDisplayVariant() assembled into a single string. The display name will have
0783: * one of the following forms:<p><blockquote>
0784: * language (country, variant)<p>
0785: * language (country)<p>
0786: * language (variant)<p>
0787: * country (variant)<p>
0788: * language<p>
0789: * country<p>
0790: * variant<p></blockquote>
0791: * depending on which fields are specified in the locale. If the language, country,
0792: * and variant fields are all empty, this function returns the empty string.
0793: */
0794: public String getDisplayName(Locale inLocale) {
0795: ResourceBundle bundle = LocaleData.getLocaleElements(inLocale);
0796:
0797: String languageName = getDisplayLanguage(inLocale);
0798: String countryName = getDisplayCountry(inLocale);
0799: String[] variantNames = getDisplayVariantArray(bundle);
0800:
0801: // Get the localized patterns for formatting a display name.
0802: String[] patterns;
0803: try {
0804: patterns = (String[]) bundle
0805: .getObject("LocaleNamePatterns");
0806: } catch (MissingResourceException e) {
0807: patterns = null;
0808: }
0809:
0810: // The display name consists of a main name, followed by qualifiers.
0811: // Typically, the format is "MainName (Qualifier, Qualifier)" but this
0812: // depends on what pattern is stored in the display locale.
0813: String mainName = null;
0814: String[] qualifierNames = null;
0815:
0816: // The main name is the language, or if there is no language, the country.
0817: // If there is neither language nor country (an anomalous situation) then
0818: // the display name is simply the variant's display name.
0819: if (languageName.length() != 0) {
0820: mainName = languageName;
0821: if (countryName.length() != 0) {
0822: qualifierNames = new String[variantNames.length + 1];
0823: System.arraycopy(variantNames, 0, qualifierNames, 1,
0824: variantNames.length);
0825: qualifierNames[0] = countryName;
0826: } else
0827: qualifierNames = variantNames;
0828: } else if (countryName.length() != 0) {
0829: mainName = countryName;
0830: qualifierNames = variantNames;
0831: } else {
0832: return formatList(patterns, variantNames);
0833: }
0834:
0835: // Create an array whose first element is the number of remaining
0836: // elements. This serves as a selector into a ChoiceFormat pattern from
0837: // the resource. The second and third elements are the main name and
0838: // the qualifier; if there are no qualifiers, the third element is
0839: // unused by the format pattern.
0840: Object[] displayNames = {
0841: new Integer(qualifierNames.length != 0 ? 2 : 1),
0842: mainName,
0843: // We could also just call formatList() and have it handle the empty
0844: // list case, but this is more efficient, and we want it to be
0845: // efficient since all the language-only locales will not have any
0846: // qualifiers.
0847: qualifierNames.length != 0 ? formatList(patterns,
0848: qualifierNames) : null };
0849:
0850: if (patterns != null) {
0851: return new MessageFormat(patterns[0]).format(displayNames);
0852: } else {
0853: // If we cannot get the message format pattern, then we use a simple
0854: // hard-coded pattern. This should not occur in practice unless the
0855: // installation is missing some core files (LocaleElements etc.).
0856: StringBuffer result = new StringBuffer();
0857: result.append((String) displayNames[1]);
0858: if (displayNames.length > 2) {
0859: result.append(" (");
0860: result.append((String) displayNames[2]);
0861: result.append(")");
0862: }
0863: return result.toString();
0864: }
0865: }
0866:
0867: /**
0868: * Overrides Cloneable
0869: */
0870: public Object clone() {
0871: try {
0872: Locale that = (Locale) super .clone();
0873: return that;
0874: } catch (CloneNotSupportedException e) {
0875: throw new InternalError();
0876: }
0877: }
0878:
0879: /**
0880: * Override hashCode.
0881: * Since Locales are often used in hashtables, caches the value
0882: * for speed.
0883: */
0884: // Depending on performance of synchronized, may want to
0885: // just compute in constructor.
0886: public synchronized int hashCode() {
0887: if (hashcode == -1) {
0888: hashcode = language.hashCode() ^ country.hashCode()
0889: ^ variant.hashCode();
0890: }
0891: return hashcode;
0892: }
0893:
0894: // Overrides
0895:
0896: /**
0897: * Returns true if this Locale is equal to another object. A Locale is
0898: * deemed equal to another Locale with identical language, country,
0899: * and variant, and unequal to all other objects.
0900: *
0901: * @return true if this Locale is equal to the specified object.
0902: */
0903:
0904: public boolean equals(Object obj) {
0905: if (this == obj) // quick check
0906: return true;
0907: if (!(obj instanceof Locale)) // (1) same object?
0908: return false;
0909: Locale other = (Locale) obj;
0910: if (hashCode() != other.hashCode())
0911: return false; // quick check
0912: if (language != other.language)
0913: return false;
0914: if (country != other.country)
0915: return false;
0916: if (variant != other.variant)
0917: return false;
0918: return true; // we made it through the guantlet.
0919: // (1) We don't check super.equals since it is Object.
0920: // Since Locale is final, we don't have to check both directions.
0921: }
0922:
0923: // ================= privates =====================================
0924:
0925: // instance and class variables. For now keep these separate, since it is
0926: // faster to match. Later, make into single string.
0927:
0928: /**
0929: * @serial
0930: * @see #getLanguage
0931: */
0932: private String language = "";
0933:
0934: /**
0935: * @serial
0936: * @see #getCountry
0937: */
0938: private String country = "";
0939:
0940: /**
0941: * @serial
0942: * @see #getVariant
0943: */
0944: private String variant = "";
0945:
0946: /**
0947: * Placeholder for the object's hash code. Always -1.
0948: * @serial
0949: */
0950: private int hashcode = -1;
0951:
0952: private static Locale defaultLocale = null;
0953:
0954: /**
0955: * Return an array of the display names of the variant.
0956: * @param bundle the ResourceBundle to use to get the display names
0957: * @return an array of display names, possible of zero length.
0958: */
0959: private String[] getDisplayVariantArray(ResourceBundle bundle) {
0960: // Split the variant name into tokens separated by '_'.
0961: StringTokenizer tokenizer = new StringTokenizer(variant, "_");
0962: String[] names = new String[tokenizer.countTokens()];
0963:
0964: // For each variant token, lookup the display name. If
0965: // not found, use the variant name itself.
0966: for (int i = 0; i < names.length; ++i) {
0967: String token = tokenizer.nextToken();
0968: try {
0969: names[i] = (String) bundle.getObject("%%" + token);
0970: } catch (MissingResourceException e) {
0971: names[i] = token;
0972: }
0973: }
0974:
0975: return names;
0976: }
0977:
0978: /**
0979: * Format a list with an array of patterns.
0980: * @param patterns an array of three patterns. The first pattern is not
0981: * used. The second pattern should create a MessageFormat taking 0-3 arguments
0982: * and formatting them into a list. The third pattern should take 2 arguments
0983: * and is used by composeList. If patterns is null, then a the list is
0984: * formatted by concatenation with the delimiter ','.
0985: * @param stringList the list of strings to be formatted.
0986: * @return a string representing the list.
0987: */
0988: private static String formatList(String[] patterns,
0989: String[] stringList) {
0990: // If we have no list patterns, compose the list in a simple,
0991: // non-localized way.
0992: if (patterns == null) {
0993: StringBuffer result = new StringBuffer();
0994: for (int i = 0; i < stringList.length; ++i) {
0995: if (i > 0)
0996: result.append(',');
0997: result.append(stringList[i]);
0998: }
0999: return result.toString();
1000: }
1001:
1002: // Compose the list down to three elements if necessary
1003: if (stringList.length > 3) {
1004: MessageFormat format = new MessageFormat(patterns[2]);
1005: stringList = composeList(format, stringList);
1006: }
1007:
1008: // Rebuild the argument list with the list length as the first element
1009: Object[] args = new Object[stringList.length + 1];
1010: System.arraycopy(stringList, 0, args, 1, stringList.length);
1011: args[0] = new Integer(stringList.length);
1012:
1013: // Format it using the pattern in the resource
1014: MessageFormat format = new MessageFormat(patterns[1]);
1015: return format.format(args);
1016: }
1017:
1018: /**
1019: * Given a list of strings, return a list shortened to three elements.
1020: * Shorten it by applying the given format to the first two elements
1021: * recursively.
1022: * @param format a format which takes two arguments
1023: * @param list a list of strings
1024: * @return if the list is three elements or shorter, the same list;
1025: * otherwise, a new list of three elements.
1026: */
1027: private static String[] composeList(MessageFormat format,
1028: String[] list) {
1029: if (list.length <= 3)
1030: return list;
1031:
1032: // Use the given format to compose the first two elements into one
1033: String[] listItems = { list[0], list[1] };
1034: String newItem = format.format(listItems);
1035:
1036: // Form a new list one element shorter
1037: String[] newList = new String[list.length - 1];
1038: System.arraycopy(list, 2, newList, 1, newList.length - 1);
1039: newList[0] = newItem;
1040:
1041: // Recurse
1042: return composeList(format, newList);
1043: }
1044:
1045: /**
1046: * @serialData The first three fields are three <code>String</code> objects:
1047: * the first is a 2-letter ISO 639 code representing the locale's language,
1048: * the second is a 2-letter ISO 3166 code representing the locale's region or
1049: * country, and the third is an optional chain of variant codes defined by this
1050: * library. Any of the fields may be the empty string. The fourth field is an
1051: * <code>int</code> whose value is always -1. This is a sentinel value indicating
1052: * the <code>Locale</code>'s hash code must be recomputed.
1053: */
1054: private void writeObject(ObjectOutputStream out) throws IOException {
1055: // hashcode is semantically transient. We couldn't define it as transient
1056: // because versions of this class that DIDN'T declare it as transient have
1057: // already shipped. What we're doing here is making sure that the written-out
1058: // version of hashcode is always -1, regardless of what's really stored there
1059: // (we hold onto the original value just in case someone might want it again).
1060: // Writing -1 ensures that version 1.1 Locales will always recalculate their
1061: // hash codes after being streamed back in. This is necessary because
1062: // String.hashCode() calculates its hash code differently in 1.2 than it did
1063: // in 1.1.
1064: int temp = hashcode;
1065: hashcode = -1;
1066: out.defaultWriteObject();
1067: hashcode = temp;
1068: }
1069:
1070: /**
1071: * @serialData The first three fields are three <code>String</code> objects:
1072: * the first is a 2-letter ISO 639 code representing the locale's language,
1073: * the second is a 2-letter ISO 3166 code representing the locale's region or
1074: * country, and the third is an optional chain of variant codes defined by this
1075: * library. Any of the fields may be the empty string. The fourth field is an
1076: * <code>int</code>representing the locale's hash code, but is ignored by
1077: * <code>readObject()</code>. Whatever this field's value, the hash code is
1078: * initialized to -1, a sentinel value that indicates the hash code must be
1079: * recomputed.
1080: */
1081: private void readObject(ObjectInputStream in) throws IOException,
1082: ClassNotFoundException {
1083: // hashcode is semantically transient. We couldn't define it as transient
1084: // because versions of this class that DIDN'T declare is as transient have
1085: // already shipped. This code makes sure that whatever value for hashcode
1086: // was written on the stream, we ignore it and recalculate it on demand. This
1087: // is necessary because String.hashCode() calculates is hash code differently
1088: // in version 1.2 than it did in 1.1.
1089: in.defaultReadObject();
1090: hashcode = -1;
1091: language = convertOldISOCodes(language);
1092: country = country.intern();
1093: variant = variant.intern();
1094: }
1095:
1096: /**
1097: * List of all 2-letter language codes currently defined in ISO 639.
1098: * (Because the Java VM specification turns an array constant into executable code
1099: * that generates the array element by element, we keep the array in compressed
1100: * form in a single string and build the array from it at run time when requested.)
1101: * [We're now also using this table to store a mapping from 2-letter ISO language codes
1102: * to 3-letter ISO language codes. Each group of characters consists of a comma, a
1103: * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
1104: * a comma followed by a 2-letter code and then getting the three letters following
1105: * the 2-letter code.]
1106: */
1107: private static String[] isoLanguages = null;
1108: private static final String compressedIsoLanguages = ",aaaar,ababk,afafr,amamh,arara,asasm,ayaym,azaze,babak,bebel,bgbul,bhbih,bibis,bnben,"
1109: + "bobod,brbre,cacat,cocos,csces,cycym,dadan,dedeu,dzdzo,elell,eneng,eoepo,esspa,"
1110: + "etest,eueus,fafas,fifin,fjfij,fofao,frfra,fyfry,gagai,gdgdh,glglg,gngrn,guguj,"
1111: + "hahau,heheb,hihin,hrhrv,huhun,hyhye,iaina,idind,ieile,ikipk,inind,isisl,itita,"
1112: + "iuiku,iwheb,jajpn,jiyid,jwjaw,kakat,kkkaz,klkal,kmkhm,knkan,kokor,kskas,kukur,"
1113: + "kykir,lalat,lnlin,lolao,ltlit,lvlav,mgmlg,mimri,mkmkd,mlmal,mnmon,momol,mrmar,"
1114: + "msmsa,mtmlt,mymya,nanau,nenep,nlnld,nonor,ococi,omorm,orori,papan,plpol,pspus,"
1115: + "ptpor,quque,rmroh,rnrun,roron,rurus,rwkin,sasan,sdsnd,sgsag,shsrp,sisin,skslk,"
1116: + "slslv,smsmo,snsna,sosom,sqsqi,srsrp,ssssw,stsot,susun,svswe,swswa,tatam,tetel,"
1117: + "tgtgk,ththa,titir,tktuk,tltgl,tntsn,toton,trtur,tstso,tttat,twtwi,uguig,ukukr,"
1118: + "ururd,uzuzb,vivie,vovol,wowol,xhxho,yiyid,yoyor,zazha,zhzho,zuzul";
1119:
1120: /**
1121: * List of all 2-letter country codes currently defined in ISO 3166.
1122: * (Because the Java VM specification turns an array constant into executable code
1123: * that generates the array element by element, we keep the array in compressed
1124: * form in a single string and build the array from it at run time when requested.)
1125: * [We're now also using this table to store a mapping from 2-letter ISO country codes
1126: * to 3-letter ISO country codes. Each group of characters consists of a comma, a
1127: * 2-letter code, and a 3-letter code. We look up a 3-letter code by searching for
1128: * a comma followed by a 2-letter code and then getting the three letters following
1129: * the 2-letter code.]
1130: */
1131: private static String[] isoCountries = null;
1132: private static final String compressedIsoCountries = ",ADAND,AEARE,AFAFG,AGATG,AIAIA,ALALB,AMARM,ANANT,AOAGO,AQATA,ARARG,ASASM,ATAUT,"
1133: + "AUAUS,AWABW,AZAZE,BABIH,BBBRB,BDBGD,BEBEL,BFBFA,BGBGR,BHBHR,BIBDI,BJBEN,BMBMU,"
1134: + "BNBRN,BOBOL,BRBRA,BSBHS,BTBTN,BVBVT,BWBWA,BYBLR,BZBLZ,CACAN,CCCCK,CFCAF,CGCOG,"
1135: + "CHCHE,CICIV,CKCOK,CLCHL,CMCMR,CNCHN,COCOL,CRCRI,CUCUB,CVCPV,CXCXR,CYCYP,CZCZE,"
1136: + "DEDEU,DJDJI,DKDNK,DMDMA,DODOM,DZDZA,ECECU,EEEST,EGEGY,EHESH,ERERI,ESESP,ETETH,"
1137: + "FIFIN,FJFJI,FKFLK,FMFSM,FOFRO,FRFRA,FXFXX,GAGAB,GBGBR,GDGRD,GEGEO,GFGUF,GHGHA,"
1138: + "GIGIB,GLGRL,GMGMB,GNGIN,GPGLP,GQGNQ,GRGRC,GSSGS,GTGTM,GUGUM,GWGNB,GYGUY,HKHKG,"
1139: + "HMHMD,HNHND,HRHRV,HTHTI,HUHUN,IDIDN,IEIRL,ILISR,ININD,IOIOT,IQIRQ,IRIRN,ISISL,"
1140: + "ITITA,JMJAM,JOJOR,JPJPN,KEKEN,KGKGZ,KHKHM,KIKIR,KMCOM,KNKNA,KPPRK,KRKOR,KWKWT,"
1141: + "KYCYM,KZKAZ,LALAO,LBLBN,LCLCA,LILIE,LKLKA,LRLBR,LSLSO,LTLTU,LULUX,LVLVA,LYLBY,"
1142: + "MAMAR,MCMCO,MDMDA,MGMDG,MHMHL,MKMKD,MLMLI,MMMMR,MNMNG,MOMAC,MPMNP,MQMTQ,MRMRT,"
1143: + "MSMSR,MTMLT,MUMUS,MVMDV,MWMWI,MXMEX,MYMYS,MZMOZ,NANAM,NCNCL,NENER,NFNFK,NGNGA,"
1144: + "NINIC,NLNLD,NONOR,NPNPL,NRNRU,NUNIU,NZNZL,OMOMN,PAPAN,PEPER,PFPYF,PGPNG,PHPHL,"
1145: + "PKPAK,PLPOL,PMSPM,PNPCN,PRPRI,PTPRT,PWPLW,PYPRY,QAQAT,REREU,ROROM,RURUS,RWRWA,"
1146: + "SASAU,SBSLB,SCSYC,SDSDN,SESWE,SGSGP,SHSHN,SISVN,SJSJM,SKSVK,SLSLE,SMSMR,SNSEN,"
1147: + "SOSOM,SRSUR,STSTP,SVSLV,SYSYR,SZSWZ,TCTCA,TDTCD,TFATF,TGTGO,THTHA,TJTJK,TKTKL,"
1148: + "TMTKM,TNTUN,TOTON,TPTMP,TRTUR,TTTTO,TVTUV,TWTWN,TZTZA,UAUKR,UGUGA,UMUMI,USUSA,"
1149: + "UYURY,UZUZB,VAVAT,VCVCT,VEVEN,VGVGB,VIVIR,VNVNM,VUVUT,WFWLF,WSWSM,YEYEM,YTMYT,"
1150: + "YUYUG,ZAZAF,ZMZMB,ZRZAR,ZWZWE";
1151:
1152: /*
1153: * Locale needs its own, locale insenitive version of toLowerCase to
1154: * avoid circularity problems between Locale and String.
1155: * The most straightforward algorithm is used. Look at optimizations later.
1156: */
1157: private String toLowerCase(String str) {
1158: char[] buf = str.toCharArray();
1159: for (int i = 0; i < buf.length; i++) {
1160: buf[i] = Character.toLowerCase(buf[i]);
1161: }
1162: return new String(buf);
1163: }
1164:
1165: /*
1166: * Locale needs its own, locale insensitive version of toUpperCase to
1167: * avoid circularity problems between Locale and String.
1168: * The most straightforward algorithm is used. Look at optimizations later.
1169: */
1170: private String toUpperCase(String str) {
1171: char[] buf = str.toCharArray();
1172: for (int i = 0; i < buf.length; i++) {
1173: buf[i] = Character.toUpperCase(buf[i]);
1174: }
1175: return new String(buf);
1176: }
1177:
1178: private String findStringMatch(String[][] languages,
1179: String desiredLanguage, String fallbackLanguage) {
1180: for (int i = 0; i < languages.length; ++i)
1181: if (desiredLanguage.equals(languages[i][0]))
1182: return languages[i][1];
1183: if (!fallbackLanguage.equals(desiredLanguage))
1184: for (int i = 0; i < languages.length; ++i)
1185: if (fallbackLanguage.equals(languages[i][0]))
1186: return languages[i][1];
1187: if (!"EN".equals(desiredLanguage)
1188: && "EN".equals(fallbackLanguage))
1189: for (int i = 0; i < languages.length; ++i)
1190: if ("EN".equals(languages[i][0]))
1191: return languages[i][1];
1192: return "";
1193: }
1194:
1195: private String convertOldISOCodes(String language) {
1196: // we accept both the old and the new ISO codes for the languages whose ISO
1197: // codes have changed, but we always store the OLD code, for backward compatibility
1198: language = toLowerCase(language).intern();
1199: if (language == "he") {
1200: return "iw";
1201: } else if (language == "yi") {
1202: return "ji";
1203: } else if (language == "id") {
1204: return "in";
1205: } else {
1206: return language;
1207: }
1208: }
1209: }
|