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 57bd7dd09 Keep LocaleUtils.toLocale to ASCII language and country 
codes (#1838)
57bd7dd09 is described below

commit 57bd7dd094fd855f2ab3c969170933bbd7bafa2d
Author: alhuda <[email protected]>
AuthorDate: Sun Oct 25 19:10:47 2026 +0530

    Keep LocaleUtils.toLocale to ASCII language and country codes (#1838)
    
    * keep LocaleUtils.toLocale to ASCII language and country codes
    
    toLocale(String) documents strict validation, but its language, country
    and area-code checks used StringUtils.isAllLowerCase, isAllUpperCase,
    isNumeric and Character.isUpperCase, which accept any Unicode letter or
    decimal digit. java.util.Locale defines these codes as [a-zA-Z]{2,8},
    [a-zA-Z]{2} and [0-9]{3}, so fullwidth letters, Latin-1 letters and
    Arabic-Indic digits were turned into an ill-formed Locale instead of
    an IllegalArgumentException.
    
    Check those characters against the ASCII ranges. Variants are unchanged.
    
    * Use a parameterized test for LocaleUtils non-ASCII codes
---
 src/main/java/org/apache/commons/lang3/LocaleUtils.java   |  8 ++++----
 .../java/org/apache/commons/lang3/LocaleUtilsTest.java    | 15 +++++++++++++++
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java 
b/src/main/java/org/apache/commons/lang3/LocaleUtils.java
index fd8854b71..cbc721c09 100644
--- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java
+++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java
@@ -214,7 +214,7 @@ public static boolean isAvailableLocale(final Locale 
locale) {
      * @return true if the given String is a <a 
href="https://www.iso.org/iso-3166-country-codes.html";>ISO 3166</a> compliant 
country code.
      */
     private static boolean isISO3166CountryCode(final String str) {
-        return StringUtils.isAllUpperCase(str) && isAlpha2Len(str);
+        return isAlpha2Len(str) && str.chars().allMatch(ch -> 
CharUtils.isAsciiAlphaUpper((char) ch));
     }
 
     /**
@@ -224,7 +224,7 @@ private static boolean isISO3166CountryCode(final String 
str) {
      * @return true, if the given String is a <a 
href="https://www.iso.org/iso-639-language-code";>ISO 639</a> compliant language 
code.
      */
     private static boolean isISO639LanguageCode(final String str) {
-        return StringUtils.isAllLowerCase(str) && (isAlpha2Len(str) || 
isAlpha3Len(str));
+        return (isAlpha2Len(str) || isAlpha3Len(str)) && 
str.chars().allMatch(ch -> CharUtils.isAsciiAlphaLower((char) ch));
     }
 
     /**
@@ -250,7 +250,7 @@ public static boolean isLanguageUndetermined(final Locale 
locale) {
      * @return true if the given String is a UN M.49 numeric area code.
      */
     private static boolean isNumericAreaCode(final String str) {
-        return StringUtils.isNumeric(str) && isAlpha3Len(str);
+        return isAlpha3Len(str) && str.chars().allMatch(ch -> 
CharUtils.isAsciiNumeric((char) ch));
     }
 
     /**
@@ -446,7 +446,7 @@ public static Locale toLocale(final String str) {
             }
             final char ch1 = str.charAt(1);
             final char ch2 = str.charAt(2);
-            if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) {
+            if (!CharUtils.isAsciiAlphaUpper(ch1) || 
!CharUtils.isAsciiAlphaUpper(ch2)) {
                 throw new IllegalArgumentException("Invalid locale format: " + 
str);
             }
             if (len == 3) {
diff --git a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
index 38ea40ab7..cb665f882 100644
--- a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
@@ -540,6 +540,21 @@ void testToLocale_2Part() {
         assertIllegalArgumentException(() -> LocaleUtils.toLocale("us_E3"), 
"Should fail second part not uppercase");
     }
 
+    /**
+     * Tests that language, country and area codes are restricted to ASCII, as 
{@link Locale} defines them.
+     */
+    @ParameterizedTest
+    @ValueSource(strings = {
+            // FULLWIDTH LATIN SMALL LETTER E and N
+            "en", "éé", "éé_GB",
+            // FULLWIDTH LATIN CAPITAL LETTER G and B
+            "en_GB", "en_ÉÉ", "en_ÉÉ_POSIX", "_ÉÉ", "_ÉÉ_POSIX",
+            // ARABIC-INDIC DIGIT ONE, TWO and THREE
+            "en_١٢٣" })
+    void testToLocaleNonAsciiCodes(final String localeString) {
+        assertIllegalArgumentException(() -> 
LocaleUtils.toLocale(localeString), "Should fail code not ASCII: " + 
localeString);
+    }
+
     /**
      * Test toLocale() method.
      */

Reply via email to