Make WordPress Core

Changeset 49236

Timestamp:
10/20/2020 04:03:58 PM (4 years ago)
Author:
ocean90
Message:

I18N: Introduce WP_Textdomain_Registry to store text domains and their language directory paths.

Previously, when using switch_to_locale() all current loaded text domains were unloaded and added to the $l10n_unloaded global. This prevented the just-in-time loading for text domains after a switch. The just-in-time loading was also only possible if the translations were stored in WP_LANG_DIR. Both issues have been fixed.

  • Adds WP_Textdomain_Registry to keep track of the language directory paths for all plugins and themes.
  • Updates all load_*_textdomain() functions to store the path in WP_Textdomain_Registry.
  • Adds $reloadable parameter to unload_textdomain() to define whether a text domain can be loaded just-in-time again. This is used by WP_Locale_Switcher::load_translations().
  • Extends _load_textdomain_just_in_time() to also support text domains of plugins and themes with custom language directories.
  • Fixes the incorrect test_plugin_translation_after_switching_locale_twice() test which should have catch this issue earlier.
  • Adds a new test plugin/theme to test the loading of translations with a custom language directory.
  • Deprecates the now unused and private _get_path_to_translation() and _get_path_to_translation_from_lang_dir() functions.

Props yoavf, swissspidy, dd32, ocean90.
See #26511.
Fixes #39210.

