diff --git a/src/wp-admin/admin-header.php b/src/wp-admin/admin-header.php index 9ec3999ac8..b5bbec0e98 100644 --- a/src/wp-admin/admin-header.php +++ b/src/wp-admin/admin-header.php @@ -156,7 +156,7 @@ if ( $current_screen->taxonomy ) $admin_body_class .= ' branch-' . str_replace( array( '.', ',' ), '-', floatval( get_bloginfo( 'version' ) ) ); $admin_body_class .= ' version-' . str_replace( '.', '-', preg_replace( '/^([.0-9]+).*/', '$1', get_bloginfo( 'version' ) ) ); $admin_body_class .= ' admin-color-' . sanitize_html_class( get_user_option( 'admin_color' ), 'fresh' ); -$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) ); +$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) ); if ( wp_is_mobile() ) $admin_body_class .= ' mobile'; diff --git a/src/wp-admin/includes/class-wp-plugin-install-list-table.php b/src/wp-admin/includes/class-wp-plugin-install-list-table.php index 475c95e8af..c4bdaf9a15 100644 --- a/src/wp-admin/includes/class-wp-plugin-install-list-table.php +++ b/src/wp-admin/includes/class-wp-plugin-install-list-table.php @@ -134,7 +134,7 @@ class WP_Plugin_Install_List_Table extends WP_List_Table { 'active_installs' => true ), // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results. - 'locale' => get_locale(), + 'locale' => get_user_locale(), 'installed_plugins' => $this->get_installed_plugin_slugs(), ); diff --git a/src/wp-admin/includes/class-wp-press-this.php b/src/wp-admin/includes/class-wp-press-this.php index 3ff242bed8..e275b16e5b 100644 --- a/src/wp-admin/includes/class-wp-press-this.php +++ b/src/wp-admin/includes/class-wp-press-this.php @@ -1312,7 +1312,7 @@ class WP_Press_This { $admin_body_class .= ' branch-' . str_replace( array( '.', ',' ), '-', floatval( $wp_version ) ); $admin_body_class .= ' version-' . str_replace( '.', '-', preg_replace( '/^([.0-9]+).*/', '$1', $wp_version ) ); $admin_body_class .= ' admin-color-' . sanitize_html_class( get_user_option( 'admin_color' ), 'fresh' ); - $admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) ); + $admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) ); /** This filter is documented in wp-admin/admin-header.php */ $admin_body_classes = apply_filters( 'admin_body_class', '' ); diff --git a/src/wp-admin/includes/credits.php b/src/wp-admin/includes/credits.php index 6634724c84..005858c384 100644 --- a/src/wp-admin/includes/credits.php +++ b/src/wp-admin/includes/credits.php @@ -16,7 +16,7 @@ */ function wp_credits() { $wp_version = get_bloginfo( 'version' ); - $locale = get_locale(); + $locale = get_user_locale(); $results = get_site_transient( 'wordpress_credits_' . $locale ); diff --git a/src/wp-admin/includes/dashboard.php b/src/wp-admin/includes/dashboard.php index 659e0600d8..a338f4c48d 100644 --- a/src/wp-admin/includes/dashboard.php +++ b/src/wp-admin/includes/dashboard.php @@ -1353,7 +1353,7 @@ function wp_dashboard_browser_nag() { $notice .= "

{$msg}

