Make WordPress Core

Opened 7 weeks ago

Last modified 5 days ago

#64977 assigned defect (bug)

The `core/get-site-info` ability returns wrong locale code in the admin

Reported by: afercia's profile afercia Owned by: gziolo's profile gziolo
Milestone: 7.1 Priority: normal
Severity: normal Version: 6.9
Component: Abilities API Keywords: abilities has-patch has-unit-tests commit
Focuses: Cc:

Description (last modified by afercia)

Besides other site data, the core/get-site-info ability is supposed to return The site language locale code.

I would expect that to be the site language locale.
However, in the admin it returns the _user_locale which may not the same of the site locale.

Under the hood, the ability callback uses get_bloginfo().

That doesn't seem to be the right tool to retrieve the _site_ language locale code. In fact, get_bloginfo():

  • can be passed a different language code by translators
  • internally uses determine_locale() which uses some logic that may return different results depending on (roughly):
    • whether the page is the login page
    • whether is an admin or whether there are some get parameters related to user and locale _and_ it's a json request: in this case it returns the _user_locale
    • other logic based on whether there is a 'language' request parameter or is installing
    • finally fallbacks to get_locale() which returns the _site_ locale

It is also worth considering what the ability should return in a multisite installation.

To reproduce:

  • In WP Admin > Settings > General, set the Site Language to a language other than english e.g. Italian.
  • In WP Admin > Users > Profile, set the Language to a different language e.g. Spanish.
  • Place the following snippet in a template that outputs on the front end:
$ability = wp_get_ability( 'core/get-site-info' );
$result  = $ability->execute();
var_dump( $result['language'] );
  • Observe it returns string(5) "it-IT", which is the expected site locale.
  • Place the same snippet into some admin page for example in options-general.php.
  • Observe it returns string(2) "es" which is not the expected locale. Actually, this is the _user_ locale.
  • Worth checking what is returned in a REST API call. As far as I see the returned language is correct. Tested with:
curl -X GET "http://localhost:8889/wp-json/wp-abilities/v1/abilities/core/get-site-info/run" \
  -u "your-username:your-generated-application-password" \
  -H "Content-Type: application/json"

Introduced in https://core.trac.wordpress.org/changeset/61063/
See https://core.trac.wordpress.org/ticket/64146

Change History (17)

#1 @afercia
7 weeks ago

  • Description modified (diff)

This ticket was mentioned in PR #11388 on WordPress/wordpress-develop by @iamadisingh.


7 weeks ago
#2

  • Keywords has-patch added

This core/get-site-info ability returned the wrong language value in admin context. It used get_bloginfo( language ), which goes through determine_locale() and may resolve to the current user locale in wp-admin. For a site-level ability, this is incorrect because the language field should represent the site locale consistently.

Trac ticket: https://core.trac.wordpress.org/ticket/64977

#3 @audrasjb
7 weeks ago

  • Milestone changed from Awaiting Review to 7.1

The PR looks good to me. Moving to milestone 7.1.

This ticket was mentioned in PR #11492 on WordPress/wordpress-develop by @sangu3105.


6 weeks ago
#4

The core/get-site-info ability should return the site locale, but currently returns the user locale in admin due to using get_bloginfo(), which relies on determine_locale().

This can vary based on context (admin, login, request params, JSON requests), making it unreliable for getting the actual site locale.

The site's language is stored directly as the WPLANG option. Reading it bypasses all the context-dependent filtering:

if ( 'language' === $field ) {

$result[ $field ] = str_replace( '_', '-', get_option( 'WPLANG' ) ?: 'en_US' );

}
get_option('WPLANG') returns the raw DB value — no context influence
The ?: 'en_US' handles the default English case where WPLANG is an empty string
str_replace('_', '-', ...) preserves the existing BCP 47 formatting (e.g. en-US, pt-BR)

Multisite Behavior
In multisite, get_option() already scopes to the current site (it internally routes through get_blog_option()), so no special handling is needed. Each site in the network correctly returns its own configured language. If you ever needed the network-level language, that would be get_network_option(null, 'WPLANG'), but that's not appropriate for core/get-site-info.

This ticket was mentioned in PR #11498 on WordPress/wordpress-develop by @sangu3105.


6 weeks ago
#5

The core/get-site-info ability should return the site locale, but currently returns the user locale in admin due to using get_bloginfo(), which relies on determine_locale().

This can vary based on context (admin, login, request params, JSON requests), making it unreliable for getting the actual site locale.

The site's language is stored directly as the WPLANG option. Reading it bypasses all the context-dependent filtering:

if ( 'language' === $field ) {
$result[ $field ] = str_replace( , '-', get_option( 'WPLANG' ) ?: 'en_US' );
}
get_option('WPLANG') returns the raw DB value — no context influence
The ?: 'en_US' handles the default English case where WPLANG is an empty string
str_replace(
, '-', ...) preserves the existing BCP 47 formatting (e.g. en-US, pt-BR)

