Commit fb7960ed35 for woocommerce

commit fb7960ed355318d669edbd38c4562e1145387dff
Author: Hannah Tinkler <hannah.tinkler@gmail.com>
Date:   Wed Feb 25 11:07:11 2026 +0000

    Update locale validation to reflect that there are valid 2 and 3 letter locales. (#63411)

    * Update locale validation to reflect that there are valid 2 and 3 letter locales.

diff --git a/plugins/woocommerce/src/Internal/PushNotifications/Validators/PushTokenValidator.php b/plugins/woocommerce/src/Internal/PushNotifications/Validators/PushTokenValidator.php
index 1bee511fa0..c875457f81 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/Validators/PushTokenValidator.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/Validators/PushTokenValidator.php
@@ -41,10 +41,15 @@ class PushTokenValidator {
 	/**
 	 * Validates device locale format:
 	 * - language code (2–3 lowercase letters)
-	 * - underscore
-	 * - region code (2 uppercase letters).
+	 * - optionally followed by underscore and region code (2 uppercase letters)
+	 *
+	 * We don't have access to a locale dictionary to check valid locales, but
+	 * it's important to note that we do not support country codes here, ONLY
+	 * valid locales. Some languages have valid 2 and 3 character locales (like
+	 * Japanese - ja, Arabic - ar, Asturian - ast), which is what this regex
+	 * reflects.
 	 */
-	const DEVICE_LOCALE_FORMAT = '/^(?<language>[a-z]{2,3})_(?<region>[A-Z]{2})$/';
+	const DEVICE_LOCALE_FORMAT = '/^(?<language>[a-z]{2,3})(?:_(?<region>[A-Z]{2}))?$/';

 	/**
 	 * The regex to use when validating device UUID format.
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Validators/PushTokenValidatorTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Validators/PushTokenValidatorTest.php
index 07f3917cca..0c51520f50 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Validators/PushTokenValidatorTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Validators/PushTokenValidatorTest.php
@@ -907,11 +907,13 @@ class PushTokenValidatorTest extends WC_Unit_Test_Case {
 	 */
 	public function valid_locales_provider(): array {
 		return array(
-			'English US'   => array( 'en_US' ),
-			'French'       => array( 'fr_FR' ),
-			'Chinese'      => array( 'zh_CN' ),
-			'Portuguese'   => array( 'pt_BR' ),
-			'Three-letter' => array( 'ast_ES' ),
+			'English US'                  => array( 'en_US' ),
+			'French'                      => array( 'fr_FR' ),
+			'Chinese'                     => array( 'zh_CN' ),
+			'Portuguese'                  => array( 'pt_BR' ),
+			'Three-letter with region'    => array( 'rup_MK' ),
+			'Two-letter without region'   => array( 'ja' ),
+			'Three-letter without region' => array( 'ast' ),
 		);
 	}

@@ -922,12 +924,13 @@ class PushTokenValidatorTest extends WC_Unit_Test_Case {
 	 */
 	public function invalid_locales_provider(): array {
 		return array(
-			'no underscore'    => array( 'enUS' ),
-			'lowercase region' => array( 'en_gb' ),
-			'uppercase lang'   => array( 'EN_US' ),
-			'just language'    => array( 'en' ),
-			'with hyphen'      => array( 'en-US' ),
-			'too long lang'    => array( 'engl_US' ),
+			'no underscore'       => array( 'enUS' ),
+			'lowercase region'    => array( 'en_gb' ),
+			'uppercase lang'      => array( 'EN_US' ),
+			'single char'         => array( 'e' ),
+			'with hyphen'         => array( 'en-US' ),
+			'too long lang'       => array( 'engl_US' ),
+			'trailing underscore' => array( 'en_' ),
 		);
 	}