diff --git a/src/wp-admin/includes/ms.php b/src/wp-admin/includes/ms.php index 7e08e6f4f2..48dbf55c74 100644 --- a/src/wp-admin/includes/ms.php +++ b/src/wp-admin/includes/ms.php @@ -272,6 +272,8 @@ function update_option_new_admin_email( $old_value, $value ) { ); update_option( 'adminhash', $new_admin_email ); + $switched_locale = switch_to_locale( get_user_locale() ); + /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ $email_text = __( 'Howdy ###USERNAME###, @@ -315,6 +317,10 @@ All at ###SITENAME### $content = str_replace( '###SITEURL###', network_home_url(), $content ); wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); + + if ( $switched_locale ) { + restore_previous_locale(); + } } /** @@ -353,6 +359,8 @@ function send_confirmation_on_profile_email() { ); update_user_meta( $current_user->ID, '_new_email', $new_user_email ); + $switched_locale = switch_to_locale( get_user_locale() ); + /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ $email_text = __( 'Howdy ###USERNAME###, @@ -395,6 +403,10 @@ All at ###SITENAME### wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); $_POST['email'] = $current_user->user_email; + + if ( $switched_locale ) { + restore_previous_locale(); + } } } diff --git a/src/wp-admin/ms-delete-site.php b/src/wp-admin/ms-delete-site.php index 9192509d94..6bc3cfcd89 100644 --- a/src/wp-admin/ms-delete-site.php +++ b/src/wp-admin/ms-delete-site.php @@ -42,6 +42,8 @@ if ( isset( $_POST['action'] ) && $_POST['action'] == 'deleteblog' && isset( $_P $url_delete = esc_url( admin_url( 'ms-delete-site.php?h=' . $hash ) ); + $switched_locale = switch_to_locale( get_locale() ); + /* translators: Do not translate USERNAME, URL_DELETE, SITE_NAME: those are placeholders. */ $content = __( "Howdy ###USERNAME###, @@ -73,6 +75,10 @@ Webmaster $content = str_replace( '###SITE_NAME###', get_network()->site_name, $content ); wp_mail( get_option( 'admin_email' ), "[ " . wp_specialchars_decode( get_option( 'blogname' ) ) . " ] ".__( 'Delete My Site' ), $content ); + + if ( $switched_locale ) { + restore_previous_locale(); + } ?>
diff --git a/src/wp-admin/user-new.php b/src/wp-admin/user-new.php index 38b78dd5af..4915960027 100644 --- a/src/wp-admin/user-new.php +++ b/src/wp-admin/user-new.php @@ -87,6 +87,8 @@ if ( isset($_REQUEST['action']) && 'adduser' == $_REQUEST['action'] ) { */ do_action( 'invite_user', $user_id, $role, $newuser_key ); + $switched_locale = switch_to_locale( get_user_locale( $user_details ) ); + /* translators: 1: Site name, 2: site URL, 3: role, 4: activation URL */ $message = __( 'Hi, @@ -96,6 +98,11 @@ You\'ve been invited to join \'%1$s\' at Please click the following link to confirm the invite: %4$s' ); wp_mail( $new_user_email, sprintf( __( '[%s] Joining confirmation' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), sprintf( $message, get_option( 'blogname' ), home_url(), wp_specialchars_decode( translate_user_role( $role['name'] ) ), home_url( "/newbloguser/$newuser_key/" ) ) ); + + if ( $switched_locale ) { + restore_previous_locale(); + } + $redirect = add_query_arg( array('update' => 'add'), 'user-new.php' ); } } diff --git a/src/wp-includes/class-wp-locale-switcher.php b/src/wp-includes/class-wp-locale-switcher.php new file mode 100644 index 0000000000..3d288c8c35 --- /dev/null +++ b/src/wp-includes/class-wp-locale-switcher.php @@ -0,0 +1,244 @@ +original_locale = is_admin() ? get_user_locale() : get_locale(); + $this->available_languages = array_merge( array( 'en_US' ), get_available_languages() ); + } + + /** + * Initializes the locale switcher. + * + * Hooks into the {@see 'locale'} filter to change the locale on the fly. + */ + public function init() { + add_filter( 'locale', array( $this, 'filter_locale' ) ); + } + + /** + * Switches the translations according to the given locale. + * + * @since 4.7.0 + * + * @param string $locale The locale to switch to. + * @return bool True on success, false on failure. + */ + public function switch_to_locale( $locale ) { + $current_locale = is_admin() ? get_user_locale() : get_locale(); + if ( $current_locale === $locale ) { + return false; + } + + if ( ! in_array( $locale, $this->available_languages, true ) ) { + return false; + } + + $this->locales[] = $locale; + + $this->change_locale( $locale ); + + /** + * Fires when the locale is switched. + * + * @since 4.7.0 + * + * @param string $locale The new locale. + */ + do_action( 'switch_locale', $locale ); + + return true; + } + + /** + * Restores the translations according to the previous locale. + * + * @since 4.7.0 + * + * @return string|false Locale on success, false on failure. + */ + public function restore_previous_locale() { + $previous_locale = array_pop( $this->locales ); + + if ( null === $previous_locale ) { + // The stack is empty, bail. + return false; + } + + $locale = end( $this->locales ); + + if ( ! $locale ) { + // There's nothing left in the stack: go back to the original locale. + $locale = $this->original_locale; + } + + $this->change_locale( $locale ); + + /** + * Fires when the locale is restored to the previous one. + * + * @since 4.7.0 + * + * @param string $locale The new locale. + * @param string $previous_locale The previous locale. + */ + do_action( 'restore_previous_locale', $locale, $previous_locale ); + + return $locale; + } + + /** + * Restores the translations according to the original locale. + * + * @since 4.7.0 + * + * @return string|false Locale on success, false on failure. + */ + public function restore_current_locale() { + if ( empty( $this->locales ) ) { + return false; + } + + $this->locales = array( $this->original_locale ); + + return $this->restore_previous_locale(); + } + + /** + * Whether switch_to_locale() is in effect. + * + * @since 4.7.0 + * + * @return bool True if the locale has been switched, false otherwise. + */ + public function is_switched() { + return ! empty( $this->locales ); + } + + /** + * Filters the WordPress install's locale. + * + * @since 4.7.0 + * + * @param string $locale The WordPress install's locale. + * @return string The locale currently being switched to. + */ + public function filter_locale( $locale ) { + $switched_locale = end( $this->locales ); + + if ( $switched_locale ) { + return $switched_locale; + } + + return $locale; + } + + /** + * Load translations for a given locale. + * + * When switching to a locale, translations for this locale must be loaded from scratch. + * + * @since 4.7.0 + * @access private + * + * @global Mo[] $l10n An array of all currently loaded text domains. + * + * @param string $locale The locale to load translations for. + */ + private function load_translations( $locale ) { + global $l10n; + + $domains = $l10n ? array_keys( $l10n ) : array(); + + load_default_textdomain( $locale ); + + foreach ( $domains as $domain ) { + if ( 'default' === $domain ) { + continue; + } + + $mofile = $l10n[ $domain ]->get_filename(); + + unload_textdomain( $domain ); + + if ( $mofile ) { + load_textdomain( $domain, $mofile ); + } + + get_translations_for_domain( $domain ); + } + } + + /** + * Changes the site's locale to the given one. + * + * Loads the translations, changes the global `$wp_locale` object and updates + * all post type labels. + * + * @since 4.7.0 + * @access private + * + * @global WP_Locale $wp_locale The WordPress date and time locale object. + * + * @param string $locale The locale to change to. + */ + private function change_locale( $locale ) { + $this->load_translations( $locale ); + + $GLOBALS['wp_locale'] = new WP_Locale(); + + /** + * Fires when the locale is switched to or restored. + * + * @since 4.7.0 + * + * @param string $locale The new locale. + */ + do_action( 'change_locale', $locale ); + } +} diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 323e55e840..58aedf91f2 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -406,6 +406,7 @@ add_action( 'init', 'create_initial_post_types', 0 ); // highest priority add_action( 'admin_menu', '_add_post_type_submenus' ); add_action( 'before_delete_post', '_reset_front_page_settings_for_post' ); add_action( 'wp_trash_post', '_reset_front_page_settings_for_post' ); +add_action( 'change_locale', 'create_initial_post_types' ); // Post Formats add_filter( 'request', '_post_format_request' ); @@ -431,6 +432,7 @@ add_filter( 'style_loader_src', 'wp_style_loader_src', 10, 2 ); // Taxonomy add_action( 'init', 'create_initial_taxonomies', 0 ); // highest priority +add_action( 'change_locale', 'create_initial_taxonomies' ); // Canonical add_action( 'template_redirect', 'redirect_canonical' ); diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index ba696ded93..ba856491af 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -1178,3 +1178,68 @@ function is_rtl() { } return $wp_locale->is_rtl(); } + +/** + * Switches the translations according to the given locale. + * + * @since 4.7.0 + * + * @global WP_Locale_Switcher $wp_locale_switcher + * + * @param string $locale The locale. + * @return bool True on success, false on failure. + */ +function switch_to_locale( $locale ) { + /* @var WP_Locale_Switcher $wp_locale_switcher */ + global $wp_locale_switcher; + + return $wp_locale_switcher->switch_to_locale( $locale ); +} + +/** + * Restores the translations according to the previous locale. + * + * @since 4.7.0 + * + * @global WP_Locale_Switcher $wp_locale_switcher + * + * @return string|false Locale on success, false on error. + */ +function restore_previous_locale() { + /* @var WP_Locale_Switcher $wp_locale_switcher */ + global $wp_locale_switcher; + + return $wp_locale_switcher->restore_previous_locale(); +} + +/** + * Restores the translations according to the original locale. + * + * @since 4.7.0 + * + * @global WP_Locale_Switcher $wp_locale_switcher + * + * @return string|false Locale on success, false on error. + */ +function restore_current_locale() { + /* @var WP_Locale_Switcher $wp_locale_switcher */ + global $wp_locale_switcher; + + return $wp_locale_switcher->restore_current_locale(); +} + +/** + * Whether switch_to_locale() is in effect. + * + * @since 4.7.0 + * + * @global WP_Locale_Switcher $wp_locale_switcher + * + * @return bool True if the locale has been switched, false otherwise. + */ +function is_locale_switched() { + /* @var WP_Locale_Switcher $wp_locale_switcher */ + global $wp_locale_switcher; + + return $wp_locale_switcher->is_switched(); +} diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 4a3e1c3e28..523960f023 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -841,13 +841,14 @@ function get_current_network_id() { * @since 3.4.0 * @access private * - * @global string $text_direction - * @global WP_Locale $wp_locale The WordPress date and time locale object. + * @global string $text_direction + * @global WP_Locale $wp_locale The WordPress date and time locale object. + * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. * * @staticvar bool $loaded */ function wp_load_translations_early() { - global $text_direction, $wp_locale; + global $text_direction, $wp_locale, $wp_locale_switcher; static $loaded = false; if ( $loaded ) @@ -864,6 +865,7 @@ function wp_load_translations_early() { require_once ABSPATH . WPINC . '/pomo/mo.php'; require_once ABSPATH . WPINC . '/l10n.php'; require_once ABSPATH . WPINC . '/class-wp-locale.php'; + require_once ABSPATH . WPINC . '/class-wp-locale-switcher.php'; // General libraries require_once ABSPATH . WPINC . '/plugin.php'; @@ -915,6 +917,8 @@ function wp_load_translations_early() { } $wp_locale = new WP_Locale(); + $wp_locale_switcher = new WP_Locale_Switcher(); + $wp_locale_switcher->init(); } /** diff --git a/src/wp-includes/ms-functions.php b/src/wp-includes/ms-functions.php index c65c9d5753..3eb7f671dc 100644 --- a/src/wp-includes/ms-functions.php +++ b/src/wp-includes/ms-functions.php @@ -800,6 +800,10 @@ function wpmu_signup_blog_notification( $domain, $path, $title, $user, $user_ema $admin_email = 'support@' . $_SERVER['SERVER_NAME']; $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) ); $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n"; + + $user = get_user_by( 'login', $user ); + $switched_locale = switch_to_locale( get_user_locale( $user ) ); + $message = sprintf( /** * Filters the message content of the new blog notification email. @@ -849,6 +853,11 @@ function wpmu_signup_blog_notification( $domain, $path, $title, $user, $user_ema esc_url( 'http://' . $domain . $path ) ); wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); + + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } @@ -887,6 +896,9 @@ function wpmu_signup_user_notification( $user, $user_email, $key, $meta = array( if ( ! apply_filters( 'wpmu_signup_user_notification', $user, $user_email, $key, $meta ) ) return false; + $user = get_user_by( 'login', $user ); + $switched_locale = switch_to_locale( get_user_locale( $user ) ); + // Send email with activation link. $admin_email = get_site_option( 'admin_email' ); if ( $admin_email == '' ) @@ -934,6 +946,11 @@ function wpmu_signup_user_notification( $user, $user_email, $key, $meta = array( $user ); wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); + + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } @@ -1448,6 +1465,10 @@ function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) ) return false; + $user = get_userdata( $user_id ); + + $switched_locale = switch_to_locale( get_user_locale( $user ) ); + $welcome_email = get_site_option( 'welcome_email' ); if ( $welcome_email == false ) { /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */ @@ -1468,7 +1489,6 @@ We hope you enjoy your new site. Thanks! } $url = get_blogaddress_by_id($blog_id); - $user = get_userdata( $user_id ); $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email ); $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email ); @@ -1512,6 +1532,11 @@ We hope you enjoy your new site. Thanks! */ $subject = apply_filters( 'update_welcome_subject', sprintf( __( 'New %1$s Site: %2$s' ), $current_network->site_name, wp_unslash( $title ) ) ); wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); + + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } @@ -1551,6 +1576,8 @@ function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) $user = get_userdata( $user_id ); + $switched_locale = switch_to_locale( get_user_locale( $user ) ); + /** * Filters the content of the welcome email after user activation. * @@ -1590,6 +1617,11 @@ function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) */ $subject = apply_filters( 'update_welcome_user_subject', sprintf( __( 'New %1$s User: %2$s' ), $current_network->site_name, $user->user_login) ); wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); + + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index d7713b7dd2..d232426e3d 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -211,7 +211,7 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() // (Re)create it, if it's gone missing if ( ! ( $phpmailer instanceof PHPMailer ) ) { require_once ABSPATH . WPINC . '/class-phpmailer.php'; - require_once ABSPATH . WPINC . '/class-smtp.php'; + require_once ABSPATH . WPINC . '/class-smtp.php'; $phpmailer = new PHPMailer( true ); } @@ -1418,6 +1418,8 @@ function wp_notify_postauthor( $comment_id, $deprecated = null ) { $emails = array_flip( $emails ); } + $switched_locale = switch_to_locale( get_locale() ); + $comment_author_domain = @gethostbyaddr($comment->comment_author_IP); // The blogname option is escaped with esc_html on the way into the database in sanitize_option @@ -1522,6 +1524,10 @@ function wp_notify_postauthor( $comment_id, $deprecated = null ) { @wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers ); } + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } endif; @@ -1569,6 +1575,8 @@ function wp_notify_moderator($comment_id) { $emails[] = $user->user_email; } + $switched_locale = switch_to_locale( get_locale() ); + $comment_author_domain = @gethostbyaddr($comment->comment_author_IP); $comments_waiting = $wpdb->get_var("SELECT count(comment_ID) FROM $wpdb->comments WHERE comment_approved = '0'"); @@ -1664,6 +1672,10 @@ function wp_notify_moderator($comment_id) { @wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers ); } + if ( $switched_locale ) { + restore_previous_locale(); + } + return true; } endif; @@ -1723,11 +1735,16 @@ function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES); if ( 'user' !== $notify ) { + $switched_locale = switch_to_locale( get_locale() ); $message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n"; $message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; $message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n"; @wp_mail( get_option( 'admin_email' ), sprintf( __( '[%s] New User Registration' ), $blogname ), $message ); + + if ( $switched_locale ) { + restore_previous_locale(); + } } // `$deprecated was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notifcation. @@ -1748,6 +1765,8 @@ function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) $hashed = time() . ':' . $wp_hasher->HashPassword( $key ); $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) ); + $switched_locale = switch_to_locale( get_user_locale( $user ) ); + $message = sprintf(__('Username: %s'), $user->user_login) . "\r\n\r\n"; $message .= __('To set your password, visit the following address:') . "\r\n\r\n"; $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user->user_login), 'login') . ">\r\n\r\n"; @@ -1755,6 +1774,10 @@ function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) $message .= wp_login_url() . "\r\n"; wp_mail($user->user_email, sprintf(__('[%s] Your username and password info'), $blogname), $message); + + if ( $switched_locale ) { + restore_previous_locale(); + } } endif; diff --git a/src/wp-includes/pomo/mo.php b/src/wp-includes/pomo/mo.php index 6bc44d614d..47e9b6afe4 100644 --- a/src/wp-includes/pomo/mo.php +++ b/src/wp-includes/pomo/mo.php @@ -15,16 +15,37 @@ class MO extends Gettext_Translations { var $_nplurals = 2; + /** + * Loaded MO file. + * + * @var string + */ + private $filename = ''; + + /** + * Returns the loaded MO file. + * + * @return string The loaded MO file. + */ + public function get_filename() { + return $this->filename; + } + /** * Fills up with the entries from MO file $filename * * @param string $filename MO file to load */ function import_from_file($filename) { - $reader = new POMO_FileReader($filename); - if (!$reader->is_resource()) + $reader = new POMO_FileReader( $filename ); + + if ( ! $reader->is_resource() ) { return false; - return $this->import_from_reader($reader); + } + + $this->filename = (string) $filename; + + return $this->import_from_reader( $reader ); } /** @@ -299,4 +320,4 @@ class MO extends Gettext_Translations { return $this->_nplurals; } } -endif; \ No newline at end of file +endif; diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 734d26e0ff..d02b022272 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -1801,8 +1801,12 @@ function wp_update_user($userdata) { $blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); - if ( ! empty( $send_password_change_email ) ) { + $switched_locale = false; + if ( ! empty( $send_password_change_email ) || ! empty( $send_email_change_email ) ) { + $switched_locale = switch_to_locale( get_user_locale( $user_id ) ); + } + if ( ! empty( $send_password_change_email ) ) { /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */ $pass_change_text = __( 'Hi ###USERNAME###, @@ -1910,6 +1914,10 @@ All at ###SITENAME### wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] ); } + + if ( $switched_locale ) { + restore_previous_locale(); + } } // Update the cookies if the password changed. diff --git a/src/wp-settings.php b/src/wp-settings.php index bacb37d8f4..c592bab6e5 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -130,6 +130,7 @@ if ( SHORTINIT ) // Load the L10n library. require_once( ABSPATH . WPINC . '/l10n.php' ); require_once( ABSPATH . WPINC . '/class-wp-locale.php' ); +require_once( ABSPATH . WPINC . '/class-wp-locale-switcher.php' ); // Run the installer if WordPress is not installed. wp_not_installed(); @@ -400,6 +401,16 @@ unset( $locale_file ); */ $GLOBALS['wp_locale'] = new WP_Locale(); +/** + * WordPress Locale Switcher object for switching locales. + * + * @since 4.7.0 + * + * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. + */ +$GLOBALS['wp_locale_switcher'] = new WP_Locale_Switcher(); +$GLOBALS['wp_locale_switcher']->init(); + // Load the functions for the active theme, for both parent and child theme if applicable. if ( ! wp_installing() || 'wp-activate.php' === $pagenow ) { if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) ) diff --git a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php index f73cfd5a90..57aca144d3 100644 --- a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php +++ b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php @@ -9,7 +9,7 @@ class Tests_L10n_loadTextdomainJustInTime extends WP_UnitTestCase { private $orig_theme_dir; private $theme_root; - function setUp() { + public function setUp() { parent::setUp(); $this->theme_root = DIR_TESTDATA . '/themedir1'; @@ -22,17 +22,19 @@ class Tests_L10n_loadTextdomainJustInTime extends WP_UnitTestCase { add_filter( 'template_root', array( $this, 'filter_theme_root' ) ); wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); - + unset( $GLOBALS['l10n'] ); unset( $GLOBALS['l10n_unloaded'] ); } - function tearDown() { + public function tearDown() { $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir; remove_filter( 'theme_root', array( $this, 'filter_theme_root' ) ); remove_filter( 'stylesheet_root', array( $this, 'filter_theme_root' ) ); remove_filter( 'template_root', array( $this, 'filter_theme_root' ) ); wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); + unset( $GLOBALS['l10n'] ); + unset( $GLOBALS['l10n_unloaded'] ); parent::tearDown(); } @@ -60,7 +62,6 @@ class Tests_L10n_loadTextdomainJustInTime extends WP_UnitTestCase { $expected_output = i18n_plugin_test(); $is_textdomain_loaded_after = is_textdomain_loaded( 'internationalized-plugin' ); - unload_textdomain( 'internationalized-plugin' ); remove_filter( 'locale', array( $this, 'filter_set_locale_to_german' ) ); $this->assertFalse( $is_textdomain_loaded_before ); @@ -82,7 +83,6 @@ class Tests_L10n_loadTextdomainJustInTime extends WP_UnitTestCase { $expected_output = i18n_theme_test(); $is_textdomain_loaded_after = is_textdomain_loaded( 'internationalized-theme' ); - unload_textdomain( 'internationalized-theme' ); remove_filter( 'locale', array( $this, 'filter_set_locale_to_german' ) ); $this->assertFalse( $is_textdomain_loaded_before ); @@ -141,4 +141,34 @@ class Tests_L10n_loadTextdomainJustInTime extends WP_UnitTestCase { $this->assertSame( 'Das ist ein Dummy Plugin', $expected_output_final ); $this->assertTrue( $is_textdomain_loaded_final ); } + + /** + * @ticket 26511 + */ + public function test_plugin_translation_after_switching_locale() { + require_once DIR_TESTDATA . '/plugins/internationalized-plugin.php'; + + switch_to_locale( 'de_DE' ); + $expected = i18n_plugin_test(); + restore_previous_locale(); + + $this->assertSame( 'Das ist ein Dummy Plugin', $expected ); + } + + /** + * @ticket 26511 + */ + public function test_theme_translation_after_switching_locale() { + switch_theme( 'internationalized-theme' ); + + require_once get_stylesheet_directory() . '/functions.php'; + + switch_to_locale( 'de_DE' ); + $expected = i18n_theme_test(); + restore_previous_locale(); + + switch_theme( WP_DEFAULT_THEME ); + + $this->assertSame( 'Das ist ein Dummy Theme', $expected ); + } } diff --git a/tests/phpunit/tests/l10n/localeSwitcher.php b/tests/phpunit/tests/l10n/localeSwitcher.php new file mode 100644 index 0000000000..262f8b1810 --- /dev/null +++ b/tests/phpunit/tests/l10n/localeSwitcher.php @@ -0,0 +1,386 @@ +locale = ''; + $this->previous_locale = ''; + + unset( $GLOBALS['l10n'] ); + unset( $GLOBALS['l10n_unloaded'] ); + } + + public function tearDown() { + unset( $GLOBALS['l10n'] ); + unset( $GLOBALS['l10n_unloaded'] ); + + parent::tearDown(); + } + + public function test_switch_to_non_existent_locale_returns_false() { + $this->assertFalse( switch_to_locale( 'foo_BAR' ) ); + } + + public function test_switch_to_non_existent_locale_does_not_change_locale() { + switch_to_locale( 'foo_BAR' ); + + $this->assertSame( 'en_US', get_locale() ); + } + + public function test_switch_to_locale_returns_true() { + $expected = switch_to_locale( 'en_GB' ); + + // Cleanup. + restore_previous_locale(); + + $this->assertTrue( $expected ); + } + + public function test_switch_to_locale_changes_the_locale() { + switch_to_locale( 'en_GB' ); + + $locale = get_locale(); + + // Cleanup. + restore_previous_locale(); + + $this->assertSame( 'en_GB', $locale ); + } + + public function test_switch_to_locale_loads_translation() { + switch_to_locale( 'es_ES' ); + + $actual = __( 'Invalid parameter.' ); + + // Cleanup. + restore_previous_locale(); + + $this->assertSame( 'Parámetro no válido. ', $actual ); + } + + public function test_switch_to_locale_changes_wp_locale_global() { + global $wp_locale; + + $expected = array( + 'thousands_sep' => '.', + 'decimal_point' => ',', + ); + + switch_to_locale( 'de_DE' ); + + $wp_locale_de_DE = clone $wp_locale; + + // Cleanup. + restore_previous_locale(); + + $this->assertEqualSetsWithIndex( $expected, $wp_locale_de_DE->number_format ); + } + + public function test_switch_to_locale_en_US() { + switch_to_locale( 'en_GB' ); + $locale_en_GB = get_locale(); + switch_to_locale( 'en_US' ); + $locale_en_US = get_locale(); + + // Cleanup. + restore_current_locale(); + + $this->assertSame( 'en_GB', $locale_en_GB ); + $this->assertSame( 'en_US', $locale_en_US ); + } + + public function test_switch_to_locale_multiple_times() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'es_ES' ); + $locale = get_locale(); + + // Cleanup. + restore_previous_locale(); + restore_previous_locale(); + + $this->assertSame( 'es_ES', $locale ); + } + + public function test_switch_to_locale_multiple_times_loads_translation() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'de_DE' ); + switch_to_locale( 'es_ES' ); + + $actual = __( 'Invalid parameter.' ); + + // Cleanup. + restore_previous_locale(); + restore_previous_locale(); + restore_previous_locale(); + + $this->assertSame( 'Parámetro no válido. ', $actual ); + } + + public function test_restore_previous_locale_without_switching() { + $this->assertFalse( restore_previous_locale() ); + } + + public function test_restore_previous_locale_changes_the_locale_back() { + switch_to_locale( 'en_GB' ); + + // Cleanup. + restore_previous_locale(); + + $this->assertSame( 'en_US', get_locale() ); + } + + public function test_restore_previous_locale_after_switching_multiple_times() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'es_ES' ); + restore_previous_locale(); + + $locale = get_locale(); + + // Cleanup. + restore_previous_locale(); + + $this->assertSame( 'en_GB', $locale ); + } + + public function test_restore_previous_locale_restores_translation() { + switch_to_locale( 'es_ES' ); + restore_previous_locale(); + + $actual = __( 'Invalid parameter.' ); + + $this->assertSame( 'Invalid parameter.', $actual ); + } + + public function test_restore_previous_locale_action_passes_previous_locale() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'es_ES' ); + + add_action( 'restore_previous_locale', array( $this, 'store_locale' ), 10, 2 ); + + restore_previous_locale(); + + $previous_locale = $this->previous_locale; + + // Cleanup. + restore_previous_locale(); + + $this->assertSame( 'es_ES', $previous_locale ); + } + + public function test_restore_previous_locale_restores_wp_locale_global() { + global $wp_locale; + + $expected = array( + 'thousands_sep' => ',', + 'decimal_point' => '.', + ); + + switch_to_locale( 'de_DE' ); + restore_previous_locale(); + + $this->assertEqualSetsWithIndex( $expected, $wp_locale->number_format ); + } + + public function test_restore_current_locale_without_switching() { + $this->assertFalse( restore_current_locale() ); + } + + public function test_restore_current_locale_after_switching_multiple_times() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'nl_NL' ); + switch_to_locale( 'es_ES' ); + + restore_current_locale(); + + $this->assertSame( 'en_US', get_locale() ); + } + + public function store_locale( $locale, $previous_locale ) { + $this->locale = $locale; + $this->previous_locale = $previous_locale; + } + + public function test_is_locale_switched_if_not_switched() { + $this->assertFalse( is_locale_switched() ); + } + + public function test_is_locale_switched_original_locale() { + $original_locale = get_locale(); + + switch_to_locale( 'en_GB' ); + switch_to_locale( $original_locale ); + + $is_locale_switched = is_locale_switched(); + + restore_current_locale(); + + $this->assertTrue( $is_locale_switched ); + } + + public function test_is_locale_switched() { + switch_to_locale( 'en_GB' ); + switch_to_locale( 'nl_NL' ); + + $is_locale_switched = is_locale_switched(); + + restore_current_locale(); + + $this->assertTrue( $is_locale_switched ); + } + + public function test_switch_to_site_locale_if_user_locale_is_set() { + global $l10n, $wp_locale_switcher; + + $site_locale = get_locale(); + + $user_id = $this->factory()->user->create( array( + 'role' => 'administrator', + 'locale' => 'de_DE', + ) ); + + wp_set_current_user( $user_id ); + set_current_screen( 'dashboard' ); + + $locale_switcher = clone $wp_locale_switcher; + + $wp_locale_switcher = new WP_Locale_Switcher(); + $wp_locale_switcher->init(); + + $user_locale = get_user_locale(); + + $this->assertSame( 'de_DE', $user_locale ); + + load_default_textdomain( $user_locale ); + $language_header_before_switch = $l10n['default']->headers['Language']; // de_DE + + $locale_switched_user_locale = switch_to_locale( $user_locale ); // False. + $locale_switched_site_locale = switch_to_locale( $site_locale ); // True. + $site_locale_after_switch = get_locale(); + $language_header_after_switch = isset( $l10n['default'] ); // en_US + + restore_current_locale(); + + $language_header_after_restore = $l10n['default']->headers['Language']; // de_DE + + $wp_locale_switcher = $locale_switcher; + + set_current_screen( 'front' ); + + $this->assertFalse( $locale_switched_user_locale ); + $this->assertTrue( $locale_switched_site_locale ); + $this->assertSame( $site_locale, $site_locale_after_switch ); + $this->assertSame( 'de_DE', $language_header_before_switch ); + $this->assertFalse( $language_header_after_switch ); + $this->assertSame( 'de_DE', $language_header_after_restore ); + } + + public function test_switch_to_different_site_locale_if_user_locale_is_set() { + global $l10n, $wp_locale_switcher; + + // Change site locale to es_ES. + add_filter( 'locale', array( $this, 'filter_locale' ) ); + + $site_locale = get_locale(); + + $user_id = $this->factory()->user->create( array( + 'role' => 'administrator', + 'locale' => 'de_DE', + ) ); + + wp_set_current_user( $user_id ); + set_current_screen( 'dashboard' ); + + $locale_switcher = clone $wp_locale_switcher; + + $wp_locale_switcher = new WP_Locale_Switcher(); + $wp_locale_switcher->init(); + + $user_locale = get_user_locale(); + + $this->assertSame( 'de_DE', $user_locale ); + + load_default_textdomain( $user_locale ); + $language_header_before_switch = $l10n['default']->headers['Language']; // de_DE + + $locale_switched_user_locale = switch_to_locale( $user_locale ); // False. + $locale_switched_site_locale = switch_to_locale( $site_locale ); // True. + $site_locale_after_switch = get_locale(); + $language_header_after_switch = $l10n['default']->headers['Language']; // es_ES + + restore_current_locale(); + + $language_header_after_restore = $l10n['default']->headers['Language']; // de_DE + + $wp_locale_switcher = $locale_switcher; + + set_current_screen( 'front' ); + + remove_filter( 'locale', array( $this, 'filter_locale' ) ); + + $this->assertFalse( $locale_switched_user_locale ); + $this->assertTrue( $locale_switched_site_locale ); + $this->assertSame( $site_locale, $site_locale_after_switch ); + $this->assertSame( 'de_DE', $language_header_before_switch ); + $this->assertSame( 'es_ES', $language_header_after_switch ); + $this->assertSame( 'de_DE', $language_header_after_restore ); + } + + public function test_multiple_switches_to_site_locale_and_user_locale() { + global $wp_locale_switcher; + + $site_locale = get_locale(); + + $user_id = $this->factory()->user->create( array( + 'role' => 'administrator', + 'locale' => 'en_GB', + ) ); + + wp_set_current_user( $user_id ); + set_current_screen( 'dashboard' ); + + $locale_switcher = clone $wp_locale_switcher; + + $wp_locale_switcher = new WP_Locale_Switcher(); + $wp_locale_switcher->init(); + + $user_locale = get_user_locale(); + + load_default_textdomain( $user_locale ); + + require_once DIR_TESTDATA . '/plugins/internationalized-plugin.php'; + + switch_to_locale( 'de_DE' ); + switch_to_locale( $site_locale ); + + $expected = i18n_plugin_test(); + + restore_current_locale(); + + $wp_locale_switcher = $locale_switcher; + + set_current_screen( 'front' ); + + $this->assertSame( 'en_US', get_locale() ); + $this->assertSame( 'This is a dummy plugin', $expected ); + } + + public function filter_locale() { + return 'es_ES'; + } +} diff --git a/tests/phpunit/tests/locale.php b/tests/phpunit/tests/locale.php index 1c335f4094..79372d618f 100644 --- a/tests/phpunit/tests/locale.php +++ b/tests/phpunit/tests/locale.php @@ -1,7 +1,7 @@