I18N Improvements in 6.5 (Performant Translations)

Various internationalization (i18n) improvements are in WordPress 6.5, and this developers note will focus on these.

New localization system with improved performance

Over the past year, WordPress contributors have meticulously analyzed performance of the existing i18ni18n Internationalization, or the act of writing and preparing code to be fully translatable into other languages. Also see localization. Often written with a lowercase i so it is not confused with a lowercase L or the numeral 1. Often an acquired skill. system in WordPress and ultimately created a new Performant Translations feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins. that provided a completely overhauled system with significantly better performance. After thousands of betaBeta A pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. testers and a merge announcement late last year, this new library is now included in WordPress 6.5! See #59656 for all the details.

The Performant Translations pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party is still useful and will continue to be maintained to build on top of the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. solution with a distinct additional feature. As is already the case today, the plugin will automatically convert any .mo files to PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher files if a PHP file does not currently exist. This is useful for sites where translations are not coming from translate.wordpress.org or only exist locally on that server.

This new library is faster at loading binary .mo files and uses less memory. It even supports loading multiple locales at the same time, which makes locale switching faster. In addition to that, it supports translations contained in PHP files, avoiding a binary file format and leveraging OPCache if available.

The new library is so fast, in fact, that it paves the way for the Preferred Languages feature plugin to merge translations of multiple locales by default starting with WordPress 6.5.

While this is in large part a silent and backward-compatible under-the-hood change, there are still a few things to be aware of:

New .l10n.php translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. file format

When downloading language packs from WordPress.orgWordPress.org The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/, there will be a new .l10n.php file in addition to the .mo and .po files you are already familiar with. If an .mo translation file has a corresponding .l10n.php file, the latter will be loaded instead, making things even faster and use even less memory.

This is a progressive enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature., so if there’s only an .mo file but no PHP file, translations will still be loaded as expected. However, the opposite is also true! So you can theoretically use only .l10n.php translation files in your project and features such as the just-in-time translation loading continue to work. Right now, WordPress still expects corresponding .po and .mo files for things like update checks. However, this limitation will be addressed in the future, see #60554 for more information.

Note: if you don’t see any .l10n.php translation files in wp-content/languages yet, it might be that the language pack hasn’t been updated in a while, i.e. there were no new translations.

Here’s an example of a PHP translation file as supported by WordPress 6.5:

<?php
return [
'project-id-version' => 'WordPress - 6.5.x - Development',
'report-msgid-bugs-to' => 'polyglots@example.com',
'messages' =>
[
'original' => 'translation',
'contextEOToriginal with context' => 'translation with context',
'plural0' => 'translation0' . "\0" . 'translation1',
'contextEOTplural0 with context' => 'translation0 with context' . "\0" . 'translation1 with context',
'Product' => 'Produkt' . "\0" . 'Produkte',
],
];

Note: EOT here stands for the “End of Transmission” character (U+0004, or "\4" in PHP). It’s the same delimiter as in gettext used to glue the context with the singular string.

Generating PHP translation files

If you would like to generate these PHP translation files yourself, version 4.0 of GlotPress, the plugin that powers translate.WordPress.org, already supports the new .l10n.php format.

In addition to that, WP-CLI 2.10.0 (i18n-command 2.6.0) provides a new wp i18n make-php command to create these PHP files from a given .po file. Examples:

# Create PHP files for all PO files in the current directory.
$ wp i18n make-php .

# Create a PHP file from a single PO file in a specific directory.
$ wp i18n make-php example-plugin-de_DE.po languages

If you are developing a WordPress plugin that deals with translations, you can also use the new WP_Translation_File class to convert an .mo file into a PHP file. Example:

$contents = WP_Translation_File::transform( $mofile, 'php' );
if ( $contents ) {
file_put_contents( $path_to_php_file, $contents );
}

New filters to customize this behavior

If you would like to disable the support for PHP files for some reason; for example, if you don’t have any yet in your project and want to prevent the extra file lookup operation, you can use the new translation_file_format filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. to change the preferred format (default is php) like so:

add_filter(
'translation_file_format',
static function () {
return 'mo';
}
);

The existing load_textdomain_mofile filter can still be used to filter the .mo file path for loading translations for a specific text domain. However, it only works for .mo files. To filter the path for a translation file, be it a .l10n.php or a .mo file, use the new load_translation_file filter.

Working with the $l10n global variable

Previously, when loading translations, WordPress would store an instance of the MO class in the $l10n global variable. With WordPress 6.5, this will be an instance of a new WP_Translations class that acts as a shim with similar characteristics. If your project directly works with this global variable or the MO class in some way, this is an area to keep an eye on.

Cached list of language file paths

This another slight performance improvement but unrelated to the new localization library covered above.

In places such as get_available_languages() and WP_Textdomain_Registry, WordPress used to directly use the glob() function to retrieve all .mo files in a specific directory. This is important for just-in-time translation loading and generally knowing which translations are installed. However, on sites with a large number of language files, the glob() operation can become expensive.

Because of this, a new caching mechanism was introduced in #58919 / [57287]. The file lookup is now handled centrally in WP_Textdomain_Registry and stored in the object cache in the translations group, with the cache key having the format cached_mo_files_<hash>, where <hash> is the MD5 hash of the scanned directory, e.g. wp-content/languages. The cache is cleared whenever language packs are updated.

Also, the lookup now also scans for .l10n.php files in addition to .mo files, in case only the former exist on a site.

More questions? Please let us know

If you have any questions, please leave a comment below or file a new ticket on Trac under the I18N component if you’ve encountered any bugs.

Props to @joemcgill, @stevenlinx for review.

#6-5, #dev-notes, #dev-notes-6-5, #i18n