This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 3df7f4440 [LANG-1823] LocaleUtils.toLocale cannot parse valid JDK 
Locale string containing '#'  (#1630)
3df7f4440 is described below

commit 3df7f4440e7447bc55e95400ec511323e708c652
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Apr 30 08:18:58 2026 -0400

    [LANG-1823] LocaleUtils.toLocale cannot parse valid JDK Locale string 
containing '#'  (#1630)
    
    * Add testLang1641()
    
    * Rename some test methods
    
    * [LANG-1823] LocaleUtils.toLocale cannot parse valid JDK Locale string
    containing '#' (e.g., from Locale.getAvailableLocales())
    
    -[LANG-879]
    LocaleUtils test fails with new Locale "ja_JP_JP_#u-ca-japanese" of JDK7
    - See 
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor
---
 .../java/org/apache/commons/lang3/LocaleUtils.java | 13 +++++++---
 .../org/apache/commons/lang3/LocaleUtilsTest.java  | 29 ++++++++++++++++++----
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java 
b/src/main/java/org/apache/commons/lang3/LocaleUtils.java
index 2bcda1ade..ea075cb5b 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 <a 
href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor";>Locale
 special cases</a>
      */
     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 <a 
href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Locale.html#special_cases_constructor";>Locale
 special cases</a>
      */
     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 101d02467..9fcbabcf7 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
      */

Reply via email to