-
CSR
-
Resolution: Approved
-
P4
-
None
-
behavioral
-
medium
-
Applications that extract the language code from the `Locale` object and use it will see a behavioral change. To mitigate the situation, a new system property is provided.
-
Java API, System or security property
-
SE
Summary
Change the mapping of the obsolete ISO 639 code mapping in Locale
class to the current code.
Problem
Historically, constructors in java.util.Locale
class map three ISO 639 language codes, namely "he", "ji", and "id" to their obsolete codes; "iw", "yi", and "in" for backward compatibility. Although this solution works well to accept both obsolete and current ISO 639 codes, constructed Locale
object represents the obsolete language code (i.e. Locale.getLanguage()
and Locale.toString()
returns obsolete language codes), which looks as if the current language codes were not supported.
Solution
Flip the mapping from current->obsolete to obsolete->current. For example, mapping for Hebrew changes from "he" -> "iw" to "iw"->"he". To provide the backward compatible behavior, a new system property java.locale.useOldISOCodes
will be introduced. If the value of the system property is true
, then the Locale class behaves in a backward-compatible manner. java.util.ResourceBundle.Control#newBundle()
is also modified to load both obsolete and current bundle name resource if needed, honoring the requested name as a priority.
Specification
Change the part of the class description of java.util.Locale
as follows:
*
* <p>During deserialization, readResolve adds extensions as described
* 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
Change the method description of each constructor in Locale
class as follows:
/**
* 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>
Change the method description of Locale#getLanguage()
as follows:
/**
* Returns the language code of this Locale.
*
- * <p><b>Note:</b> 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")) // 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
*/
Change the method description of Locale#forLanguageTag()
as follows:
*
* <p>The following <b>conversions</b> are performed:<ul>
*
* <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
* result locale (without case normalization). If it is then
* empty, the private use subtag is discarded:
Add the following list item in the method description of java.util.ResourceBundle.Control#newBundle()
as follows:
*
* <li>If {@code format} is neither {@code "java.class"}
* 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
* the base bundle name of the resource bundle, a fully
* qualified class name
- csr of
-
JDK-8263202 Update Hebrew/Indonesian/Yiddish ISO 639 language codes to current
- Resolved
- relates to
-
JDK-8269513 Clarify the spec wrt `useOldISOCodes` system property
- Resolved