Multisite Behavior
In multisite, get_option() already scopes to the current site (it internally routes through get_blog_option()), so no special handling is needed. Each site in the network correctly returns its own configured language. If you ever needed the network-level language, that would be get_network_option(null, 'WPLANG'), but that's not appropriate for core/get-site-info.

This ticket was mentioned in PR #11502 on WordPress/wordpress-develop by @sangu3105.


6 weeks ago
#6

Ticket: 64977
Link: https://core.trac.wordpress.org/ticket/64977

The core/get-site-info ability should return the site locale, but currently returns the user locale in admin due to using get_bloginfo(), which relies on determine_locale().

This can vary based on context (admin, login, request params, JSON requests), making it unreliable for getting the actual site locale.

The site's language is stored directly as the WPLANG option. Reading it bypasses all the context-dependent filtering:

if ( 'language' === $field ) {
$result[ $field ] = str_replace( , '-', get_option( 'WPLANG' ) ?: 'en_US' );
}
get_option('WPLANG') returns the raw DB value — no context influence
The ?: 'en_US' handles the default English case where WPLANG is an empty string
str_replace(
, '-', ...) preserves the existing BCP 47 formatting (e.g. en-US, pt-BR)

Multisite Behavior
In multisite, get_option() already scopes to the current site (it internally routes through get_blog_option()), so no special handling is needed. Each site in the network correctly returns its own configured language. If you ever needed the network-level language, that would be get_network_option(null, 'WPLANG'), but that's not appropriate for core/get-site-info.

#7 @JeffPaul
6 weeks ago

  • Keywords abilities added

#8 @desrosj
4 weeks ago

  • Component changed from AI to Abilities API

Moving tickets related to the Abilities API to a new sub-component.

This ticket was mentioned in Slack in #core-test by nikunj8866. View the logs.


3 weeks ago

#10 @yusufmudagal
2 weeks ago

Tested PR https://github.com/WordPress/wordpress-develop/pull/11388

In a local test environment, I set the site locale to it_IT and the admin user locale to es_ES.

Before the patch, wp_get_ability( 'core/get-site-info' )->execute() returned language => es in admin context.
After the patch, the same ability returned language => it-IT, matching the site locale instead of the user locale.

Patch tests well for me.

#11 @r1k0
2 weeks ago

Patch Testing Report

Patch Tested: https://github.com/WordPress/wordpress-develop/pull/11502

Environment

  • WordPress: 7.1-alpha-62161-src
  • PHP: 8.3.30
  • Server: nginx/1.29.5
  • Database: mysqli (Server: 8.4.8 / Client: mysqlnd 8.3.30)
  • Browser: Chrome 147.0.0.0
  • OS: Windows 10/11
  • Theme: Twenty Twenty-Five 1.4
  • MU Plugins: None activated
  • Plugins:
    • Test Reports 1.2.1

Steps taken

  1. In WP Admin > Settings > General, set the Site Language to a language other than English, e.g., Italian.
  2. In WP Admin > Users > Profile, set the Language to a different language e.g., French.
  3. Used the code provided by the reporter and created a plugin.
    <?php
    /**
     * Plugin Name: Test Language Locale
     * Description: Difference in language locale
     * Version: 1.0
    */
    
    add_action( 'wp_footer', 'language_locale' );
    
    add_action( 'admin_footer', 'language_locale' );
    
    function language_locale () {
            $ability = wp_get_ability( 'core/get-site-info' );
            $result  = $ability->execute();
            var_dump( $result['language'] );
    }
    
  4. Head over to the footer of the site on the frontend and observe the returned language before and after the patch.
  5. Head over to the admin dashboard, scroll down to the end of the page and observe the returned language before and after the patch.
  6. ✅ Patch is solving the problem

Expected result

  • The site locale should be returned in the admin dashboard instead of the user-specific admin locale.

Additional Notes

  • None

Screenshots/Screencast with results

  • Before:

https://i.ibb.co/bMPDq4Zf/language-locale-before.png

  • After:

https://i.ibb.co/Xx2NMzTG/language-locale-after.png

@gziolo commented on PR #11388:


7 days ago
#12

@Adi-ty, this patch looks good. Would you mind adding a single unit test covering this edge case so it doesn't regress over time?

#13 @gziolo
7 days ago

I see multiple patches attached to this ticket. https://github.com/WordPress/wordpress-develop/pull/11388 looks the closest, but it still would benefit from a unit test that covers this edge case. https://github.com/WordPress/wordpress-develop/pull/11502 seems correct, but it uses a subset of functionality that the former covers through get_locale() call. @afercia, does it fix the issue for you as well?

#14 @gziolo
7 days ago

  • Owner set to gziolo
  • Status changed from new to assigned

@iamadisingh commented on PR #11388:


6 days ago
#15

@gziolo
Sure, I’ll add a unit test for this edge case as well

@iamadisingh commented on PR #11388:


6 days ago
#16

Added a unit test covering the edge case as well

#17 @gziolo
5 days ago

  • Keywords has-unit-tests commit added
Note: See TracTickets for help on using tickets.