Make WordPress Core


Ignore:
Timestamp:
01/23/2024 01:32:34 PM (23 months ago)
Author:
swissspidy
Message:

I18N: Introduce a more performant localization library.

This introduces a more lightweight library for loading .mo translation files which offers increased speed and lower memory usage.
It also supports loading multiple locales at the same time, which makes locale switching faster too.

For plugins interacting with the $l10n global variable in core, a shim is added to retain backward compatibility with the existing pomo library.

In addition to that, this library supports translations contained in PHP files, avoiding a binary file format and leveraging OPCache if available.
If an .mo translation file has a corresponding .l10n.php file, the latter will be loaded instead.
This behavior can be adjusted using the new translation_file_format and load_translation_file filters.

PHP translation files will be typically created by downloading language packs, but can also be generated by plugins.
See https://make.wordpress.org/core/2023/11/08/merging-performant-translations-into-core/ for more context.

Props dd32, swissspidy, flixos90, joemcgill, westonruter, akirk, SergeyBiryukov.
Fixes #59656.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/l10n.php

    r57287 r57337  
    798798    }
    799799
    800     $mo = new MO();
    801     if ( ! $mo->import_from_file( $mofile ) ) {
    802         $wp_textdomain_registry->set( $domain, $locale, false );
    803 
    804         return false;
    805     }
    806 
    807     if ( isset( $l10n[ $domain ] ) ) {
    808         $mo->merge_with( $l10n[ $domain ] );
    809     }
    810 
    811     unset( $l10n_unloaded[ $domain ] );
    812 
    813     $l10n[ $domain ] = &$mo;
    814 
    815     $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) );
     800    $i18n_controller = WP_Translation_Controller::instance();
     801
     802    // Ensures the correct locale is set as the current one, in case it was filtered.
     803    $i18n_controller->set_locale( $locale );
     804
     805    /**
     806     * Filters the preferred file format for translation files.
     807     *
     808     * Can be used to disable the use of PHP files for translations.
     809     *
     810     * @since 6.5.0
     811     *
     812     * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'.
     813     * @param string $domain           The text domain.
     814     */
     815    $preferred_format = apply_filters( 'translation_file_format', 'php', $domain );
     816    if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) {
     817        $preferred_format = 'php';
     818    }
     819
     820    $translation_files = array( $mofile );
     821    if ( 'mo' !== $preferred_format ) {
     822        array_unshift(
     823            $translation_files,
     824            substr_replace( $mofile, '.l10n.', - strlen( $preferred_format ) )
     825        );
     826    }
     827
     828    foreach ( $translation_files as $file ) {
     829        /**
     830         * Filters the file path for loading translations for the given text domain.
     831         *
     832         * Similar to the {@see 'load_textdomain_mofile'} filter with the difference that
     833         * the file path could be for an MO or PHP file.
     834         *
     835         * @since 6.5.0
     836         *
     837         * @param string $file   Path to the translation file to load.
     838         * @param string $domain The text domain.
     839         */
     840        $file = (string) apply_filters( 'load_translation_file', $file, $domain );
     841
     842        $success = $i18n_controller->load_file( $file, $domain, $locale );
     843
     844        if ( $success ) {
     845            if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) {
     846                $i18n_controller->load_file( $l10n[ $domain ]->get_filename(), $domain, $locale );
     847            }
     848
     849            // Unset NOOP_Translations reference in get_translations_for_domain().
     850            unset( $l10n[ $domain ] );
     851
     852            $l10n[ $domain ] = new WP_Translations( $i18n_controller, $domain );
     853
     854            $wp_textdomain_registry->set( $domain, $locale, dirname( $file ) );
     855
     856            return true;
     857        }
     858    }
    816859
    817860    return true;
     
    867910    do_action( 'unload_textdomain', $domain, $reloadable );
    868911
     912    // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded.
     913    if ( ! $reloadable ) {
     914        WP_Translation_Controller::instance()->unload_textdomain( $domain );
     915    }
     916
    869917    if ( isset( $l10n[ $domain ] ) ) {
    870918        if ( $l10n[ $domain ] instanceof NOOP_Translations ) {
     
    905953
    906954    // Unload previously loaded strings so we can switch translations.
    907     unload_textdomain( 'default' );
     955    unload_textdomain( 'default', true );
    908956
    909957    $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo", $locale );
Note: See TracChangeset for help on using the changeset viewer.