Location:
trunk
Files:
19 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-locale-switcher.php

    r46586 r49236  
    197197
    198198        foreach ( $domains as $domain ) {
     199
    199200            if ( 'default' === $domain ) {
    200201                continue;
    201202            }
    202203
    203             unload_textdomain( $domain );
     204            unload_textdomain( $domain );
    204205            get_translations_for_domain( $domain );
    205206        }
     
    219220     */
    220221    private function change_locale( $locale ) {
    221         // Reset translation availability information.
    222         _get_path_to_translation( null, true );
     222        global $wp_locale;
    223223
    224224        $this->load_translations( $locale );
    225225
    226         $GLOBALS['wp_locale'] = new WP_Locale();
     226        $ = new WP_Locale();
    227227
    228228        /**
  • trunk/src/wp-includes/deprecated.php

    r49197 r49236  
    41354135    return is_string( $value ) ? addslashes( $value ) : $value;
    41364136}
     4137
     4138
     4139
     4140
     4141
     4142
     4143
     4144
     4145
     4146
     4147
     4148
     4149
     4150
     4151
     4152
     4153
     4154
     4155
     4156
     4157
     4158
     4159
     4160
     4161
     4162
     4163
     4164
     4165
     4166
     4167
     4168
     4169
     4170
     4171
     4172
     4173
     4174
     4175
     4176
     4177
     4178
     4179
     4180
     4181
     4182
     4183
     4184
     4185
     4186
     4187
     4188
     4189
     4190
     4191
     4192
     4193
     4194
     4195
     4196
     4197
     4198
     4199
     4200
     4201
     4202
     4203
     4204
     4205
     4206
     4207
     4208
     4209
     4210
     4211
     4212
     4213
     4214
     4215
     4216
     4217
     4218
  • trunk/src/wp-includes/l10n.php

    r49193 r49236  
    690690 * @since 1.5.0
    691691 *
    692  * @global MO[] $l10n          An array of all currently loaded text domains.
    693  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
     692 * @global MO[]                   $l10n                   An array of all currently loaded text domains.
     693 * @global MO[]                   $l10n_unloaded          An array of all text domains that have been unloaded again.
     694 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
    694695 *
    695696 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     
    698699 */
    699700function load_textdomain( $domain, $mofile ) {
    700     global $l10n, $l10n_unloaded;
     701    global $l10n, $l10n_unloaded;
    701702
    702703    $l10n_unloaded = (array) $l10n_unloaded;
     
    756757    $l10n[ $domain ] = &$mo;
    757758
     759
     760
     761
    758762    return true;
    759763}
     
    763767 *
    764768 * @since 3.0.0
     769
    765770 *
    766771 * @global MO[] $l10n          An array of all currently loaded text domains.
    767772 * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
    768773 *
    769  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     774 * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
     775 * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
    770776 * @return bool Whether textdomain was unloaded.
    771777 */
    772 function unload_textdomain( $domain ) {
     778function unload_textdomain( $domain ) {
    773779    global $l10n, $l10n_unloaded;
    774780
     
    779785     *
    780786     * @since 3.0.0
    781      *
    782      * @param bool   $override Whether to override the text domain unloading. Default false.
    783      * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
    784      */
    785     $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain );
     787     * @since 5.6.0 Added the `$reloadable` parameter.
     788     *
     789     * @param bool   $override   Whether to override the text domain unloading. Default false.
     790     * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
     791     * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
     792     */
     793    $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain, $reloadable );
    786794
    787795    if ( $plugin_override ) {
    788         $l10n_unloaded[ $domain ] = true;
     796        if ( ! $reloadable ) {
     797            $l10n_unloaded[ $domain ] = true;
     798        }
    789799
    790800        return true;
     
    795805     *
    796806     * @since 3.0.0
    797      *
    798      * @param string $domain Text domain. Unique identifier for retrieving translated strings.
    799      */
    800     do_action( 'unload_textdomain', $domain );
     807     * @since 5.6.0 Added the `$reloadable` parameter.
     808     *
     809     * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
     810     * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
     811     */
     812    do_action( 'unload_textdomain', $domain, $reloadable );
    801813
    802814    if ( isset( $l10n[ $domain ] ) ) {
    803815        unset( $l10n[ $domain ] );
    804816
    805         $l10n_unloaded[ $domain ] = true;
     817        if ( ! $reloadable ) {
     818            $l10n_unloaded[ $domain ] = true;
     819        }
    806820
    807821        return true;
     
    868882 */
    869883function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
     884
     885
    870886    /**
    871887     * Filters a plugin's locale.
     
    894910    }
    895911
     912
     913
     914
    896915    return load_textdomain( $domain, $path . '/' . $mofile );
    897916}
     
    902921 * @since 3.0.0
    903922 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
     923
     924
    904925 *
    905926 * @param string $domain             Text domain. Unique identifier for retrieving translated strings.
     
    909930 */
    910931function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
     932
     933
    911934    /** This filter is documented in wp-includes/l10n.php */
    912935    $locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
     
    921944    $path = WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' );
    922945
     946
     947
     948
    923949    return load_textdomain( $domain, $path . '/' . $mofile );
    924950}
     
    934960 * @since 1.5.0
    935961 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
     962
     963
    936964 *
    937965 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     
    941969 */
    942970function load_theme_textdomain( $domain, $path = false ) {
     971
     972
    943973    /**
    944974     * Filters a theme's locale.
     
    961991        $path = get_template_directory();
    962992    }
     993
     994
     995
    963996
    964997    return load_textdomain( $domain, $path . '/' . $locale . '.mo' );
     
    11911224 * @access private
    11921225 *
    1193  * @see get_translations_for_domain()
    1194  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
     1226 * @
     1227 * @global .
    11951228 *
    11961229 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     
    11981231 */
    11991232function _load_textdomain_just_in_time( $domain ) {
    1200     global $l10n_unloaded;
     1233    global $l10n_unloaded;
    12011234
    12021235    $l10n_unloaded = (array) $l10n_unloaded;
     
    12071240    }
    12081241
    1209     $translation_path = _get_path_to_translation( $domain );
    1210     if ( false === $translation_path ) {
     1242    /** @var WP_Textdomain_Registry $wp_textdomain_registry */
     1243    $path = $wp_textdomain_registry->get( $domain );
     1244    if ( ! $path ) {
    12111245        return false;
    12121246    }
    12131247
    1214     return load_textdomain( $domain, $translation_path );
    1215 }
    1216 
    1217 /**
    1218  * Gets the path to a translation file for loading a textdomain just in time.
    1219  *
    1220  * Caches the retrieved results internally.
    1221  *
    1222  * @since 4.7.0
    1223  * @access private
    1224  *
    1225  * @see _load_textdomain_just_in_time()
    1226  *
    1227  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
    1228  * @param bool   $reset  Whether to reset the internal cache. Used by the switch to locale functionality.
    1229  * @return string|false The path to the translation file or false if no translation file was found.
    1230  */
    1231 function _get_path_to_translation( $domain, $reset = false ) {
    1232     static $available_translations = array();
    1233 
    1234     if ( true === $reset ) {
    1235         $available_translations = array();
    1236     }
    1237 
    1238     if ( ! isset( $available_translations[ $domain ] ) ) {
    1239         $available_translations[ $domain ] = _get_path_to_translation_from_lang_dir( $domain );
    1240     }
    1241 
    1242     return $available_translations[ $domain ];
    1243 }
    1244 
    1245 /**
    1246  * Gets the path to a translation file in the languages directory for the current locale.
    1247  *
    1248  * Holds a cached list of available .mo files to improve performance.
    1249  *
    1250  * @since 4.7.0
    1251  * @access private
    1252  *
    1253  * @see _get_path_to_translation()
    1254  *
    1255  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
    1256  * @return string|false The path to the translation file or false if no translation file was found.
    1257  */
    1258 function _get_path_to_translation_from_lang_dir( $domain ) {
    1259     static $cached_mofiles = null;
    1260 
    1261     if ( null === $cached_mofiles ) {
    1262         $cached_mofiles = array();
    1263 
    1264         $locations = array(
    1265             WP_LANG_DIR . '/plugins',
    1266             WP_LANG_DIR . '/themes',
    1267         );
    1268 
    1269         foreach ( $locations as $location ) {
    1270             $mofiles = glob( $location . '/*.mo' );
    1271             if ( $mofiles ) {
    1272                 $cached_mofiles = array_merge( $cached_mofiles, $mofiles );
    1273             }
    1274         }
    1275     }
    1276 
    12771248    $locale = determine_locale();
    1278     $mofile = "{$domain}-{$locale}.mo";
    1279 
    1280     $path = WP_LANG_DIR . '/plugins/' . $mofile;
    1281     if ( in_array( $path, $cached_mofiles, true ) ) {
    1282         return $path;
    1283     }
    1284 
    1285     $path = WP_LANG_DIR . '/themes/' . $mofile;
    1286     if ( in_array( $path, $cached_mofiles, true ) ) {
    1287         return $path;
    1288     }
    1289 
    1290     return false;
     1249
     1250    // Themes with their language directory outside of WP_LANG_DIR have a different file name.
     1251    $template_directory   = trailingslashit( get_template_directory() );
     1252    $stylesheet_directory = trailingslashit( get_stylesheet_directory() );
     1253    if ( 0 === strpos( $path, $template_directory ) || 0 === strpos( $path, $stylesheet_directory ) ) {
     1254        $mofile = "{$path}{$locale}.mo";
     1255    } else {
     1256        $mofile = "{$path}{$domain}-{$locale}.mo";
     1257    }
     1258
     1259    return load_textdomain( $domain, $mofile );
    12911260}
    12921261
     
    12981267 * @since 2.8.0
    12991268 *
    1300  * @global MO[] $l10n
     1269 * @global MO[] $l10n
    13011270 *
    13021271 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     
    13221291 * @since 3.0.0
    13231292 *
    1324  * @global MO[] $l10n
     1293 * @global MO[] $l10n
    13251294 *
    13261295 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  • trunk/src/wp-settings.php

    r49226 r49236  
    153153// Load the L10n library.
    154154require_once ABSPATH . WPINC . '/l10n.php';
     155
    155156require_once ABSPATH . WPINC . '/class-wp-locale.php';
    156157require_once ABSPATH . WPINC . '/class-wp-locale-switcher.php';
     
    302303$GLOBALS['wp_embed'] = new WP_Embed();
    303304
     305
     306
     307
     308
     309
     310
     311
     312
     313
     314
     315
    304316// Load multisite-specific files.
    305317if ( is_multisite() ) {
  • trunk/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.po

    r48930 r49236  
    33"Project-Id-Version: \n"
    44"POT-Creation-Date: 2015-12-31 16:31+0100\n"
    5 "PO-Revision-Date: 2016-10-26 00:02+0200\n"
     5"PO-Revision-Date: 20+0200\n"
    66"Language: de_DE\n"
    77"MIME-Version: 1.0\n"
    88"Content-Type: text/plain; charset=UTF-8\n"
    99"Content-Transfer-Encoding: 8bit\n"
    10 "X-Generator: Poedit 1.8.10\n"
     10"X-Generator: Poedit \n"
    1111"X-Poedit-Basepath: .\n"
    1212"Plural-Forms: nplurals=2; plural=(n != 1);\n"
     
    1515"esc_html_x:1,2c\n"
    1616"X-Textdomain-Support: yes\n"
     17
     18
    1719"X-Poedit-SearchPath-0: .\n"
    1820
  • trunk/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.po

    r48930 r49236  
    33"Project-Id-Version: \n"
    44"POT-Creation-Date: 2015-12-31 16:38+0100\n"
    5 "PO-Revision-Date: 2016-10-26 00:02+0200\n"
     5"PO-Revision-Date: 20+0200\n"
    66"Language: de_DE\n"
    77"MIME-Version: 1.0\n"
    88"Content-Type: text/plain; charset=UTF-8\n"
    99"Content-Transfer-Encoding: 8bit\n"
    10 "X-Generator: Poedit 1.8.10\n"
     10"X-Generator: Poedit \n"
    1111"X-Poedit-Basepath: .\n"
    1212"Plural-Forms: nplurals=2; plural=(n != 1);\n"
     
    1515"esc_html_x:1,2c\n"
    1616"X-Textdomain-Support: yes\n"
     17
     18
    1719"X-Poedit-SearchPath-0: .\n"
    1820
  • trunk/tests/phpunit/data/plugins/hello.php

    r46586 r49236  
    55Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
    66Author: Matt Mullenweg
    7 Version: 1.5.1
     7Version: 1.
    88Author URI: http://ma.tt/
    99Text Domain: hello-dolly
    1010
    1111*/
    12 
    13 // Test for
    14 ?>
  • trunk/tests/phpunit/includes/bootstrap.php

    r49226 r49236  
    7777define( 'DIR_TESTROOT', realpath( dirname( __DIR__ ) ) );
    7878
    79 define( 'WP_LANG_DIR', DIR_TESTDATA . '/languages' );
     79define( 'WP_LANG_DIR', realpath( DIR_TESTDATA . '/languages' ) );
     80define( 'WP_PLUGIN_DIR', realpath( DIR_TESTDATA . '/plugins' ) );
    8081
    8182if ( ! defined( 'WP_TESTS_FORCE_KNOWN_BUGS' ) ) {
  • trunk/tests/phpunit/tests/admin/includesPlugin.php

    r49112 r49236  
    1515            'Author'      => '<a href="http://ma.tt/">Matt Mullenweg</a>',
    1616            'AuthorURI'   => 'http://ma.tt/',
    17             'Version'     => '1.5.1',
     17            'Version'     => '1.',
    1818            'TextDomain'  => 'hello-dolly',
    1919            'DomainPath'  => '',
  • trunk/tests/phpunit/tests/l10n/loadTextdomain.php

    r46586 r49236  
    2525        add_filter( 'plugin_locale', array( $this, 'store_locale' ) );
    2626        add_filter( 'theme_locale', array( $this, 'store_locale' ) );
     27
     28
     29
     30
     31
    2732    }
    2833
     
    3136        remove_filter( 'theme_locale', array( $this, 'store_locale' ) );
    3237
     38
     39
     40
     41
     42
    3343        parent::tearDown();
    3444    }
     
    117127     * @ticket 21319
    118128     */
    119     function test_is_textdomain_is_not_loaded_after_gettext_call_with_no_translations() {
     129    function test_is_textdomain_is_not_loaded_after_gettext_call_with_no_translations() {
    120130        $this->assertFalse( is_textdomain_loaded( 'wp-tests-domain' ) );
    121131        __( 'just some string', 'wp-tests-domain' );
     
    123133    }
    124134
    125     function test_override_load_textdomain_noop() {
     135    function test_override_load_textdomain_noop() {
    126136        add_filter( 'override_load_textdomain', '__return_true' );
    127137        $load_textdomain = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/non-existent-file' );
     
    132142    }
    133143
    134     function test_override_load_textdomain_non_existent_mofile() {
     144    function test_override_load_textdomain_non_existent_mofile() {
    135145        add_filter( 'override_load_textdomain', array( $this, '_override_load_textdomain_filter' ), 10, 3 );
    136146        $load_textdomain = load_textdomain( 'wp-tests-domain', WP_LANG_DIR . '/non-existent-file.mo' );
     
    146156    }
    147157
    148     function test_override_load_textdomain_custom_mofile() {
     158    function test_override_load_textdomain_custom_mofile() {
    149159        add_filter( 'override_load_textdomain', array( $this, '_override_load_textdomain_filter' ), 10, 3 );
    150160        $load_textdomain = load_textdomain( 'wp-tests-domain', WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo' );
     
    166176     * @return bool
    167177     */
    168     function _override_load_textdomain_filter( $override, $domain, $file ) {
     178    function _override_load_textdomain_filter( $override, $domain, $file ) {
    169179        global $l10n;
    170180
  • trunk/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php

    r47200 r49236  
    3333        add_filter( 'template_root', array( $this, 'filter_theme_root' ) );
    3434        wp_clean_themes_cache();
    35         unset( $GLOBALS['wp_themes'] );
    36         unset( $GLOBALS['l10n'] );
    37         unset( $GLOBALS['l10n_unloaded'] );
    38         _get_path_to_translation( null, true );
     35        unset( $GLOBALS['wp_themes'], $GLOBALS['l10n'], $GLOBALS['l10n_unloaded'] );
     36
     37        /** @var WP_Textdomain_Registry $wp_textdomain_registry */
     38        global $wp_textdomain_registry;
     39
     40        $wp_textdomain_registry->reset();
    3941    }
    4042
     
    4547        remove_filter( 'template_root', array( $this, 'filter_theme_root' ) );
    4648        wp_clean_themes_cache();
    47         unset( $GLOBALS['wp_themes'] );
    48         unset( $GLOBALS['l10n'] );
    49         unset( $GLOBALS['l10n_unloaded'] );
    50         _get_path_to_translation( null, true );
     49        unset( $GLOBALS['wp_themes'], $GLOBALS['l10n'], $GLOBALS['l10n_unloaded'] );
     50
     51        /** @var WP_Textdomain_Registry $wp_textdomain_registry */
     52        global $wp_textdomain_registry;
     53
     54        $wp_textdomain_registry->reset();
    5155
    5256        parent::tearDown();
     
    171175    /**
    172176     * @ticket 37997
     177
    173178     */
    174179    public function test_plugin_translation_after_switching_locale_twice() {
     
    184189
    185190        $this->assertSame( 'Das ist ein Dummy Plugin', $expected_de_de );
    186         $this->assertSame( 'This is a dummy plugin', $expected_es_es );
     191        $this->assertSame( '', $expected_es_es );
    187192    }
    188193
  • trunk/tests/phpunit/tests/l10n/localeSwitcher.php

    r48939 r49236  
    2323        $this->previous_locale = '';
    2424
    25         unset( $GLOBALS['l10n'] );
    26         unset( $GLOBALS['l10n_unloaded'] );
    27         _get_path_to_translation( null, true );
     25        unset( $GLOBALS['l10n'], $GLOBALS['l10n_unloaded'] );
     26
     27        /** @var WP_Textdomain_Registry $wp_textdomain_registry */
     28        global $wp_textdomain_registry;
     29
     30        $wp_textdomain_registry->reset();
    2831    }
    2932
    3033    public function tearDown() {
    31         unset( $GLOBALS['l10n'] );
    32         unset( $GLOBALS['l10n_unloaded'] );
    33         _get_path_to_translation( null, true );
     34        unset( $GLOBALS['l10n'], $GLOBALS['l10n_unloaded'] );
     35
     36        /** @var WP_Textdomain_Registry $wp_textdomain_registry */
     37        global $wp_textdomain_registry;
     38
     39        $wp_textdomain_registry->reset();
    3440
    3541        parent::tearDown();
     
    389395    }
    390396
     397
     398
     399
     400
     401
     402
     403
     404
     405
     406
     407
     408
     409
     410
     411
     412
     413
     414
     415
     416
     417
     418
     419
     420
     421
     422
     423
     424
     425
     426
     427
     428
     429
     430
     431
     432
     433
     434
     435
     436
     437
     438
     439
     440
     441
     442
     443
     444
     445
     446
     447
     448
     449
     450
     451
     452
     453
     454
     455
     456
     457
     458
     459
     460
     461
     462
     463
     464
     465
     466
     467
    391468    public function filter_locale() {
    392469        return 'es_ES';
  • trunk/tests/phpunit/tests/theme/themeDir.php

    r48937 r49236  
    163163            'Theme with Spaces in the Directory',
    164164            'Internationalized Theme',
     165
    165166            'camelCase',
    166167            'REST Theme',
Note: See TracChangeset for help on using the changeset viewer.