提交 557a5a33 编写于 作者: N naoto
上级 c8875b5c
......@@ -183,10 +183,22 @@ JAVA_JAVA_java = \
java/util/MissingFormatWidthException.java \
java/util/UnknownFormatConversionException.java \
java/util/UnknownFormatFlagsException.java \
java/util/IllformedLocaleException.java \
java/util/FormatterClosedException.java \
java/util/ListResourceBundle.java \
sun/util/EmptyListResourceBundle.java \
java/util/Locale.java \
sun/util/locale/AsciiUtil.java \
sun/util/locale/BaseLocale.java \
sun/util/locale/Extension.java \
sun/util/locale/InternalLocaleBuilder.java \
sun/util/locale/LanguageTag.java \
sun/util/locale/LocaleExtensions.java \
sun/util/locale/LocaleObjectCache.java \
sun/util/locale/LocaleSyntaxException.java \
sun/util/locale/ParseStatus.java \
sun/util/locale/StringTokenIterator.java \
sun/util/locale/UnicodeLocaleExtension.java \
java/util/LocaleISOData.java \
sun/util/LocaleServiceProviderPool.java \
sun/util/LocaleDataMetaInfo.java \
......
/*
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -43,10 +43,10 @@ import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.util.Currency;
import java.util.Hashtable;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.spi.LocaleServiceProvider;
import java.util.concurrent.ConcurrentHashMap;
import sun.util.LocaleServiceProviderPool;
import sun.util.resources.LocaleData;
......@@ -527,10 +527,17 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
// get resource bundle data - try the cache first
boolean needCacheUpdate = false;
Object[] data = (Object[]) cachedLocaleData.get(locale);
Object[] data = cachedLocaleData.get(locale);
if (data == null) { /* cache miss */
// When numbering system is thai (Locale's extension contains u-nu-thai),
// we read the data from th_TH_TH.
Locale lookupLocale = locale;
String numberType = locale.getUnicodeLocaleType("nu");
if (numberType != null && numberType.equals("thai")) {
lookupLocale = new Locale("th", "TH", "TH");
}
data = new Object[3];
ResourceBundle rb = LocaleData.getNumberFormatData(locale);
ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
data[0] = rb.getStringArray("NumberElements");
needCacheUpdate = true;
}
......@@ -586,7 +593,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
monetarySeparator = decimalSeparator;
if (needCacheUpdate) {
cachedLocaleData.put(locale, data);
cachedLocaleData.putIfAbsent(locale, data);
}
}
......@@ -806,7 +813,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
* cache to hold the NumberElements and the Currency
* of a Locale.
*/
private static final Hashtable cachedLocaleData = new Hashtable(3);
private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
/**
* Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
......
......@@ -1013,19 +1013,30 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
// If the specified locale is a Thai locale, returns a BuddhistCalendar
// instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
return new sun.util.BuddhistCalendar(zone, aLocale);
} else if ("JP".equals(aLocale.getVariant())
&& "JP".equals(aLocale.getCountry())
&& "ja".equals(aLocale.getLanguage())) {
return new JapaneseImperialCalendar(zone, aLocale);
Calendar cal = null;
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype == null) {
// Calendar type is not specified.
// If the specified locale is a Thai locale,
// returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
} else if (caltype.equals("japanese")) {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
// Unsupported calendar type.
// Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
}
// else create the default calendar
return new GregorianCalendar(zone, aLocale);
return cal;
}
/**
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package java.util;
/**
* Thrown by methods in {@link Locale} and {@link Locale.Builder} to
* indicate that an argument is not a well-formed BCP 47 tag.
*
* @see Locale
* @since 1.7
*/
public class IllformedLocaleException extends RuntimeException {
private static final long serialVersionUID = -5245986824925681401L;
private int _errIdx = -1;
/**
* Constructs a new <code>IllformedLocaleException</code> with no
* detail message and -1 as the error index.
*/
public IllformedLocaleException() {
super();
}
/**
* Constructs a new <code>IllformedLocaleException</code> with the
* given message and -1 as the error index.
*
* @param message the message
*/
public IllformedLocaleException(String message) {
super(message);
}
/**
* Constructs a new <code>IllformedLocaleException</code> with the
* given message and the error index. The error index is the approximate
* offset from the start of the ill-formed value to the point where the
* parse first detected an error. A negative error index value indicates
* either the error index is not applicable or unknown.
*
* @param message the message
* @param errorIndex the index
*/
public IllformedLocaleException(String message, int errorIndex) {
super(message + ((errorIndex < 0) ? "" : " [at index " + errorIndex + "]"));
_errIdx = errorIndex;
}
/**
* Returns the index where the error was found. A negative value indicates
* either the error index is not applicable or unknown.
*
* @return the error index
*/
public int getErrorIndex() {
return _errIdx;
}
}
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -44,22 +44,23 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider {
}
/**
* Returns a localized name for the given ISO 639 language code and the
* given locale that is appropriate for display to the user.
* Returns a localized name for the given <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
* IETF BCP47</a> language code and the given locale that is appropriate for
* display to the user.
* For example, if <code>languageCode</code> is "fr" and <code>locale</code>
* is en_US, getDisplayLanguage() will return "French"; if <code>languageCode</code>
* is "en" and <code>locale</code> is fr_FR, getDisplayLanguage() will return "anglais".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatian),
* this method returns null.
* @param languageCode the ISO 639 language code string in the form of two
* @param languageCode the language code string in the form of two to eight
* lower-case letters between 'a' (U+0061) and 'z' (U+007A)
* @param locale the desired locale
* @return the name of the given language code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>languageCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>languageCode</code> is not in the form of
* two lower-case letters, or <code>locale</code> isn't
* two or three lower-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
......@@ -68,22 +69,52 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider {
public abstract String getDisplayLanguage(String languageCode, Locale locale);
/**
* Returns a localized name for the given ISO 3166 country code and the
* given locale that is appropriate for display to the user.
* Returns a localized name for the given <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
* IETF BCP47</a> script code and the given locale that is appropriate for
* display to the user.
* For example, if <code>scriptCode</code> is "Latn" and <code>locale</code>
* is en_US, getDisplayScript() will return "Latin"; if <code>scriptCode</code>
* is "Cyrl" and <code>locale</code> is fr_FR, getDisplayScript() will return "cyrillique".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Cyrillic),
* this method returns null.
* @param scriptCode the four letter script code string in the form of title-case
* letters (the first letter is upper-case character between 'A' (U+0041) and
* 'Z' (U+005A) followed by three lower-case character between 'a' (U+0061)
* and 'z' (U+007A)).
* @param locale the desired locale
* @return the name of the given script code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>scriptCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>scriptCode</code> is not in the form of
* four title case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayScript(java.util.Locale)
* @since 1.7
*/
public abstract String getDisplayScript(String scriptCode, Locale locale);
/**
* Returns a localized name for the given <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
* IETF BCP47</a> region code (either ISO 3166 country code or UN M.49 area
* codes) and the given locale that is appropriate for display to the user.
* For example, if <code>countryCode</code> is "FR" and <code>locale</code>
* is en_US, getDisplayCountry() will return "France"; if <code>countryCode</code>
* is "US" and <code>locale</code> is fr_FR, getDisplayCountry() will return "Etats-Unis".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatia),
* this method returns null.
* @param countryCode the ISO 3166 country code string in the form of two
* upper-case letters between 'A' (U+0041) and 'Z' (U+005A)
* @param countryCode the country(region) code string in the form of two
* upper-case letters between 'A' (U+0041) and 'Z' (U+005A) or the UN M.49 area code
* in the form of three digit letters between '0' (U+0030) and '9' (U+0039).
* @param locale the desired locale
* @return the name of the given country code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>countryCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>countryCode</code> is not in the form of
* two upper-case letters, or <code>locale</code> isn't
* two upper-case letters or three digit letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
......
......@@ -86,18 +86,19 @@ import java.util.Locale;
* Otherwise, they call the <code>getAvailableLocales()</code> methods of
* installed providers for the appropriate interface to find one that
* supports the requested locale. If such a provider is found, its other
* methods are called to obtain the requested object or name. If neither
* the Java runtime environment itself nor an installed provider supports
* the requested locale, a fallback locale is constructed by replacing the
* first of the variant, country, or language strings of the locale that's
* not an empty string with an empty string, and the lookup process is
* restarted. In the case that the variant contains one or more '_'s, the
* fallback locale is constructed by replacing the variant with a new variant
* which eliminates the last '_' and the part following it. Even if a
* fallback occurs, methods that return requested objects or name are
* invoked with the original locale before the fallback.The Java runtime
* environment must support the root locale for all locale sensitive services
* in order to guarantee that this process terminates.
* methods are called to obtain the requested object or name. When checking
* whether a locale is supported, the locale's extensions are ignored.
* If neither the Java runtime environment itself nor an installed provider
* supports the requested locale, the methods go through a list of candidate
* locales and repeat the availability check for each until a match is found.
* The algorithm used for creating a list of candidate locales is same as
* the one used by <code>ResourceBunlde</code> by default (see
* {@link java.util.ResourceBundle.Control#getCandidateLocales getCandidateLocales}
* for the details). Even if a locale is resolved from the candidate list,
* methods that return requested objects or names are invoked with the original
* requested locale including extensions. The Java runtime environment must
* support the root locale for all locale sensitive services in order to
* guarantee that this process terminates.
* <p>
* Providers of names (but not providers of other objects) are allowed to
* return null for some name requests even for locales that they claim to
......@@ -124,6 +125,11 @@ public abstract class LocaleServiceProvider {
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
* <p>
* <b>Note:</b> Extensions in a <code>Locale</code> are ignored during
* service provider lookup. So the array returned by this method should
* not include two or more <code>Locale</code> objects only differing in
* their extensions.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
......
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -28,18 +28,20 @@ package sun.util;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.IllformedLocaleException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Builder;
import java.util.Map;
import java.util.ResourceBundle.Control;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.spi.LocaleServiceProvider;
import sun.util.logging.PlatformLogger;
import sun.util.resources.LocaleData;
import sun.util.resources.OpenListResourceBundle;
......@@ -89,6 +91,16 @@ public final class LocaleServiceProviderPool {
*/
private Set<Locale> providerLocales = null;
/**
* Special locale for ja_JP with Japanese calendar
*/
private static Locale locale_ja_JP_JP = new Locale("ja", "JP", "JP");
/**
* Special locale for th_TH with Thai numbering system
*/
private static Locale locale_th_TH_TH = new Locale("th", "TH", "TH");
/**
* A factory method that returns a singleton instance
*/
......@@ -153,14 +165,20 @@ public final class LocaleServiceProviderPool {
java.util.spi.CurrencyNameProvider.class,
java.util.spi.LocaleNameProvider.class,
java.util.spi.TimeZoneNameProvider.class };
Set<Locale> all = new HashSet<Locale>(Arrays.asList(
LocaleData.getAvailableLocales())
);
// Normalize locales for look up
Locale[] allLocales = LocaleData.getAvailableLocales();
Set<Locale> all = new HashSet<Locale>(allLocales.length);
for (Locale locale : allLocales) {
all.add(getLookupLocale(locale));
}
for (Class providerClass : providerClasses) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(providerClass);
all.addAll(pool.getProviderLocales());
}
allAvailableLocales = all.toArray(new Locale[0]);
}
}
......@@ -196,7 +214,8 @@ public final class LocaleServiceProviderPool {
}
/**
* Returns an array of available locales from providers.
* Returns an array of available locales (already normalized
* for service lookup) from providers.
* Note that this method does not return a defensive copy.
*
* @return list of the provider locales
......@@ -208,7 +227,7 @@ public final class LocaleServiceProviderPool {
for (LocaleServiceProvider lsp : providers) {
Locale[] locales = lsp.getAvailableLocales();
for (Locale locale: locales) {
providerLocales.add(locale);
providerLocales.add(getLookupLocale(locale));
}
}
}
......@@ -227,15 +246,19 @@ public final class LocaleServiceProviderPool {
}
/**
* Returns an array of available locales supported by the JRE.
* Returns an array of available locales (already normalized for
* service lookup) supported by the JRE.
* Note that this method does not return a defensive copy.
*
* @return list of the available JRE locales
*/
private synchronized List<Locale> getJRELocales() {
if (availableJRELocales == null) {
availableJRELocales =
Arrays.asList(LocaleData.getAvailableLocales());
Locale[] allLocales = LocaleData.getAvailableLocales();
availableJRELocales = new ArrayList<Locale>(allLocales.length);
for (Locale locale : allLocales) {
availableJRELocales.add(getLookupLocale(locale));
}
}
return availableJRELocales;
}
......@@ -249,7 +272,7 @@ public final class LocaleServiceProviderPool {
*/
private boolean isJRESupported(Locale locale) {
List<Locale> locales = getJRELocales();
return locales.contains(locale);
return locales.contains(getLookupLocale(locale));
}
/**
......@@ -325,7 +348,7 @@ public final class LocaleServiceProviderPool {
bundleKey = key;
}
Locale bundleLocale = (bundle != null ? bundle.getLocale() : null);
Locale requested = locale;
List<Locale> lookupLocales = getLookupLocales(locale);
P lsp;
S providersObj = null;
......@@ -333,21 +356,30 @@ public final class LocaleServiceProviderPool {
// to the requested locale than the bundle we've found (for
// localized names), or Java runtime's supported locale
// (for localized objects)
while ((locale = findProviderLocale(locale, bundleLocale)) != null) {
lsp = (P)findProvider(locale);
if (lsp != null) {
providersObj = getter.getObject(lsp, requested, key, params);
if (providersObj != null) {
return providersObj;
} else if (isObjectProvider) {
config(
"A locale sensitive service provider returned null for a localized objects, which should not happen. provider: " + lsp + " locale: " + requested);
Set<Locale> provLoc = getProviderLocales();
for (int i = 0; i < lookupLocales.size(); i++) {
Locale current = lookupLocales.get(i);
if (bundleLocale != null) {
if (current.equals(bundleLocale)) {
break;
}
} else {
if (isJRESupported(current)) {
break;
}
}
if (provLoc.contains(current)) {
lsp = (P)findProvider(current);
if (lsp != null) {
providersObj = getter.getObject(lsp, locale, key, params);
if (providersObj != null) {
return providersObj;
} else if (isObjectProvider) {
config(
"A locale sensitive service provider returned null for a localized objects, which should not happen. provider: " + lsp + " locale: " + locale);
}
}
}
locale = getParentLocale(locale);
}
// look up the JRE bundle and its parent chain. Only
......@@ -361,7 +393,7 @@ public final class LocaleServiceProviderPool {
} else {
lsp = (P)findProvider(bundleLocale);
if (lsp != null) {
providersObj = getter.getObject(lsp, requested, key, params);
providersObj = getter.getObject(lsp, locale, key, params);
if (providersObj != null) {
return providersObj;
}
......@@ -399,6 +431,8 @@ public final class LocaleServiceProviderPool {
for (LocaleServiceProvider lsp : providers) {
Locale[] locales = lsp.getAvailableLocales();
for (Locale available: locales) {
// normalize
available = getLookupLocale(available);
if (locale.equals(available)) {
LocaleServiceProvider providerInCache =
providersCache.put(locale, lsp);
......@@ -414,66 +448,51 @@ public final class LocaleServiceProviderPool {
}
/**
* Returns the provider's locale that is the most appropriate
* within the range
*
* @param start the given locale that is used as the starting one
* @param end the given locale that is used as the end one (exclusive),
* or null if it reaching any of the JRE supported locale should
* terminate the look up.
* @return the most specific locale within the range, or null
* if no provider locale found in that range.
* Returns a list of candidate locales for service look up.
* @param locale the input locale
* @return the list of candiate locales for the given locale
*/
private Locale findProviderLocale(Locale start, Locale end) {
Set<Locale> provLoc = getProviderLocales();
Locale current = start;
while (current != null) {
if (end != null) {
if (current.equals(end)) {
current = null;
break;
}
} else {
if (isJRESupported(current)) {
current = null;
break;
}
}
if (provLoc.contains(current)) {
break;
}
current = getParentLocale(current);
}
return current;
private static List<Locale> getLookupLocales(Locale locale) {
// Note: We currently use the default implementation of
// ResourceBundle.Control.getCandidateLocales. The result
// returned by getCandidateLocales are already normalized
// (no extensions) for service look up.
List<Locale> lookupLocales = new Control(){}.getCandidateLocales("", locale);
return lookupLocales;
}
/**
* Returns the parent locale.
* Returns an instance of Locale used for service look up.
* The result Locale has no extensions except for ja_JP_JP
* and th_TH_TH
*
* @param locale the locale
* @return the parent locale
* @return the locale used for service look up
*/
private static Locale getParentLocale(Locale locale) {
String variant = locale.getVariant();
if (variant != "") {
int underscoreIndex = variant.lastIndexOf('_');
if (underscoreIndex != (-1)) {
return new Locale(locale.getLanguage(), locale.getCountry(),
variant.substring(0, underscoreIndex));
} else {
return new Locale(locale.getLanguage(), locale.getCountry());
private static Locale getLookupLocale(Locale locale) {
Locale lookupLocale = locale;
Set<Character> extensions = locale.getExtensionKeys();
if (!extensions.isEmpty()
&& !locale.equals(locale_ja_JP_JP)
&& !locale.equals(locale_th_TH_TH)) {
// remove extensions
Builder locbld = new Builder();
try {
locbld.setLocale(locale);
locbld.clearExtensions();
lookupLocale = locbld.build();
} catch (IllformedLocaleException e) {
// A Locale with non-empty extensions
// should have well-formed fields except
// for ja_JP_JP and th_TH_TH. Therefore,
// it should never enter in this catch clause.
config("A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
// Fallback - script field will be lost.
lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
}
} else if (locale.getCountry() != "") {
return new Locale(locale.getLanguage());
} else if (locale.getLanguage() != "") {
return Locale.ROOT;
} else {
return null;
}
return lookupLocale;
}
/**
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public final class AsciiUtil {
public static boolean caseIgnoreMatch(String s1, String s2) {
if (s1 == s2) {
return true;
}
int len = s1.length();
if (len != s2.length()) {
return false;
}
int i = 0;
while (i < len) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2 && toLower(c1) != toLower(c2)) {
break;
}
i++;
}
return (i == len);
}
public static int caseIgnoreCompare(String s1, String s2) {
if (s1 == s2) {
return 0;
}
return AsciiUtil.toLowerString(s1).compareTo(AsciiUtil.toLowerString(s2));
}
public static char toUpper(char c) {
if (c >= 'a' && c <= 'z') {
c -= 0x20;
}
return c;
}
public static char toLower(char c) {
if (c >= 'A' && c <= 'Z') {
c += 0x20;
}
return c;
}
public static String toLowerString(String s) {
int idx = 0;
for (; idx < s.length(); idx++) {
char c = s.charAt(idx);
if (c >= 'A' && c <= 'Z') {
break;
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
for (; idx < s.length(); idx++) {
buf.append(toLower(s.charAt(idx)));
}
return buf.toString();
}
public static String toUpperString(String s) {
int idx = 0;
for (; idx < s.length(); idx++) {
char c = s.charAt(idx);
if (c >= 'a' && c <= 'z') {
break;
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
for (; idx < s.length(); idx++) {
buf.append(toUpper(s.charAt(idx)));
}
return buf.toString();
}
public static String toTitleString(String s) {
if (s.length() == 0) {
return s;
}
int idx = 0;
char c = s.charAt(idx);
if (!(c >= 'a' && c <= 'z')) {
for (idx = 1; idx < s.length(); idx++) {
if (c >= 'A' && c <= 'Z') {
break;
}
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
if (idx == 0) {
buf.append(toUpper(s.charAt(idx)));
idx++;
}
for (; idx < s.length(); idx++) {
buf.append(toLower(s.charAt(idx)));
}
return buf.toString();
}
public static boolean isAlpha(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
public static boolean isAlphaString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isAlpha(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static boolean isNumeric(char c) {
return (c >= '0' && c <= '9');
}
public static boolean isNumericString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isNumeric(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static boolean isAlphaNumeric(char c) {
return isAlpha(c) || isNumeric(c);
}
public static boolean isAlphaNumericString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isAlphaNumeric(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static class CaseInsensitiveKey {
private String _key;
private int _hash;
public CaseInsensitiveKey(String key) {
_key = key;
_hash = AsciiUtil.toLowerString(key).hashCode();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof CaseInsensitiveKey) {
return AsciiUtil.caseIgnoreMatch(_key, ((CaseInsensitiveKey)o)._key);
}
return false;
}
public int hashCode() {
return _hash;
}
}
}
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public final class BaseLocale {
public static final String SEP = "_";
private static final Cache CACHE = new Cache();
public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", "");
private String _language = "";
private String _script = "";
private String _region = "";
private String _variant = "";
private transient volatile int _hash = 0;
private BaseLocale(String language, String script, String region, String variant) {
if (language != null) {
_language = AsciiUtil.toLowerString(language).intern();
}
if (script != null) {
_script = AsciiUtil.toTitleString(script).intern();
}
if (region != null) {
_region = AsciiUtil.toUpperString(region).intern();
}
if (variant != null) {
_variant = variant.intern();
}
}
public static BaseLocale getInstance(String language, String script, String region, String variant) {
// JDK uses deprecated ISO639.1 language codes for he, yi and id
if (AsciiUtil.caseIgnoreMatch(language, "he")) {
language = "iw";
} else if (AsciiUtil.caseIgnoreMatch(language, "yi")) {
language = "ji";
} else if (AsciiUtil.caseIgnoreMatch(language, "id")) {
language = "in";
}
Key key = new Key(language, script, region, variant);
BaseLocale baseLocale = CACHE.get(key);
return baseLocale;
}
public String getLanguage() {
return _language;
}
public String getScript() {
return _script;
}
public String getRegion() {
return _region;
}
public String getVariant() {
return _variant;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BaseLocale)) {
return false;
}
BaseLocale other = (BaseLocale)obj;
return hashCode() == other.hashCode()
&& _language.equals(other._language)
&& _script.equals(other._script)
&& _region.equals(other._region)
&& _variant.equals(other._variant);
}
public String toString() {
StringBuilder buf = new StringBuilder();
if (_language.length() > 0) {
buf.append("language=");
buf.append(_language);
}
if (_script.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("script=");
buf.append(_script);
}
if (_region.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("region=");
buf.append(_region);
}
if (_variant.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("variant=");
buf.append(_variant);
}
return buf.toString();
}
public int hashCode() {
int h = _hash;
if (h == 0) {
// Generating a hash value from language, script, region and variant
for (int i = 0; i < _language.length(); i++) {
h = 31*h + _language.charAt(i);
}
for (int i = 0; i < _script.length(); i++) {
h = 31*h + _script.charAt(i);
}
for (int i = 0; i < _region.length(); i++) {
h = 31*h + _region.charAt(i);
}
for (int i = 0; i < _variant.length(); i++) {
h = 31*h + _variant.charAt(i);
}
_hash = h;
}
return h;
}
private static class Key implements Comparable<Key> {
private String _lang = "";
private String _scrt = "";
private String _regn = "";
private String _vart = "";
private volatile int _hash; // Default to 0
public Key(String language, String script, String region, String variant) {
if (language != null) {
_lang = language;
}
if (script != null) {
_scrt = script;
}
if (region != null) {
_regn = region;
}
if (variant != null) {
_vart = variant;
}
}
public boolean equals(Object obj) {
return (this == obj) ||
(obj instanceof Key)
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._lang, this._lang)
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._scrt, this._scrt)
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._regn, this._regn)
&& ((Key)obj)._vart.equals(_vart); // variant is case sensitive in JDK!
}
public int compareTo(Key other) {
int res = AsciiUtil.caseIgnoreCompare(this._lang, other._lang);
if (res == 0) {
res = AsciiUtil.caseIgnoreCompare(this._scrt, other._scrt);
if (res == 0) {
res = AsciiUtil.caseIgnoreCompare(this._regn, other._regn);
if (res == 0) {
res = this._vart.compareTo(other._vart);
}
}
}
return res;
}
public int hashCode() {
int h = _hash;
if (h == 0) {
// Generating a hash value from language, script, region and variant
for (int i = 0; i < _lang.length(); i++) {
h = 31*h + AsciiUtil.toLower(_lang.charAt(i));
}
for (int i = 0; i < _scrt.length(); i++) {
h = 31*h + AsciiUtil.toLower(_scrt.charAt(i));
}
for (int i = 0; i < _regn.length(); i++) {
h = 31*h + AsciiUtil.toLower(_regn.charAt(i));
}
for (int i = 0; i < _vart.length(); i++) {
h = 31*h + _vart.charAt(i);
}
_hash = h;
}
return h;
}
public static Key normalize(Key key) {
String lang = AsciiUtil.toLowerString(key._lang).intern();
String scrt = AsciiUtil.toTitleString(key._scrt).intern();
String regn = AsciiUtil.toUpperString(key._regn).intern();
String vart = key._vart.intern(); // preserve upper/lower cases
return new Key(lang, scrt, regn, vart);
}
}
private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
public Cache() {
}
protected Key normalizeKey(Key key) {
return Key.normalize(key);
}
protected BaseLocale createObject(Key key) {
return new BaseLocale(key._lang, key._scrt, key._regn, key._vart);
}
}
}
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public class Extension {
private char _key;
protected String _value;
protected Extension(char key) {
_key = key;
}
Extension(char key, String value) {
_key = key;
_value = value;
}
public char getKey() {
return _key;
}
public String getValue() {
return _value;
}
public String getID() {
return _key + LanguageTag.SEP + _value;
}
public String toString() {
return getID();
}
}
此差异已折叠。
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveChar;
import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveString;
public class LocaleExtensions {
private SortedMap<Character, Extension> _map;
private String _id;
private static final SortedMap<Character, Extension> EMPTY_MAP =
Collections.unmodifiableSortedMap(new TreeMap<Character, Extension>());
public static final LocaleExtensions EMPTY_EXTENSIONS;
public static final LocaleExtensions CALENDAR_JAPANESE;
public static final LocaleExtensions NUMBER_THAI;
static {
EMPTY_EXTENSIONS = new LocaleExtensions();
EMPTY_EXTENSIONS._id = "";
EMPTY_EXTENSIONS._map = EMPTY_MAP;
CALENDAR_JAPANESE = new LocaleExtensions();
CALENDAR_JAPANESE._id = "u-ca-japanese";
CALENDAR_JAPANESE._map = new TreeMap<Character, Extension>();
CALENDAR_JAPANESE._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.CA_JAPANESE);
NUMBER_THAI = new LocaleExtensions();
NUMBER_THAI._id = "u-nu-thai";
NUMBER_THAI._map = new TreeMap<Character, Extension>();
NUMBER_THAI._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.NU_THAI);
}
private LocaleExtensions() {
}
/*
* Package local constructor, only used by InternalLocaleBuilder.
*/
LocaleExtensions(Map<CaseInsensitiveChar, String> extensions,
Set<CaseInsensitiveString> uattributes, Map<CaseInsensitiveString, String> ukeywords) {
boolean hasExtension = (extensions != null && extensions.size() > 0);
boolean hasUAttributes = (uattributes != null && uattributes.size() > 0);
boolean hasUKeywords = (ukeywords != null && ukeywords.size() > 0);
if (!hasExtension && !hasUAttributes && !hasUKeywords) {
_map = EMPTY_MAP;
_id = "";
return;
}
// Build extension map
_map = new TreeMap<Character, Extension>();
if (hasExtension) {
for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) {
char key = AsciiUtil.toLower(ext.getKey().value());
String value = ext.getValue();
if (LanguageTag.isPrivateusePrefixChar(key)) {
// we need to exclude special variant in privuateuse, e.g. "x-abc-lvariant-DEF"
value = InternalLocaleBuilder.removePrivateuseVariant(value);
if (value == null) {
continue;
}
}
Extension e = new Extension(key, AsciiUtil.toLowerString(value));
_map.put(Character.valueOf(key), e);
}
}
if (hasUAttributes || hasUKeywords) {
TreeSet<String> uaset = null;
TreeMap<String, String> ukmap = null;
if (hasUAttributes) {
uaset = new TreeSet<String>();
for (CaseInsensitiveString cis : uattributes) {
uaset.add(AsciiUtil.toLowerString(cis.value()));
}
}
if (hasUKeywords) {
ukmap = new TreeMap<String, String>();
for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) {
String key = AsciiUtil.toLowerString(kwd.getKey().value());
String type = AsciiUtil.toLowerString(kwd.getValue());
ukmap.put(key, type);
}
}
UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap);
_map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), ule);
}
if (_map.size() == 0) {
// this could happen when only privuateuse with special variant
_map = EMPTY_MAP;
_id = "";
} else {
_id = toID(_map);
}
}
public Set<Character> getKeys() {
return Collections.unmodifiableSet(_map.keySet());
}
public Extension getExtension(Character key) {
return _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
}
public String getExtensionValue(Character key) {
Extension ext = _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
if (ext == null) {
return null;
}
return ext.getValue();
}
public Set<String> getUnicodeLocaleAttributes() {
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
if (ext == null) {
return Collections.emptySet();
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes();
}
public Set<String> getUnicodeLocaleKeys() {
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
if (ext == null) {
return Collections.emptySet();
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys();
}
public String getUnicodeLocaleType(String unicodeLocaleKey) {
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
if (ext == null) {
return null;
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(AsciiUtil.toLowerString(unicodeLocaleKey));
}
public boolean isEmpty() {
return _map.isEmpty();
}
public static boolean isValidKey(char c) {
return LanguageTag.isExtensionSingletonChar(c) || LanguageTag.isPrivateusePrefixChar(c);
}
public static boolean isValidUnicodeLocaleKey(String ukey) {
return UnicodeLocaleExtension.isKey(ukey);
}
private static String toID(SortedMap<Character, Extension> map) {
StringBuilder buf = new StringBuilder();
Extension privuse = null;
for (Entry<Character, Extension> entry : map.entrySet()) {
char singleton = entry.getKey().charValue();
Extension extension = entry.getValue();
if (LanguageTag.isPrivateusePrefixChar(singleton)) {
privuse = extension;
} else {
if (buf.length() > 0) {
buf.append(LanguageTag.SEP);
}
buf.append(extension);
}
}
if (privuse != null) {
if (buf.length() > 0) {
buf.append(LanguageTag.SEP);
}
buf.append(privuse);
}
return buf.toString();
}
public String toString() {
return _id;
}
public String getID() {
return _id;
}
public int hashCode() {
return _id.hashCode();
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof LocaleExtensions)) {
return false;
}
return this._id.equals(((LocaleExtensions)other)._id);
}
}
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*
* 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.
*/
/*
*******************************************************************************
* Copyright (C) 2009-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
public abstract class LocaleObjectCache<K, V> {
private ConcurrentHashMap<K, CacheEntry<K, V>> _map;
private ReferenceQueue<V> _queue = new ReferenceQueue<V>();
public LocaleObjectCache() {
this(16, 0.75f, 16);
}
public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) {
_map = new ConcurrentHashMap<K, CacheEntry<K, V>>(initialCapacity, loadFactor, concurrencyLevel);
}
public V get(K key) {
V value = null;
cleanStaleEntries();
CacheEntry<K, V> entry = _map.get(key);
if (entry != null) {
value = entry.get();
}
if (value == null) {
key = normalizeKey(key);
V newVal = createObject(key);
if (key == null || newVal == null) {
// subclass must return non-null key/value object
return null;
}
CacheEntry<K, V> newEntry = new CacheEntry<K, V>(key, newVal, _queue);
while (value == null) {
cleanStaleEntries();
entry = _map.putIfAbsent(key, newEntry);
if (entry == null) {
value = newVal;
break;
} else {
value = entry.get();
}
}
}
return value;
}
@SuppressWarnings("unchecked")
private void cleanStaleEntries() {
CacheEntry<K, V> entry;
while ((entry = (CacheEntry<K, V>)_queue.poll()) != null) {
_map.remove(entry.getKey());
}
}
protected abstract V createObject(K key);
protected K normalizeKey(K key) {
return key;
}
private static class CacheEntry<K, V> extends SoftReference<V> {
private K _key;
CacheEntry(K key, V value, ReferenceQueue<V> queue) {
super(value, queue);
_key = key;
}
K getKey() {
return _key;
}
}
}
此差异已折叠。
/*
* Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -177,6 +177,11 @@ public class LocaleData {
for (Iterator<Locale> l = candidates.iterator(); l.hasNext(); ) {
String lstr = l.next().toString();
/* truncate extra segment introduced by Java 7 for script and extesions */
int idx = lstr.indexOf("_#");
if (idx >= 0) {
lstr = lstr.substring(0, idx);
}
/* Every locale string in the locale string list returned from
the above getSupportedLocaleString is enclosed
within two white spaces so that we could check some locale
......
......@@ -227,6 +227,12 @@ za=\u85cf\u6587
zh=\u4e2d\u6587
zu=\u7956\u9c81\u6587
# script names
# key is ISO 15924 script code
Hans=\u7b80\u4f53\u4e2d\u6587
Hant=\u7e41\u4f53\u4e2d\u6587
# country names
# key is ISO 3166 country code
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册