2007-12-01 00:00:00 +00:00
|
|
|
/*
|
2010-05-25 15:58:33 -07:00
|
|
|
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
|
2007-12-01 00:00:00 +00:00
|
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
|
|
*
|
|
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 only, as
|
2010-05-25 15:58:33 -07:00
|
|
|
* published by the Free Software Foundation. Oracle designates this
|
2007-12-01 00:00:00 +00:00
|
|
|
* particular file as subject to the "Classpath" exception as provided
|
2010-05-25 15:58:33 -07:00
|
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
2007-12-01 00:00:00 +00:00
|
|
|
*
|
|
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
|
|
* accompanied this code).
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License version
|
|
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
2010-05-25 15:58:33 -07:00
|
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
|
|
* questions.
|
2007-12-01 00:00:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
|
|
|
* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
|
|
|
|
*
|
|
|
|
* The original version of this source code and documentation
|
|
|
|
* is copyrighted and owned by Taligent, Inc., a wholly-owned
|
|
|
|
* subsidiary of IBM. These materials are provided under terms
|
|
|
|
* of a License Agreement between Taligent and Sun. This technology
|
|
|
|
* is protected by multiple US and International patents.
|
|
|
|
*
|
|
|
|
* This notice and attribution to Taligent may not be removed.
|
|
|
|
* Taligent is a registered trademark of Taligent, Inc.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
package java.util;
|
|
|
|
|
|
|
|
import java.io.*;
|
|
|
|
import java.security.AccessController;
|
|
|
|
import java.text.MessageFormat;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.spi.LocaleNameProvider;
|
|
|
|
import java.util.spi.LocaleServiceProvider;
|
|
|
|
import sun.security.action.GetPropertyAction;
|
|
|
|
import sun.util.LocaleServiceProviderPool;
|
|
|
|
import sun.util.resources.LocaleData;
|
|
|
|
import sun.util.resources.OpenListResourceBundle;
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* A <code>Locale</code> object represents a specific geographical, political,
|
|
|
|
* or cultural region. An operation that requires a <code>Locale</code> to perform
|
|
|
|
* its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
|
|
|
|
* to tailor information for the user. For example, displaying a number
|
|
|
|
* is a locale-sensitive operation--the number should be formatted
|
|
|
|
* according to the customs/conventions of the user's native country,
|
|
|
|
* region, or culture.
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* Create a <code>Locale</code> object using the constructors in this class:
|
|
|
|
* <blockquote>
|
|
|
|
* <pre>
|
|
|
|
* Locale(String language)
|
|
|
|
* Locale(String language, String country)
|
|
|
|
* Locale(String language, String country, String variant)
|
|
|
|
* </pre>
|
|
|
|
* </blockquote>
|
|
|
|
* The language argument is a valid <STRONG>ISO Language Code.</STRONG>
|
|
|
|
* These codes are the lower-case, two-letter codes as defined by ISO-639.
|
|
|
|
* You can find a full list of these codes at a number of sites, such as:
|
|
|
|
* <BR><a href ="http://www.loc.gov/standards/iso639-2/php/English_list.php">
|
|
|
|
* <code>http://www.loc.gov/standards/iso639-2/php/English_list.php</code></a>
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* The country argument is a valid <STRONG>ISO Country Code.</STRONG> These
|
|
|
|
* codes are the upper-case, two-letter codes as defined by ISO-3166.
|
|
|
|
* You can find a full list of these codes at a number of sites, such as:
|
|
|
|
* <BR><a href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">
|
|
|
|
* <code>http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html</code></a>
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* The variant argument is a vendor or browser-specific code.
|
|
|
|
* For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
|
|
|
|
* Where there are two variants, separate them with an underscore, and
|
|
|
|
* put the most important one first. For example, a Traditional Spanish collation
|
|
|
|
* might construct a locale with parameters for language, country and variant as:
|
|
|
|
* "es", "ES", "Traditional_WIN".
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* Because a <code>Locale</code> object is just an identifier for a region,
|
|
|
|
* no validity check is performed when you construct a <code>Locale</code>.
|
|
|
|
* If you want to see whether particular resources are available for the
|
|
|
|
* <code>Locale</code> you construct, you must query those resources. For
|
|
|
|
* example, ask the <code>NumberFormat</code> for the locales it supports
|
|
|
|
* using its <code>getAvailableLocales</code> method.
|
|
|
|
* <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
|
|
|
|
* locale, you get back the best available match, not necessarily
|
|
|
|
* precisely what you asked for. For more information, look at
|
|
|
|
* {@link ResourceBundle}.
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* The <code>Locale</code> class provides a number of convenient constants
|
|
|
|
* that you can use to create <code>Locale</code> objects for commonly used
|
|
|
|
* locales. For example, the following creates a <code>Locale</code> object
|
|
|
|
* for the United States:
|
|
|
|
* <blockquote>
|
|
|
|
* <pre>
|
|
|
|
* Locale.US
|
|
|
|
* </pre>
|
|
|
|
* </blockquote>
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* Once you've created a <code>Locale</code> you can query it for information about
|
|
|
|
* itself. Use <code>getCountry</code> to get the ISO Country Code and
|
|
|
|
* <code>getLanguage</code> to get the ISO Language Code. You can
|
|
|
|
* use <code>getDisplayCountry</code> to get the
|
|
|
|
* name of the country suitable for displaying to the user. Similarly,
|
|
|
|
* you can use <code>getDisplayLanguage</code> to get the name of
|
|
|
|
* the language suitable for displaying to the user. Interestingly,
|
|
|
|
* the <code>getDisplayXXX</code> methods are themselves locale-sensitive
|
|
|
|
* and have two versions: one that uses the default locale and one
|
|
|
|
* that uses the locale specified as an argument.
|
|
|
|
*
|
|
|
|
* <P>
|
|
|
|
* The Java Platform provides a number of classes that perform locale-sensitive
|
|
|
|
* operations. For example, the <code>NumberFormat</code> class formats
|
|
|
|
* numbers, currency, or percentages in a locale-sensitive manner. Classes
|
|
|
|
* such as <code>NumberFormat</code> have a number of convenience methods
|
|
|
|
* for creating a default object of that type. For example, the
|
|
|
|
* <code>NumberFormat</code> class provides these three convenience methods
|
|
|
|
* for creating a default <code>NumberFormat</code> object:
|
|
|
|
* <blockquote>
|
|
|
|
* <pre>
|
|
|
|
* NumberFormat.getInstance()
|
|
|
|
* NumberFormat.getCurrencyInstance()
|
|
|
|
* NumberFormat.getPercentInstance()
|
|
|
|
* </pre>
|
|
|
|
* </blockquote>
|
|
|
|
* These methods have two variants; one with an explicit locale
|
|
|
|
* and one without; the latter using the default locale.
|
|
|
|
* <blockquote>
|
|
|
|
* <pre>
|
|
|
|
* NumberFormat.getInstance(myLocale)
|
|
|
|
* NumberFormat.getCurrencyInstance(myLocale)
|
|
|
|
* NumberFormat.getPercentInstance(myLocale)
|
|
|
|
* </pre>
|
|
|
|
* </blockquote>
|
|
|
|
* A <code>Locale</code> is the mechanism for identifying the kind of object
|
|
|
|
* (<code>NumberFormat</code>) that you would like to get. The locale is
|
|
|
|
* <STRONG>just</STRONG> a mechanism for identifying objects,
|
|
|
|
* <STRONG>not</STRONG> a container for the objects themselves.
|
|
|
|
*
|
|
|
|
* @see ResourceBundle
|
|
|
|
* @see java.text.Format
|
|
|
|
* @see java.text.NumberFormat
|
|
|
|
* @see java.text.Collator
|
|
|
|
* @author Mark Davis
|
|
|
|
* @since 1.1
|
|
|
|
*/
|
|
|
|
|
|
|
|
public final class Locale implements Cloneable, Serializable {
|
|
|
|
|
|
|
|
// cache to store singleton Locales
|
|
|
|
private final static ConcurrentHashMap<String, Locale> cache =
|
|
|
|
new ConcurrentHashMap<String, Locale>(32);
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale ENGLISH = createSingleton("en__", "en", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale FRENCH = createSingleton("fr__", "fr", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale GERMAN = createSingleton("de__", "de", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale ITALIAN = createSingleton("it__", "it", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale JAPANESE = createSingleton("ja__", "ja", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale KOREAN = createSingleton("ko__", "ko", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale CHINESE = createSingleton("zh__", "zh", "");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale SIMPLIFIED_CHINESE = createSingleton("zh_CN_", "zh", "CN");
|
|
|
|
|
|
|
|
/** Useful constant for language.
|
|
|
|
*/
|
|
|
|
static public final Locale TRADITIONAL_CHINESE = createSingleton("zh_TW_", "zh", "TW");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale FRANCE = createSingleton("fr_FR_", "fr", "FR");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale GERMANY = createSingleton("de_DE_", "de", "DE");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale ITALY = createSingleton("it_IT_", "it", "IT");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale JAPAN = createSingleton("ja_JP_", "ja", "JP");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale KOREA = createSingleton("ko_KR_", "ko", "KR");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale CHINA = SIMPLIFIED_CHINESE;
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale PRC = SIMPLIFIED_CHINESE;
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale TAIWAN = TRADITIONAL_CHINESE;
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale UK = createSingleton("en_GB_", "en", "GB");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale US = createSingleton("en_US_", "en", "US");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale CANADA = createSingleton("en_CA_", "en", "CA");
|
|
|
|
|
|
|
|
/** Useful constant for country.
|
|
|
|
*/
|
|
|
|
static public final Locale CANADA_FRENCH = createSingleton("fr_CA_", "fr", "CA");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Useful constant for the root locale. The root locale is the locale whose
|
|
|
|
* language, country, and variant are empty ("") strings. This is regarded
|
|
|
|
* as the base locale of all locales, and is used as the language/country
|
|
|
|
* neutral locale for the locale sensitive operations.
|
|
|
|
*
|
|
|
|
* @since 1.6
|
|
|
|
*/
|
|
|
|
static public final Locale ROOT = createSingleton("__", "", "");
|
|
|
|
|
|
|
|
/** serialization ID
|
|
|
|
*/
|
|
|
|
static final long serialVersionUID = 9149081749638150636L;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display types for retrieving localized names from the name providers.
|
|
|
|
*/
|
|
|
|
private static final int DISPLAY_LANGUAGE = 0;
|
|
|
|
private static final int DISPLAY_COUNTRY = 1;
|
|
|
|
private static final int DISPLAY_VARIANT = 2;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a locale from language, country, variant.
|
|
|
|
* NOTE: ISO 639 is not a stable standard; some of the language codes it defines
|
|
|
|
* (specifically iw, ji, and in) have changed. This constructor accepts both the
|
|
|
|
* old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
|
|
|
|
* API on Locale will return only the OLD codes.
|
|
|
|
* @param language lowercase two-letter ISO-639 code.
|
|
|
|
* @param country uppercase two-letter ISO-3166 code.
|
|
|
|
* @param variant vendor and browser specific code. See class description.
|
|
|
|
* @exception NullPointerException thrown if any argument is null.
|
|
|
|
*/
|
|
|
|
public Locale(String language, String country, String variant) {
|
|
|
|
this.language = convertOldISOCodes(language);
|
|
|
|
this.country = toUpperCase(country).intern();
|
|
|
|
this.variant = variant.intern();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a locale from language, country.
|
|
|
|
* NOTE: ISO 639 is not a stable standard; some of the language codes it defines
|
|
|
|
* (specifically iw, ji, and in) have changed. This constructor accepts both the
|
|
|
|
* old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
|
|
|
|
* API on Locale will return only the OLD codes.
|
|
|
|
* @param language lowercase two-letter ISO-639 code.
|
|
|
|
* @param country uppercase two-letter ISO-3166 code.
|
|
|
|
* @exception NullPointerException thrown if either argument is null.
|
|
|
|
*/
|
|
|
|
public Locale(String language, String country) {
|
|
|
|
this(language, country, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a locale from a language code.
|
|
|
|
* NOTE: ISO 639 is not a stable standard; some of the language codes it defines
|
|
|
|
* (specifically iw, ji, and in) have changed. This constructor accepts both the
|
|
|
|
* old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
|
|
|
|
* API on Locale will return only the OLD codes.
|
|
|
|
* @param language lowercase two-letter ISO-639 code.
|
|
|
|
* @exception NullPointerException thrown if argument is null.
|
|
|
|
* @since 1.4
|
|
|
|
*/
|
|
|
|
public Locale(String language) {
|
|
|
|
this(language, "", "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a <code>Locale</code> using <code>language</code>
|
|
|
|
* and <code>country</code>. This constructor assumes that
|
|
|
|
* <code>language</code> and <code>contry</code> are interned and
|
|
|
|
* it is invoked by createSingleton only. (flag is just for
|
|
|
|
* avoiding the conflict with the public constructors.
|
|
|
|
*/
|
|
|
|
private Locale(String language, String country, boolean flag) {
|
|
|
|
this.language = language;
|
|
|
|
this.country = country;
|
|
|
|
this.variant = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a <code>Locale</code> instance with the given
|
|
|
|
* <code>language</code> and <code>counry</code> and puts the
|
|
|
|
* instance under the given <code>key</code> in the cache. This
|
|
|
|
* method must be called only when initializing the Locale
|
|
|
|
* constants.
|
|
|
|
*/
|
|
|
|
private static Locale createSingleton(String key, String language, String country) {
|
|
|
|
Locale locale = new Locale(language, country, false);
|
|
|
|
cache.put(key, locale);
|
|
|
|
return locale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a <code>Locale</code> constructed from the given
|
|
|
|
* <code>language</code>, <code>country</code> and
|
|
|
|
* <code>variant</code>. If the same <code>Locale</code> instance
|
|
|
|
* is available in the cache, then that instance is
|
|
|
|
* returned. Otherwise, a new <code>Locale</code> instance is
|
|
|
|
* created and cached.
|
|
|
|
*
|
|
|
|
* @param language lowercase two-letter ISO-639 code.
|
|
|
|
* @param country uppercase two-letter ISO-3166 code.
|
|
|
|
* @param variant vendor and browser specific code. See class description.
|
|
|
|
* @return the <code>Locale</code> instance requested
|
|
|
|
* @exception NullPointerException if any argument is null.
|
|
|
|
*/
|
|
|
|
static Locale getInstance(String language, String country, String variant) {
|
|
|
|
if (language== null || country == null || variant == null) {
|
|
|
|
throw new NullPointerException();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(language).append('_').append(country).append('_').append(variant);
|
|
|
|
String key = sb.toString();
|
|
|
|
Locale locale = cache.get(key);
|
|
|
|
if (locale == null) {
|
|
|
|
locale = new Locale(language, country, variant);
|
|
|
|
Locale l = cache.putIfAbsent(key, locale);
|
|
|
|
if (l != null) {
|
|
|
|
locale = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return locale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current value of the default locale for this instance
|
|
|
|
* of the Java Virtual Machine.
|
|
|
|
* <p>
|
|
|
|
* The Java Virtual Machine sets the default locale during startup
|
|
|
|
* based on the host environment. It is used by many locale-sensitive
|
|
|
|
* methods if no locale is explicitly specified.
|
|
|
|
* It can be changed using the
|
|
|
|
* {@link #setDefault(java.util.Locale) setDefault} method.
|
|
|
|
*
|
|
|
|
* @return the default locale for this instance of the Java Virtual Machine
|
|
|
|
*/
|
|
|
|
public static Locale getDefault() {
|
|
|
|
// do not synchronize this method - see 4071298
|
|
|
|
// it's OK if more than one default locale happens to be created
|
|
|
|
if (defaultLocale == null) {
|
|
|
|
String language, region, country, variant;
|
|
|
|
language = AccessController.doPrivileged(
|
|
|
|
new GetPropertyAction("user.language", "en"));
|
|
|
|
// for compatibility, check for old user.region property
|
|
|
|
region = AccessController.doPrivileged(
|
|
|
|
new GetPropertyAction("user.region"));
|
|
|
|
if (region != null) {
|
|
|
|
// region can be of form country, country_variant, or _variant
|
|
|
|
int i = region.indexOf('_');
|
|
|
|
if (i >= 0) {
|
|
|
|
country = region.substring(0, i);
|
|
|
|
variant = region.substring(i + 1);
|
|
|
|
} else {
|
|
|
|
country = region;
|
|
|
|
variant = "";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
country = AccessController.doPrivileged(
|
|
|
|
new GetPropertyAction("user.country", ""));
|
|
|
|
variant = AccessController.doPrivileged(
|
|
|
|
new GetPropertyAction("user.variant", ""));
|
|
|
|
}
|
|
|
|
defaultLocale = getInstance(language, country, variant);
|
|
|
|
}
|
|
|
|
return defaultLocale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the default locale for this instance of the Java Virtual Machine.
|
|
|
|
* This does not affect the host locale.
|
|
|
|
* <p>
|
|
|
|
* If there is a security manager, its <code>checkPermission</code>
|
|
|
|
* method is called with a <code>PropertyPermission("user.language", "write")</code>
|
|
|
|
* permission before the default locale is changed.
|
|
|
|
* <p>
|
|
|
|
* The Java Virtual Machine sets the default locale during startup
|
|
|
|
* based on the host environment. It is used by many locale-sensitive
|
|
|
|
* methods if no locale is explicitly specified.
|
|
|
|
* <p>
|
|
|
|
* Since changing the default locale may affect many different areas
|
|
|
|
* of functionality, this method should only be used if the caller
|
|
|
|
* is prepared to reinitialize locale-sensitive code running
|
|
|
|
* within the same Java Virtual Machine.
|
|
|
|
*
|
|
|
|
* @throws SecurityException
|
|
|
|
* if a security manager exists and its
|
|
|
|
* <code>checkPermission</code> method doesn't allow the operation.
|
|
|
|
* @throws NullPointerException if <code>newLocale</code> is null
|
|
|
|
* @param newLocale the new default locale
|
|
|
|
* @see SecurityManager#checkPermission
|
|
|
|
* @see java.util.PropertyPermission
|
|
|
|
*/
|
|
|
|
public static synchronized void setDefault(Locale newLocale) {
|
|
|
|
if (newLocale == null)
|
|
|
|
throw new NullPointerException("Can't set default locale to NULL");
|
|
|
|
|
|
|
|
SecurityManager sm = System.getSecurityManager();
|
|
|
|
if (sm != null) sm.checkPermission(new PropertyPermission
|
|
|
|
("user.language", "write"));
|
|
|
|
defaultLocale = newLocale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array of all installed locales.
|
|
|
|
* The returned array represents the union of locales supported
|
|
|
|
* by the Java runtime environment and by installed
|
|
|
|
* {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider}
|
|
|
|
* implementations. It must contain at least a <code>Locale</code>
|
|
|
|
* instance equal to {@link java.util.Locale#US Locale.US}.
|
|
|
|
*
|
|
|
|
* @return An array of installed locales.
|
|
|
|
*/
|
|
|
|
public static Locale[] getAvailableLocales() {
|
|
|
|
return LocaleServiceProviderPool.getAllAvailableLocales();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of all 2-letter country codes defined in ISO 3166.
|
|
|
|
* Can be used to create Locales.
|
|
|
|
*/
|
|
|
|
public static String[] getISOCountries() {
|
|
|
|
if (isoCountries == null) {
|
|
|
|
isoCountries = getISO2Table(LocaleISOData.isoCountryTable);
|
|
|
|
}
|
|
|
|
String[] result = new String[isoCountries.length];
|
|
|
|
System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of all 2-letter language codes defined in ISO 639.
|
|
|
|
* Can be used to create Locales.
|
|
|
|
* [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
|
|
|
|
* The list this function returns includes both the new and the old codes for the
|
|
|
|
* languages whose codes have changed.]
|
|
|
|
*/
|
|
|
|
public static String[] getISOLanguages() {
|
|
|
|
if (isoLanguages == null) {
|
|
|
|
isoLanguages = getISO2Table(LocaleISOData.isoLanguageTable);
|
|
|
|
}
|
|
|
|
String[] result = new String[isoLanguages.length];
|
|
|
|
System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final String[] getISO2Table(String table) {
|
|
|
|
int len = table.length() / 5;
|
|
|
|
String[] isoTable = new String[len];
|
|
|
|
for (int i = 0, j = 0; i < len; i++, j += 5) {
|
|
|
|
isoTable[i] = table.substring(j, j + 2);
|
|
|
|
}
|
|
|
|
return isoTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the language code for this locale, which will either be the empty string
|
|
|
|
* or a lowercase ISO 639 code.
|
|
|
|
* <p>NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
|
|
|
|
* Locale's constructor recognizes both the new and the old codes for the languages
|
|
|
|
* whose codes have changed, but this function always returns the old code. If you
|
|
|
|
* want to check for a specific language whose code has changed, don't do <pre>
|
|
|
|
* if (locale.getLanguage().equals("he"))
|
|
|
|
* ...
|
|
|
|
* </pre>Instead, do<pre>
|
|
|
|
* if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage()))
|
|
|
|
* ...</pre>
|
|
|
|
* @see #getDisplayLanguage
|
|
|
|
*/
|
|
|
|
public String getLanguage() {
|
|
|
|
return language;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the country/region code for this locale, which will
|
|
|
|
* either be the empty string or an uppercase ISO 3166 2-letter code.
|
|
|
|
* @see #getDisplayCountry
|
|
|
|
*/
|
|
|
|
public String getCountry() {
|
|
|
|
return country;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the variant code for this locale.
|
|
|
|
* @see #getDisplayVariant
|
|
|
|
*/
|
|
|
|
public String getVariant() {
|
|
|
|
return variant;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter for the programmatic name of the entire locale,
|
|
|
|
* with the language, country and variant separated by underbars.
|
|
|
|
* Language is always lower case, and country is always upper case.
|
|
|
|
* If the language is missing, the string will begin with an underbar.
|
|
|
|
* If both the language and country fields are missing, this function
|
|
|
|
* will return the empty string, even if the variant field is filled in
|
|
|
|
* (you can't have a locale with just a variant-- the variant must accompany
|
|
|
|
* a valid language or country code).
|
|
|
|
* Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
|
|
|
|
* @see #getDisplayName
|
|
|
|
*/
|
|
|
|
public final String toString() {
|
|
|
|
boolean l = language.length() != 0;
|
|
|
|
boolean c = country.length() != 0;
|
|
|
|
boolean v = variant.length() != 0;
|
|
|
|
StringBuilder result = new StringBuilder(language);
|
|
|
|
if (c||(l&&v)) {
|
|
|
|
result.append('_').append(country); // This may just append '_'
|
|
|
|
}
|
|
|
|
if (v&&(l||c)) {
|
|
|
|
result.append('_').append(variant);
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a three-letter abbreviation for this locale's language. If the locale
|
|
|
|
* doesn't specify a language, this will be the empty string. Otherwise, this will
|
|
|
|
* be a lowercase ISO 639-2/T language code.
|
|
|
|
* The ISO 639-2 language codes can be found on-line at
|
|
|
|
* <a href="http://www.loc.gov/standards/iso639-2/englangn.html">
|
|
|
|
* <code>http://www.loc.gov/standards/iso639-2/englangn.html</code>.</a>
|
|
|
|
* @exception MissingResourceException Throws MissingResourceException if the
|
|
|
|
* three-letter language abbreviation is not available for this locale.
|
|
|
|
*/
|
|
|
|
public String getISO3Language() throws MissingResourceException {
|
|
|
|
String language3 = getISO3Code(language, LocaleISOData.isoLanguageTable);
|
|
|
|
if (language3 == null) {
|
|
|
|
throw new MissingResourceException("Couldn't find 3-letter language code for "
|
|
|
|
+ language, "FormatData_" + toString(), "ShortLanguage");
|
|
|
|
}
|
|
|
|
return language3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a three-letter abbreviation for this locale's country. If the locale
|
|
|
|
* doesn't specify a country, this will be the empty string. Otherwise, this will
|
|
|
|
* be an uppercase ISO 3166 3-letter country code.
|
|
|
|
* The ISO 3166-2 country codes can be found on-line at
|
|
|
|
* <a href="http://www.davros.org/misc/iso3166.txt">
|
|
|
|
* <code>http://www.davros.org/misc/iso3166.txt</code>.</a>
|
|
|
|
* @exception MissingResourceException Throws MissingResourceException if the
|
|
|
|
* three-letter country abbreviation is not available for this locale.
|
|
|
|
*/
|
|
|
|
public String getISO3Country() throws MissingResourceException {
|
|
|
|
String country3 = getISO3Code(country, LocaleISOData.isoCountryTable);
|
|
|
|
if (country3 == null) {
|
|
|
|
throw new MissingResourceException("Couldn't find 3-letter country code for "
|
|
|
|
+ country, "FormatData_" + toString(), "ShortCountry");
|
|
|
|
}
|
|
|
|
return country3;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final String getISO3Code(String iso2Code, String table) {
|
|
|
|
int codeLength = iso2Code.length();
|
|
|
|
if (codeLength == 0) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
int tableLength = table.length();
|
|
|
|
int index = tableLength;
|
|
|
|
if (codeLength == 2) {
|
|
|
|
char c1 = iso2Code.charAt(0);
|
|
|
|
char c2 = iso2Code.charAt(1);
|
|
|
|
for (index = 0; index < tableLength; index += 5) {
|
|
|
|
if (table.charAt(index) == c1
|
|
|
|
&& table.charAt(index + 1) == c2) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return index < tableLength ? table.substring(index + 2, index + 5) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's language that is appropriate for display to the
|
|
|
|
* user.
|
|
|
|
* If possible, the name returned will be localized for the default locale.
|
|
|
|
* For example, if the locale is fr_FR and the default locale
|
|
|
|
* is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
|
|
|
|
* the default locale is fr_FR, getDisplayLanguage() will return "anglais".
|
|
|
|
* If the name returned cannot be localized for the default locale,
|
|
|
|
* (say, we don't have a Japanese name for Croatian),
|
|
|
|
* this function falls back on the English name, and uses the ISO code as a last-resort
|
|
|
|
* value. If the locale doesn't specify a language, this function returns the empty string.
|
|
|
|
*/
|
|
|
|
public final String getDisplayLanguage() {
|
|
|
|
return getDisplayLanguage(getDefault());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's language that is appropriate for display to the
|
|
|
|
* user.
|
|
|
|
* If possible, the name returned will be localized according to inLocale.
|
|
|
|
* For example, if the locale is fr_FR and inLocale
|
|
|
|
* is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
|
|
|
|
* inLocale is fr_FR, getDisplayLanguage() will return "anglais".
|
|
|
|
* If the name returned cannot be localized according to inLocale,
|
|
|
|
* (say, we don't have a Japanese name for Croatian),
|
|
|
|
* this function falls back on the English name, and finally
|
|
|
|
* on the ISO code as a last-resort value. If the locale doesn't specify a language,
|
|
|
|
* this function returns the empty string.
|
|
|
|
*
|
|
|
|
* @exception NullPointerException if <code>inLocale</code> is <code>null</code>
|
|
|
|
*/
|
|
|
|
public String getDisplayLanguage(Locale inLocale) {
|
|
|
|
return getDisplayString(language, inLocale, DISPLAY_LANGUAGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's country that is appropriate for display to the
|
|
|
|
* user.
|
|
|
|
* If possible, the name returned will be localized for the default locale.
|
|
|
|
* For example, if the locale is fr_FR and the default locale
|
|
|
|
* is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
|
|
|
|
* the default locale is fr_FR, getDisplayCountry() will return "Etats-Unis".
|
|
|
|
* If the name returned cannot be localized for the default locale,
|
|
|
|
* (say, we don't have a Japanese name for Croatia),
|
|
|
|
* this function falls back on the English name, and uses the ISO code as a last-resort
|
|
|
|
* value. If the locale doesn't specify a country, this function returns the empty string.
|
|
|
|
*/
|
|
|
|
public final String getDisplayCountry() {
|
|
|
|
return getDisplayCountry(getDefault());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's country that is appropriate for display to the
|
|
|
|
* user.
|
|
|
|
* If possible, the name returned will be localized according to inLocale.
|
|
|
|
* For example, if the locale is fr_FR and inLocale
|
|
|
|
* is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
|
|
|
|
* inLocale is fr_FR, getDisplayCountry() will return "Etats-Unis".
|
|
|
|
* If the name returned cannot be localized according to inLocale.
|
|
|
|
* (say, we don't have a Japanese name for Croatia),
|
|
|
|
* this function falls back on the English name, and finally
|
|
|
|
* on the ISO code as a last-resort value. If the locale doesn't specify a country,
|
|
|
|
* this function returns the empty string.
|
|
|
|
*
|
|
|
|
* @exception NullPointerException if <code>inLocale</code> is <code>null</code>
|
|
|
|
*/
|
|
|
|
public String getDisplayCountry(Locale inLocale) {
|
|
|
|
return getDisplayString(country, inLocale, DISPLAY_COUNTRY);
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getDisplayString(String code, Locale inLocale, int type) {
|
|
|
|
if (code.length() == 0) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inLocale == null) {
|
|
|
|
throw new NullPointerException();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
|
|
|
|
String key = (type == DISPLAY_VARIANT ? "%%"+code : code);
|
|
|
|
String result = null;
|
|
|
|
|
|
|
|
// Check whether a provider can provide an implementation that's closer
|
|
|
|
// to the requested locale than what the Java runtime itself can provide.
|
|
|
|
LocaleServiceProviderPool pool =
|
|
|
|
LocaleServiceProviderPool.getPool(LocaleNameProvider.class);
|
|
|
|
if (pool.hasProviders()) {
|
|
|
|
result = pool.getLocalizedObject(
|
|
|
|
LocaleNameGetter.INSTANCE,
|
|
|
|
inLocale, bundle, key,
|
|
|
|
type, code);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == null) {
|
|
|
|
result = bundle.getString(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result != null) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
// just fall through
|
|
|
|
}
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's variant code that is appropriate for display to the
|
|
|
|
* user. If possible, the name will be localized for the default locale. If the locale
|
|
|
|
* doesn't specify a variant code, this function returns the empty string.
|
|
|
|
*/
|
|
|
|
public final String getDisplayVariant() {
|
|
|
|
return getDisplayVariant(getDefault());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale's variant code that is appropriate for display to the
|
|
|
|
* user. If possible, the name will be localized for inLocale. If the locale
|
|
|
|
* doesn't specify a variant code, this function returns the empty string.
|
|
|
|
*
|
|
|
|
* @exception NullPointerException if <code>inLocale</code> is <code>null</code>
|
|
|
|
*/
|
|
|
|
public String getDisplayVariant(Locale inLocale) {
|
|
|
|
if (variant.length() == 0)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
|
|
|
|
|
|
|
|
String names[] = getDisplayVariantArray(bundle, inLocale);
|
|
|
|
|
|
|
|
// Get the localized patterns for formatting a list, and use
|
|
|
|
// them to format the list.
|
|
|
|
String listPattern = null;
|
|
|
|
String listCompositionPattern = null;
|
|
|
|
try {
|
|
|
|
listPattern = bundle.getString("ListPattern");
|
|
|
|
listCompositionPattern = bundle.getString("ListCompositionPattern");
|
|
|
|
} catch (MissingResourceException e) {
|
|
|
|
}
|
|
|
|
return formatList(names, listPattern, listCompositionPattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale that is appropriate for display to the
|
|
|
|
* user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
|
|
|
|
* and getDisplayVariant() assembled into a single string. The display name will have
|
|
|
|
* one of the following forms:<p><blockquote>
|
|
|
|
* language (country, variant)<p>
|
|
|
|
* language (country)<p>
|
|
|
|
* language (variant)<p>
|
|
|
|
* country (variant)<p>
|
|
|
|
* language<p>
|
|
|
|
* country<p>
|
|
|
|
* variant<p></blockquote>
|
|
|
|
* depending on which fields are specified in the locale. If the language, country,
|
|
|
|
* and variant fields are all empty, this function returns the empty string.
|
|
|
|
*/
|
|
|
|
public final String getDisplayName() {
|
|
|
|
return getDisplayName(getDefault());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a name for the locale that is appropriate for display to the
|
|
|
|
* user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
|
|
|
|
* and getDisplayVariant() assembled into a single string. The display name will have
|
|
|
|
* one of the following forms:<p><blockquote>
|
|
|
|
* language (country, variant)<p>
|
|
|
|
* language (country)<p>
|
|
|
|
* language (variant)<p>
|
|
|
|
* country (variant)<p>
|
|
|
|
* language<p>
|
|
|
|
* country<p>
|
|
|
|
* variant<p></blockquote>
|
|
|
|
* depending on which fields are specified in the locale. If the language, country,
|
|
|
|
* and variant fields are all empty, this function returns the empty string.
|
|
|
|
*
|
|
|
|
* @exception NullPointerException if <code>inLocale</code> is <code>null</code>
|
|
|
|
*/
|
|
|
|
public String getDisplayName(Locale inLocale) {
|
|
|
|
OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
|
|
|
|
|
|
|
|
String languageName = getDisplayLanguage(inLocale);
|
|
|
|
String countryName = getDisplayCountry(inLocale);
|
|
|
|
String[] variantNames = getDisplayVariantArray(bundle, inLocale);
|
|
|
|
|
|
|
|
// Get the localized patterns for formatting a display name.
|
|
|
|
String displayNamePattern = null;
|
|
|
|
String listPattern = null;
|
|
|
|
String listCompositionPattern = null;
|
|
|
|
try {
|
|
|
|
displayNamePattern = bundle.getString("DisplayNamePattern");
|
|
|
|
listPattern = bundle.getString("ListPattern");
|
|
|
|
listCompositionPattern = bundle.getString("ListCompositionPattern");
|
|
|
|
} catch (MissingResourceException e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// The display name consists of a main name, followed by qualifiers.
|
|
|
|
// Typically, the format is "MainName (Qualifier, Qualifier)" but this
|
|
|
|
// depends on what pattern is stored in the display locale.
|
|
|
|
String mainName = null;
|
|
|
|
String[] qualifierNames = null;
|
|
|
|
|
|
|
|
// The main name is the language, or if there is no language, the country.
|
|
|
|
// If there is neither language nor country (an anomalous situation) then
|
|
|
|
// the display name is simply the variant's display name.
|
|
|
|
if (languageName.length() != 0) {
|
|
|
|
mainName = languageName;
|
|
|
|
if (countryName.length() != 0) {
|
|
|
|
qualifierNames = new String[variantNames.length + 1];
|
|
|
|
System.arraycopy(variantNames, 0, qualifierNames, 1, variantNames.length);
|
|
|
|
qualifierNames[0] = countryName;
|
|
|
|
}
|
|
|
|
else qualifierNames = variantNames;
|
|
|
|
}
|
|
|
|
else if (countryName.length() != 0) {
|
|
|
|
mainName = countryName;
|
|
|
|
qualifierNames = variantNames;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return formatList(variantNames, listPattern, listCompositionPattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an array whose first element is the number of remaining
|
|
|
|
// elements. This serves as a selector into a ChoiceFormat pattern from
|
|
|
|
// the resource. The second and third elements are the main name and
|
|
|
|
// the qualifier; if there are no qualifiers, the third element is
|
|
|
|
// unused by the format pattern.
|
|
|
|
Object[] displayNames = {
|
|
|
|
new Integer(qualifierNames.length != 0 ? 2 : 1),
|
|
|
|
mainName,
|
|
|
|
// We could also just call formatList() and have it handle the empty
|
|
|
|
// list case, but this is more efficient, and we want it to be
|
|
|
|
// efficient since all the language-only locales will not have any
|
|
|
|
// qualifiers.
|
|
|
|
qualifierNames.length != 0 ? formatList(qualifierNames, listPattern, listCompositionPattern) : null
|
|
|
|
};
|
|
|
|
|
|
|
|
if (displayNamePattern != null) {
|
|
|
|
return new MessageFormat(displayNamePattern).format(displayNames);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// If we cannot get the message format pattern, then we use a simple
|
|
|
|
// hard-coded pattern. This should not occur in practice unless the
|
|
|
|
// installation is missing some core files (FormatData etc.).
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
result.append((String)displayNames[1]);
|
|
|
|
if (displayNames.length > 2) {
|
|
|
|
result.append(" (");
|
|
|
|
result.append((String)displayNames[2]);
|
|
|
|
result.append(')');
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overrides Cloneable
|
|
|
|
*/
|
|
|
|
public Object clone()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
Locale that = (Locale)super.clone();
|
|
|
|
return that;
|
|
|
|
} catch (CloneNotSupportedException e) {
|
|
|
|
throw new InternalError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Override hashCode.
|
|
|
|
* Since Locales are often used in hashtables, caches the value
|
|
|
|
* for speed.
|
|
|
|
*/
|
|
|
|
public int hashCode() {
|
|
|
|
int hc = hashCodeValue;
|
|
|
|
if (hc == 0) {
|
|
|
|
hc = (language.hashCode() << 8) ^ country.hashCode() ^ (variant.hashCode() << 4);
|
|
|
|
hashCodeValue = hc;
|
|
|
|
}
|
|
|
|
return hc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overrides
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if this Locale is equal to another object. A Locale is
|
|
|
|
* deemed equal to another Locale with identical language, country,
|
|
|
|
* and variant, and unequal to all other objects.
|
|
|
|
*
|
|
|
|
* @return true if this Locale is equal to the specified object.
|
|
|
|
*/
|
|
|
|
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (this == obj) // quick check
|
|
|
|
return true;
|
|
|
|
if (!(obj instanceof Locale))
|
|
|
|
return false;
|
|
|
|
Locale other = (Locale) obj;
|
|
|
|
return language == other.language
|
|
|
|
&& country == other.country
|
|
|
|
&& variant == other.variant;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ================= privates =====================================
|
|
|
|
|
|
|
|
// XXX instance and class variables. For now keep these separate, since it is
|
|
|
|
// faster to match. Later, make into single string.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @serial
|
|
|
|
* @see #getLanguage
|
|
|
|
*/
|
|
|
|
private final String language;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @serial
|
|
|
|
* @see #getCountry
|
|
|
|
*/
|
|
|
|
private final String country;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @serial
|
|
|
|
* @see #getVariant
|
|
|
|
*/
|
|
|
|
private final String variant;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Placeholder for the object's hash code. Always -1.
|
|
|
|
* @serial
|
|
|
|
*/
|
|
|
|
private volatile int hashcode = -1; // lazy evaluate
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculated hashcode to fix 4518797.
|
|
|
|
*/
|
|
|
|
private transient volatile int hashCodeValue = 0;
|
|
|
|
|
|
|
|
private static Locale defaultLocale = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return an array of the display names of the variant.
|
|
|
|
* @param bundle the ResourceBundle to use to get the display names
|
|
|
|
* @return an array of display names, possible of zero length.
|
|
|
|
*/
|
|
|
|
private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) {
|
|
|
|
// Split the variant name into tokens separated by '_'.
|
|
|
|
StringTokenizer tokenizer = new StringTokenizer(variant, "_");
|
|
|
|
String[] names = new String[tokenizer.countTokens()];
|
|
|
|
|
|
|
|
// For each variant token, lookup the display name. If
|
|
|
|
// not found, use the variant name itself.
|
|
|
|
for (int i=0; i<names.length; ++i) {
|
|
|
|
names[i] = getDisplayString(tokenizer.nextToken(),
|
|
|
|
inLocale, DISPLAY_VARIANT);
|
|
|
|
}
|
|
|
|
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Format a list using given pattern strings.
|
|
|
|
* If either of the patterns is null, then a the list is
|
|
|
|
* formatted by concatenation with the delimiter ','.
|
|
|
|
* @param stringList the list of strings to be formatted.
|
|
|
|
* @param listPattern should create a MessageFormat taking 0-3 arguments
|
|
|
|
* and formatting them into a list.
|
|
|
|
* @param listCompositionPattern should take 2 arguments
|
|
|
|
* and is used by composeList.
|
|
|
|
* @return a string representing the list.
|
|
|
|
*/
|
|
|
|
private static String formatList(String[] stringList, String listPattern, String listCompositionPattern) {
|
|
|
|
// If we have no list patterns, compose the list in a simple,
|
|
|
|
// non-localized way.
|
|
|
|
if (listPattern == null || listCompositionPattern == null) {
|
|
|
|
StringBuffer result = new StringBuffer();
|
|
|
|
for (int i=0; i<stringList.length; ++i) {
|
|
|
|
if (i>0) result.append(',');
|
|
|
|
result.append(stringList[i]);
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compose the list down to three elements if necessary
|
|
|
|
if (stringList.length > 3) {
|
|
|
|
MessageFormat format = new MessageFormat(listCompositionPattern);
|
|
|
|
stringList = composeList(format, stringList);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebuild the argument list with the list length as the first element
|
|
|
|
Object[] args = new Object[stringList.length + 1];
|
|
|
|
System.arraycopy(stringList, 0, args, 1, stringList.length);
|
|
|
|
args[0] = new Integer(stringList.length);
|
|
|
|
|
|
|
|
// Format it using the pattern in the resource
|
|
|
|
MessageFormat format = new MessageFormat(listPattern);
|
|
|
|
return format.format(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a list of strings, return a list shortened to three elements.
|
|
|
|
* Shorten it by applying the given format to the first two elements
|
|
|
|
* recursively.
|
|
|
|
* @param format a format which takes two arguments
|
|
|
|
* @param list a list of strings
|
|
|
|
* @return if the list is three elements or shorter, the same list;
|
|
|
|
* otherwise, a new list of three elements.
|
|
|
|
*/
|
|
|
|
private static String[] composeList(MessageFormat format, String[] list) {
|
|
|
|
if (list.length <= 3) return list;
|
|
|
|
|
|
|
|
// Use the given format to compose the first two elements into one
|
|
|
|
String[] listItems = { list[0], list[1] };
|
|
|
|
String newItem = format.format(listItems);
|
|
|
|
|
|
|
|
// Form a new list one element shorter
|
|
|
|
String[] newList = new String[list.length-1];
|
|
|
|
System.arraycopy(list, 2, newList, 1, newList.length-1);
|
|
|
|
newList[0] = newItem;
|
|
|
|
|
|
|
|
// Recurse
|
|
|
|
return composeList(format, newList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace the deserialized Locale object with a newly
|
|
|
|
* created object. Newer language codes are replaced with older ISO
|
|
|
|
* codes. The country and variant codes are replaced with internalized
|
|
|
|
* String copies.
|
|
|
|
*/
|
|
|
|
private Object readResolve() throws java.io.ObjectStreamException {
|
|
|
|
return getInstance(language, country, variant);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static volatile String[] isoLanguages = null;
|
|
|
|
|
|
|
|
private static volatile String[] isoCountries = null;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Locale needs its own, locale insensitive version of toLowerCase to
|
|
|
|
* avoid circularity problems between Locale and String.
|
|
|
|
* The most straightforward algorithm is used. Look at optimizations later.
|
|
|
|
*/
|
|
|
|
private String toLowerCase(String str) {
|
|
|
|
char[] buf = new char[str.length()];
|
|
|
|
for (int i = 0; i < buf.length; i++) {
|
|
|
|
buf[i] = Character.toLowerCase(str.charAt(i));
|
|
|
|
}
|
|
|
|
return new String( buf );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Locale needs its own, locale insensitive version of toUpperCase to
|
|
|
|
* avoid circularity problems between Locale and String.
|
|
|
|
* The most straightforward algorithm is used. Look at optimizations later.
|
|
|
|
*/
|
|
|
|
private String toUpperCase(String str) {
|
|
|
|
char[] buf = new char[str.length()];
|
|
|
|
for (int i = 0; i < buf.length; i++) {
|
|
|
|
buf[i] = Character.toUpperCase(str.charAt(i));
|
|
|
|
}
|
|
|
|
return new String( buf );
|
|
|
|
}
|
|
|
|
|
|
|
|
private String convertOldISOCodes(String language) {
|
|
|
|
// we accept both the old and the new ISO codes for the languages whose ISO
|
|
|
|
// codes have changed, but we always store the OLD code, for backward compatibility
|
|
|
|
language = toLowerCase(language).intern();
|
|
|
|
if (language == "he") {
|
|
|
|
return "iw";
|
|
|
|
} else if (language == "yi") {
|
|
|
|
return "ji";
|
|
|
|
} else if (language == "id") {
|
|
|
|
return "in";
|
|
|
|
} else {
|
|
|
|
return language;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtains a localized locale names from a LocaleNameProvider
|
|
|
|
* implementation.
|
|
|
|
*/
|
|
|
|
private static class LocaleNameGetter
|
|
|
|
implements LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, String> {
|
|
|
|
private static final LocaleNameGetter INSTANCE = new LocaleNameGetter();
|
|
|
|
|
|
|
|
public String getObject(LocaleNameProvider localeNameProvider,
|
|
|
|
Locale locale,
|
|
|
|
String key,
|
|
|
|
Object... params) {
|
|
|
|
assert params.length == 2;
|
|
|
|
int type = (Integer)params[0];
|
|
|
|
String code = (String)params[1];
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case DISPLAY_LANGUAGE:
|
|
|
|
return localeNameProvider.getDisplayLanguage(code, locale);
|
|
|
|
case DISPLAY_COUNTRY:
|
|
|
|
return localeNameProvider.getDisplayCountry(code, locale);
|
|
|
|
case DISPLAY_VARIANT:
|
|
|
|
return localeNameProvider.getDisplayVariant(code, locale);
|
|
|
|
default:
|
|
|
|
assert false; // shouldn't happen
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|