"; $browsehappy = 'http://browsehappy.com/'; - $locale = get_locale(); + $locale = get_user_locale(); if ( 'en_US' !== $locale ) $browsehappy = add_query_arg( 'locale', $locale, $browsehappy ); diff --git a/src/wp-admin/includes/import.php b/src/wp-admin/includes/import.php index 4f0b78536f..0d534422c2 100644 --- a/src/wp-admin/includes/import.php +++ b/src/wp-admin/includes/import.php @@ -125,13 +125,13 @@ function wp_import_handle_upload() { function wp_get_popular_importers() { include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version - $locale = get_locale(); + $locale = get_user_locale(); $cache_key = 'popular_importers_' . md5( $locale . $wp_version ); $popular_importers = get_site_transient( $cache_key ); if ( ! $popular_importers ) { $url = add_query_arg( array( - 'locale' => get_locale(), + 'locale' => get_user_locale(), 'version' => $wp_version, ), 'http://api.wordpress.org/core/importers/1.1/' ); $options = array( 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url() ); diff --git a/src/wp-admin/includes/plugin-install.php b/src/wp-admin/includes/plugin-install.php index 10568b0158..b569d7679d 100644 --- a/src/wp-admin/includes/plugin-install.php +++ b/src/wp-admin/includes/plugin-install.php @@ -109,7 +109,7 @@ function plugins_api( $action, $args = array() ) { } if ( ! isset( $args->locale ) ) { - $args->locale = get_locale(); + $args->locale = get_user_locale(); } /** diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index f9f463b62f..d6fcab06ee 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1622,7 +1622,7 @@ do_action( "admin_head-$hook_suffix" ); /** This action is documented in wp-admin/admin-header.php */ do_action( 'admin_head' ); -$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) ); +$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) ); if ( is_rtl() ) $admin_body_class .= ' rtl'; diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php index 95aaec1618..b5fc98042d 100644 --- a/src/wp-admin/includes/theme.php +++ b/src/wp-admin/includes/theme.php @@ -412,7 +412,7 @@ function themes_api( $action, $args = array() ) { } if ( ! isset( $args->locale ) ) { - $args->locale = get_locale(); + $args->locale = get_user_locale(); } /** diff --git a/src/wp-admin/includes/user.php b/src/wp-admin/includes/user.php index fc46dd3ad2..6bf826bb2a 100644 --- a/src/wp-admin/includes/user.php +++ b/src/wp-admin/includes/user.php @@ -94,6 +94,16 @@ function edit_user( $user_id = 0 ) { $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' == $_POST['rich_editing'] ? 'false' : 'true'; $user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh'; $user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false'; + $user->locale = ''; + + if ( isset( $_POST['locale'] ) ) { + $locale = sanitize_text_field( $_POST['locale'] ); + if ( ! in_array( $locale, get_available_languages(), true ) ) { + $locale = ''; + } + + $user->locale = ( '' === $locale ) ? 'en_US' : $locale; + } } $user->comment_shortcuts = isset( $_POST['comment_shortcuts'] ) && 'true' == $_POST['comment_shortcuts'] ? 'true' : ''; diff --git a/src/wp-admin/options.php b/src/wp-admin/options.php index f319d5208e..93c69bc1e4 100644 --- a/src/wp-admin/options.php +++ b/src/wp-admin/options.php @@ -209,19 +209,23 @@ if ( 'update' == $action ) { $value = null; if ( isset( $_POST[ $option ] ) ) { $value = $_POST[ $option ]; - if ( ! is_array( $value ) ) + if ( ! is_array( $value ) ) { $value = trim( $value ); + } $value = wp_unslash( $value ); } update_option( $option, $value ); } // Switch translation in case WPLANG was changed. - $language = get_option( 'WPLANG' ); - if ( $language ) { - load_default_textdomain( $language ); - } else { - unload_textdomain( 'default' ); + $language = get_option( 'WPLANG' ); + $user_language = get_user_locale(); + if ( $language === $user_language ) { + if ( $language ) { + load_default_textdomain( $language ); + } else { + unload_textdomain( 'default' ); + } } } diff --git a/src/wp-admin/plugin-editor.php b/src/wp-admin/plugin-editor.php index a5dd63dba1..f9bdc80f8f 100644 --- a/src/wp-admin/plugin-editor.php +++ b/src/wp-admin/plugin-editor.php @@ -260,7 +260,7 @@ foreach ( $plugin_files as $plugin_file ) : -
+
diff --git a/src/wp-admin/theme-editor.php b/src/wp-admin/theme-editor.php index cd9f8392f1..145ff5a94d 100644 --- a/src/wp-admin/theme-editor.php +++ b/src/wp-admin/theme-editor.php @@ -263,7 +263,7 @@ else : ?>
- +
diff --git a/src/wp-admin/user-edit.php b/src/wp-admin/user-edit.php index b97cdddd95..0e183e71d7 100644 --- a/src/wp-admin/user-edit.php +++ b/src/wp-admin/user-edit.php @@ -269,6 +269,38 @@ if ( !( IS_PROFILE_PAGE && !$user_can_edit ) ) : ?> + + + + + + + + ID ); + + if ( 'en_US' === $user_locale ) { // en_US + $user_locale = false; + } elseif ( ! in_array( $user_locale, $languages, true ) ) { + $user_locale = get_locale(); + } + + wp_dropdown_languages( array( + 'name' => 'locale', + 'id' => 'locale', + 'selected' => $user_locale, + 'languages' => $languages, + 'show_available_translations' => false + ) ); + ?> + + + + locale; + return ( '' === $locale ) ? get_locale() : $locale; +} + /** * Retrieve the translation of $text. * @@ -633,7 +650,7 @@ function unload_textdomain( $domain ) { */ function load_default_textdomain( $locale = null ) { if ( null === $locale ) { - $locale = get_locale(); + $locale = is_admin() ? get_user_locale() : get_locale(); } // Unload previously loaded strings so we can switch translations. @@ -1148,4 +1165,4 @@ function is_rtl() { return false; } return $wp_locale->is_rtl(); -} \ No newline at end of file +} diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index fc40b478b9..a83e57e099 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -1358,6 +1358,7 @@ function validate_username( $username ) { * @since 2.0.0 * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact * methods for new installs. See wp_get_user_contact_methods(). + * @since 4.7.0 The user's locale can be passed to `$userdata`. * * @global wpdb $wpdb WordPress database abstraction object. * @@ -1392,6 +1393,7 @@ function validate_username( $username ) { * @type string|bool $show_admin_bar_front Whether to display the Admin Bar for the user on the * site's front end. Default true. * @type string $role User's role. + * @type string $locale User's locale. Default empty. * } * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not * be created. @@ -1606,6 +1608,8 @@ function wp_insert_user( $userdata ) { $meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front']; + $meta['locale'] = isset( $userdata['locale'] ) ? $userdata['locale'] : ''; + $user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $user_nicename, $user_login)); if ( $user_nicename_check ) { @@ -1965,7 +1969,7 @@ function wp_create_user($username, $password, $email = '') { * @return array List of user keys to be populated in wp_update_user(). */ function _get_additional_user_keys( $user ) { - $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front' ); + $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' ); return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) ); } diff --git a/tests/phpunit/tests/l10n/getUserLocale.php b/tests/phpunit/tests/l10n/getUserLocale.php new file mode 100644 index 0000000000..1ea3d4b89a --- /dev/null +++ b/tests/phpunit/tests/l10n/getUserLocale.php @@ -0,0 +1,78 @@ +user_id = $this->factory()->user->create( array( + 'role' => 'administrator', + 'locale' => 'de_DE', + ) ); + + wp_set_current_user( $this->user_id ); + } + + public function tearDown() { + delete_user_meta( $this->user_id, 'locale' ); + set_current_screen( 'front' ); + + parent::tearDown(); + } + + public function test_user_locale_property() { + set_current_screen( 'dashboard' ); + $this->assertSame( 'de_DE', get_user_locale() ); + $this->assertSame( get_user_by( 'id', $this->user_id )->locale, get_user_locale() ); + } + + public function test_update_user_locale() { + set_current_screen( 'dashboard' ); + update_user_meta( $this->user_id, 'locale', 'fr_FR' ); + $this->assertSame( 'fr_FR', get_user_locale() ); + } + + public function test_returns_site_locale_if_empty() { + set_current_screen( 'dashboard' ); + update_user_meta( $this->user_id, 'locale', '' ); + $this->assertSame( get_locale(), get_user_locale() ); + } + + public function test_returns_correct_user_locale() { + set_current_screen( 'dashboard' ); + $this->assertSame( 'de_DE', get_user_locale() ); + } + + public function test_returns_correct_user_locale_on_frontend() { + $this->assertSame( 'de_DE', get_user_locale() ); + } + + public function test_site_locale_is_not_affected() { + set_current_screen( 'dashboard' ); + $this->assertSame( 'en_US', get_locale() ); + } + + public function test_site_locale_is_not_affected_on_frontend() { + $this->assertSame( 'en_US', get_locale() ); + } + + public function test_user_locale_is_same_across_network() { + if ( ! is_multisite() ) { + $this->markTestSkipped( __METHOD__ . ' requires multisite' ); + } + + $user_locale = get_user_locale(); + + switch_to_blog( self::factory()->blog->create() ); + $user_locale_2 = get_user_locale(); + restore_current_blog(); + + $this->assertSame( 'de_DE', $user_locale ); + $this->assertSame( $user_locale, $user_locale_2 ); + } +}