8263202: Update Hebrew/Indonesian/Yiddish ISO 639 language codes to current

Reviewed-by: joehw
This commit is contained in:
Naoto Sato 2021-05-26 15:52:39 +00:00
parent 9c346a1ec7
commit a4c46e1e4f
35 changed files with 345 additions and 146 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2021, 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
@ -444,18 +444,26 @@ import sun.util.locale.provider.TimeZoneNameUtility;
* in <a href="#special_cases_constructor">Special Cases</a>, only
* for the two cases th_TH_TH and ja_JP_JP.
*
* <h4>Legacy language codes</h4>
* <h4><a id="legacy_language_codes">Legacy language codes</a></h4>
*
* <p>Locale's constructor has always converted three language codes to
* their earlier, obsoleted forms: {@code he} maps to {@code iw},
* {@code yi} maps to {@code ji}, and {@code id} maps to
* {@code in}. This continues to be the case, in order to not break
* backwards compatibility.
* {@code in}. Since Java SE 17, this is no longer the case. Each
* language maps to its new form; {@code iw} maps to {@code he}, {@code ji}
* maps to {@code yi}, and {@code in} maps to {@code id}.
*
* <p>For the backward compatible behavior, the system property
* {@systemProperty java.locale.useOldISOCodes} reverts the behavior
* back to prior to Java SE 17 one. If the system property is set
* to {@code true}, those three current language codes are mapped to their
* backward compatible forms.
*
* <p>The APIs added in 1.7 map between the old and new language codes,
* maintaining the old codes internal to Locale (so that
* {@code getLanguage} and {@code toString} reflect the old
* code), but using the new codes in the BCP 47 language tag APIs (so
* maintaining the mapped codes internal to Locale (so that
* {@code getLanguage} and {@code toString} reflect the mapped
* code, which depends on the {@code java.locale.useOldISOCodes} system
* property), but using the new codes in the BCP 47 language tag APIs (so
* that {@code toLanguageTag} reflects the new one). This
* preserves the equivalence between Locales no matter which code or
* API is used to construct them. Java's default resource bundle
@ -720,13 +728,11 @@ public final class Locale implements Cloneable, Serializable {
* Construct a locale from language, country and variant.
* This constructor normalizes the language value to lowercase and
* the country value to uppercase.
* <p>
* <b>Note:</b>
* @implNote
* <ul>
* <li>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.
* <li>Obsolete ISO 639 codes ("iw", "ji", and "in") are mapped to
* their current forms. See <a href="#legacy_language_codes">Legacy language
* codes</a> for more information.
* <li>For backward compatibility reasons, this constructor does not make
* any syntactic checks on the input.
* <li>The two cases ("ja", "JP", "JP") and ("th", "TH", "TH") are handled specially,
@ -754,13 +760,11 @@ public final class Locale implements Cloneable, Serializable {
* Construct a locale from language and country.
* This constructor normalizes the language value to lowercase and
* the country value to uppercase.
* <p>
* <b>Note:</b>
* @implNote
* <ul>
* <li>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.
* <li>Obsolete ISO 639 codes ("iw", "ji", and "in") are mapped to
* their current forms. See <a href="#legacy_language_codes">Legacy language
* codes</a> for more information.
* <li>For backward compatibility reasons, this constructor does not make
* any syntactic checks on the input.
* </ul>
@ -779,13 +783,11 @@ public final class Locale implements Cloneable, Serializable {
/**
* Construct a locale from a language code.
* This constructor normalizes the language value to lowercase.
* <p>
* <b>Note:</b>
* @implNote
* <ul>
* <li>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.
* <li>Obsolete ISO 639 codes ("iw", "ji", and "in") are mapped to
* their current forms. See <a href="#legacy_language_codes">Legacy language
* codes</a> for more information.
* <li>For backward compatibility reasons, this constructor does not make
* any syntactic checks on the input.
* </ul>
@ -1207,19 +1209,10 @@ public final class Locale implements Cloneable, Serializable {
/**
* Returns the language code of this Locale.
*
* <p><b>Note:</b> ISO 639 is not a stable standard&mdash; 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")) // BAD!
* ...
* </pre>
* Instead, do
* <pre>
* if (locale.getLanguage().equals(new Locale("he").getLanguage()))
* ...
* </pre>
* @implNote This method returns the new forms for the obsolete ISO 639
* codes ("iw", "ji", and "in"). See <a href="#legacy_language_codes">
* Legacy language codes</a> for more information.
*
* @return The language code, or the empty string if none is defined.
* @see #getDisplayLanguage
*/
@ -1607,9 +1600,11 @@ public final class Locale implements Cloneable, Serializable {
*
* <li>The language code "und" is mapped to language "".
*
* <li>The language codes "he", "yi", and "id" are mapped to "iw",
* "ji", and "in" respectively. (This is the same canonicalization
* that's done in Locale's constructors.)
* <li>The language codes "iw", "ji", and "in" are mapped to "he",
* "yi", and "id" respectively. (This is the same canonicalization
* that's done in Locale's constructors.) See
* <a href="#legacy_language_codes">Legacy language codes</a>
* for more information.
*
* <li>The portion of a private use subtag prefixed by "lvariant",
* if any, is removed and appended to the variant field in the
@ -2396,17 +2391,9 @@ public final class Locale implements Cloneable, Serializable {
private static 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 = LocaleUtils.toLowerString(language).intern();
if (language == "he") {
return "iw";
} else if (language == "yi") {
return "ji";
} else if (language == "id") {
return "in";
} else {
return language;
}
// codes have changed, but we always store the NEW code, unless the property
// java.locale.useOldISOCodes is set to "true"
return BaseLocale.convertOldISOCodes(LocaleUtils.toLowerString(language).intern());
}
private static LocaleExtensions getCompatibilityExtensions(String language,

View file

@ -72,6 +72,8 @@ import jdk.internal.reflect.Reflection;
import sun.security.action.GetPropertyAction;
import sun.util.locale.BaseLocale;
import sun.util.locale.LocaleObjectCache;
import sun.util.resources.Bundles;
import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
@ -3098,6 +3100,12 @@ public abstract class ResourceBundle {
* nor {@code "java.properties"}, an
* {@code IllegalArgumentException} is thrown.</li>
*
* <li>If the {@code locale}'s language is one of the
* <a href="./Locale.html#legacy_language_codes">Legacy language
* codes</a>, either old or new, then repeat the loading process
* if needed, with the bundle name with the other language.
* For example, "iw" for "he" and vice versa.
*
* </ul>
*
* @param baseName
@ -3152,6 +3160,21 @@ public abstract class ResourceBundle {
* that is visible to the given loader and accessible to the given caller.
*/
String bundleName = toBundleName(baseName, locale);
var bundle = newBundle0(bundleName, format, loader, reload);
if (bundle == null) {
// Try loading legacy ISO language's other bundles
var otherBundleName = Bundles.toOtherBundleName(baseName, bundleName, locale);
if (!bundleName.equals(otherBundleName)) {
bundle = newBundle0(otherBundleName, format, loader, reload);
}
}
return bundle;
}
private ResourceBundle newBundle0(String bundleName, String format,
ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle bundle = null;
if (format.equals("java.class")) {
try {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, 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
@ -27,6 +27,7 @@ package java.util.spi;
import jdk.internal.access.JavaUtilResourceBundleAccess;
import jdk.internal.access.SharedSecrets;
import sun.util.resources.Bundles;
import java.io.IOException;
import java.io.InputStream;
@ -187,6 +188,17 @@ public abstract class AbstractResourceBundleProvider implements ResourceBundlePr
public ResourceBundle getBundle(String baseName, Locale locale) {
Module module = this.getClass().getModule();
String bundleName = toBundleName(baseName, locale);
var bundle = getBundle0(module, bundleName);
if (bundle == null) {
var otherBundleName = Bundles.toOtherBundleName(baseName, bundleName, locale);
if (!bundleName.equals(otherBundleName)) {
bundle = getBundle0(module, Bundles.toOtherBundleName(baseName, bundleName, locale));
}
}
return bundle;
}
private ResourceBundle getBundle0(Module module, String bundleName) {
ResourceBundle bundle = null;
for (String format : formats) {

View file

@ -34,6 +34,7 @@ package sun.util.locale;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.Stable;
import sun.security.action.GetPropertyAction;
import java.lang.ref.SoftReference;
import java.util.StringJoiner;
@ -98,6 +99,13 @@ public final class BaseLocale {
private volatile int hash;
/**
* Boolean for the old ISO language code compatibility.
*/
private static final boolean OLD_ISO_CODES = GetPropertyAction.privilegedGetProperties()
.getProperty("java.locale.useOldISOCodes", "false")
.equalsIgnoreCase("true");
// This method must be called with normalize = false only when creating the
// Locale.* constants and non-normalized BaseLocale$Keys used for lookup.
private BaseLocale(String language, String script, String region, String variant,
@ -153,19 +161,22 @@ public final class BaseLocale {
// JDK uses deprecated ISO639.1 language codes for he, yi and id
if (!language.isEmpty()) {
if (language.equals("he")) {
language = "iw";
} else if (language.equals("yi")) {
language = "ji";
} else if (language.equals("id")) {
language = "in";
}
language = convertOldISOCodes(language);
}
Key key = new Key(language, script, region, variant, false);
return Cache.CACHE.get(key);
}
public static String convertOldISOCodes(String language) {
return switch (language) {
case "he", "iw" -> OLD_ISO_CODES ? "iw" : "he";
case "id", "in" -> OLD_ISO_CODES ? "in" : "id";
case "yi", "ji" -> OLD_ISO_CODES ? "ji" : "yi";
default -> language;
};
}
public String getLanguage() {
return language;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, 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
@ -208,6 +208,17 @@ public abstract class Bundles {
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
bundle = bundleAccess.newResourceBundle(bundleClass);
}
if (bundle == null) {
var otherBundleName = toOtherBundleName(baseName, bundleName, targetLocale);
if (!bundleName.equals(otherBundleName)) {
c = Class.forName(Bundles.class.getModule(), otherBundleName);
if (c != null && ResourceBundle.class.isAssignableFrom(c)) {
@SuppressWarnings("unchecked")
Class<ResourceBundle> bundleClass = (Class<ResourceBundle>) c;
bundle = bundleAccess.newResourceBundle(bundleClass);
}
}
}
} catch (Exception e) {
cacheKey.setCause(e);
}
@ -345,27 +356,55 @@ public abstract class Bundles {
return bundle;
}
/**
* Generates the other bundle name for languages that have changed,
* i.e. "he", "id", and "yi"
*
* @param baseName ResourceBundle base name
* @param bundleName ResourceBundle bundle name
* @param locale locale
* @return the other bundle name, or the same name for non-legacy ISO languages
*/
public static String toOtherBundleName(String baseName, String bundleName, Locale locale) {
var simpleName= baseName.substring(baseName.lastIndexOf('.') + 1);
var suffix = bundleName.substring(bundleName.lastIndexOf(simpleName) + simpleName.length());
var otherSuffix = switch(locale.getLanguage()) {
case "he" -> suffix.replaceFirst("^_he(_.*)?$", "_iw$1");
case "id" -> suffix.replaceFirst("^_id(_.*)?$", "_in$1");
case "yi" -> suffix.replaceFirst("^_yi(_.*)?$", "_ji$1");
case "iw" -> suffix.replaceFirst("^_iw(_.*)?$", "_he$1");
case "in" -> suffix.replaceFirst("^_in(_.*)?$", "_id$1");
case "ji" -> suffix.replaceFirst("^_ji(_.*)?$", "_yi$1");
default -> suffix;
};
if (suffix.equals(otherSuffix)) {
return bundleName;
} else {
return bundleName.substring(0, bundleName.lastIndexOf(suffix)) + otherSuffix;
}
}
/**
* The Strategy interface defines methods that are called by Bundles.of during
* the resource bundle loading process.
*/
public static interface Strategy {
public interface Strategy {
/**
* Returns a list of locales to be looked up for bundle loading.
*/
public List<Locale> getCandidateLocales(String baseName, Locale locale);
List<Locale> getCandidateLocales(String baseName, Locale locale);
/**
* Returns the bundle name for the given baseName and locale.
*/
public String toBundleName(String baseName, Locale locale);
String toBundleName(String baseName, Locale locale);
/**
* Returns the service provider type for the given baseName
* and locale, or null if no service providers should be used.
*/
public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
Locale locale);
}
@ -374,7 +413,7 @@ public abstract class Bundles {
* BundleReference.
*/
private static interface CacheKeyReference {
public CacheKey getCacheKey();
CacheKey getCacheKey();
}
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2021, 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
@ -216,6 +216,13 @@ public class LocaleData {
protected String toBundleName(String baseName, Locale locale) {
return LocaleDataStrategy.INSTANCE.toBundleName(baseName, locale);
}
/**
* Retrieves the other bundle name for legacy ISO 639 languages.
*/
protected String toOtherBundleName(String baseName, String bundleName, Locale locale) {
return Bundles.toOtherBundleName(baseName, bundleName, locale);
}
}
/**