Commit 778ed86b2d for wordpress.org

commit 778ed86b2d9928fa98ac72486b62293ce81f6ccc
Author: jonsurrell <jonsurrell@git.wordpress.org>
Date:   Tue Oct 6 09:32:52 2026 +0000

    I18N: Ignore non-string values in the locale getters.

    `get_locale()`, `get_user_locale()` and `determine_locale()` read untyped values from options, user meta, globals, filters and request parameters. A non-string value can cause a `TypeError` during just-in-time translation. The getters now ignore non-string and empty values and use their existing fallbacks.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/13335

    Props jonsurrell, westonruter, josephscott.
    Fixes #66106.

    Built from https://develop.svn.wordpress.org/trunk@64122


    git-svn-id: http://core.svn.wordpress.org/trunk@63278 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php
index c39beaf7b1..480ca7fb5e 100644
--- a/wp-includes/formatting.php
+++ b/wp-includes/formatting.php
@@ -2498,6 +2498,11 @@ function sanitize_html_class( $classname, $fallback = '' ) {
  * @return string The sanitized value.
  */
 function sanitize_locale_name( $locale_name ) {
+	// Request values can arrive as arrays, and preg_replace() would map over them.
+	if ( ! is_string( $locale_name ) ) {
+		return '';
+	}
+
 	// Limit to A-Z, a-z, 0-9, '_', '-'.
 	$sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $locale_name );

diff --git a/wp-includes/l10n.php b/wp-includes/l10n.php
index 7b9d2652d4..f83d1ce61f 100644
--- a/wp-includes/l10n.php
+++ b/wp-includes/l10n.php
@@ -31,8 +31,18 @@ function get_locale() {
 	global $locale, $wp_local_package;

 	if ( isset( $locale ) ) {
+		if ( empty( $locale ) || ! is_string( $locale ) ) {
+			$locale = 'en_US';
+		}
+
 		/** This filter is documented in wp-includes/l10n.php */
-		return apply_filters( 'locale', $locale );
+		$filtered_locale = apply_filters( 'locale', $locale );
+
+		if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) {
+			return $locale;
+		}
+
+		return $filtered_locale;
 	}

 	if ( isset( $wp_local_package ) ) {
@@ -66,18 +76,26 @@ function get_locale() {
 		}
 	}

-	if ( empty( $locale ) ) {
+	if ( empty( $locale ) || ! is_string( $locale ) ) {
 		$locale = 'en_US';
 	}

 	/**
 	 * Filters the locale ID of the WordPress installation.
 	 *
+	 * A non-string or empty value is ignored.
+	 *
 	 * @since 1.5.0
 	 *
 	 * @param string $locale The locale ID.
 	 */
-	return apply_filters( 'locale', $locale );
+	$filtered_locale = apply_filters( 'locale', $locale );
+
+	if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) {
+		return $locale;
+	}
+
+	return $filtered_locale;
 }

 /**
@@ -108,7 +126,11 @@ function get_user_locale( $user = 0 ) {

 	$locale = $user_object->locale;

-	return $locale ? $locale : get_locale();
+	if ( empty( $locale ) || ! is_string( $locale ) ) {
+		return get_locale();
+	}
+
+	return $locale;
 }

 /**
@@ -162,18 +184,26 @@ function determine_locale() {
 		}
 	}

-	if ( ! $determined_locale ) {
+	if ( empty( $determined_locale ) || ! is_string( $determined_locale ) ) {
 		$determined_locale = get_locale();
 	}

 	/**
 	 * Filters the locale for the current request.
 	 *
+	 * A non-string or empty value is ignored.
+	 *
 	 * @since 5.0.0
 	 *
 	 * @param string $determined_locale The locale.
 	 */
-	return apply_filters( 'determine_locale', $determined_locale );
+	$filtered_locale = apply_filters( 'determine_locale', $determined_locale );
+
+	if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) {
+		return $determined_locale;
+	}
+
+	return $filtered_locale;
 }

 /**
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 28e2675257..516d385c01 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-64121';
+$wp_version = '7.2-alpha-64122';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.