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
*/