diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java index 2bcda1ade85..ea075cb5bfe 100644 --- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java +++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java @@ -326,6 +326,7 @@ static Locale ofCountry(final String country) { * @return a Locale parsed from the given String. * @throws IllegalArgumentException if the given String cannot be parsed. * @see Locale + * @see Locale special cases */ private static Locale parseLocale(final String str) { if (isISO639LanguageCode(str)) { @@ -343,6 +344,14 @@ private static Locale parseLocale(final String str) { } else if (segments.length == limit) { final String country = segments[1]; final String variant = segments[2]; + // Special case 1: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor + if (str.equals("th_TH_TH_#u-nu-thai")) { + return new Locale(language, country, "TH"); + } + // Special case 2: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor + if (str.equals("ja_JP_JP_#u-ca-japanese")) { + return new Locale(language, country, "JP"); + } if (isISO639LanguageCode(language) && (country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) && !variant.isEmpty()) { return new Locale(language, country, variant); } @@ -396,6 +405,7 @@ public static Locale toLocale(final Locale locale) { * @throws IllegalArgumentException if the string is an invalid format. * @see Locale#forLanguageTag(String) * @see Locale#getISOCountries() + * @see Locale special cases */ public static Locale toLocale(final String str) { if (str == null) { @@ -405,9 +415,6 @@ public static Locale toLocale(final String str) { if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank return new Locale(StringUtils.EMPTY, StringUtils.EMPTY); } - if (str.contains("#")) { // LANG-879 - Cannot handle Java 7 script & extensions - throw new IllegalArgumentException("Invalid locale format: " + str); - } final int len = str.length(); if (len < 2) { throw new IllegalArgumentException("Invalid locale format: " + str); diff --git a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java index 101d0246773..9fcbabcf72a 100644 --- a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java @@ -288,6 +288,14 @@ void testIsLanguageUndetermined() { assertTrue(LocaleUtils.isLanguageUndetermined(null)); } + /** + * Tests #LANG-1823 + */ + @Test + void testLang1823() { + assertValidToLocale("th_TH_#Thai", "th", "TH", "#Thai"); + } + /** * Tests #LANG-328 - only language+variant */ @@ -417,23 +425,34 @@ void testParseAllLocales(final Locale actualLocale) { // Check if it's possible to recreate the Locale using just the standard constructor final Locale locale = new Locale(actualLocale.getLanguage(), actualLocale.getCountry(), actualLocale.getVariant()); if (actualLocale.equals(locale)) { // it is possible for LocaleUtils.toLocale to handle these Locales + assertEquals(actualLocale, LocaleUtils.toLocale(actualLocale.toString())); final String str = actualLocale.toString(); // Look for the script/extension suffix int suff = str.indexOf("_#"); - if (suff == - 1) { + if (suff == -1) { suff = str.indexOf("#"); } String localeStr = str; if (suff >= 0) { // we have a suffix - assertIllegalArgumentException(() -> LocaleUtils.toLocale(str)); - // try without suffix localeStr = str.substring(0, suff); } - final Locale loc = LocaleUtils.toLocale(localeStr); - assertEquals(actualLocale, loc); + assertEquals(actualLocale, LocaleUtils.toLocale(localeStr)); } } + /** + * Special cases from https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor + */ + @Test + void testSpecialCases() { + assertValidToLocale("th_TH_TH", "th", "TH", "TH"); + assertValidToLocale("ja_JP_JP", "ja", "JP", "JP"); + // "th_TH_TH_#u-nu-thai" and friends + LocaleUtils.localeLookupList(new Locale("th", "TH", "TH")).forEach(locale -> assertEquals(locale, LocaleUtils.toLocale(locale.toString()))); + // "ja_JP_JP_#u-ca-japanese" and friends + LocaleUtils.localeLookupList(new Locale("ja", "JP", "JP")).forEach(locale -> assertEquals(locale, LocaleUtils.toLocale(locale.toString()))); + } + /** * Test for 3-chars locale, further details at LANG-915 */