The WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. development team builds WordPress! Follow this site for general updates, status reports, and the occasional code debate. There’s lots of ways to contribute:
Found a bugbugA bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority.?Create a ticket in the bug tracker.
The coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. performance team recently conducted an in-depth i18n performance analysis. It showed that localized WordPress sites load significantly slower than a site without translations. The blogblog(versus network, site) post presented and compared multiple solutions to this problem, and now the team would like to test the most promising approach at a wider scale using a dedicated pluginPluginA 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.
Introducing the Performant Translations plugin
What it does
The Performant Translations plugin uses a new approach to handle translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. files in WordPress, making localization blazing fast. The primary purpose of this plugin is to allow broader testing of these enhancements, for which the goal is to eventually land in WordPress core.
This plugin helps to make localized WordPress sites faster by replacing the traditional MO translation files with PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher files, which are much faster to parse. Plus, PHP files can be stored in the so-called OPcache, which provides an additional speed boost.
If your site is using a language other than English (US), you should see immediate speed improvements simply by activating this plugin. No further action is required.
The Performant Translations plugin is available for download on WordPress.orgWordPress.orgThe 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/ or directly from your WordPress adminadmin(and super admin).
Since the Performant Translations plugin requires no configuration, all that’s needed to benefit from its speed improvements is to activate the plugin.
To verify that something has changed, you could use a tool like Query Monitor or an external tool for testing server response times. In Query Monitor, the page load time and memory usage should drop quite a bit after plugin activation:
Page load time and memory usage beforePage load time and memory usage after
In Query Monitor you will also see how translations are loaded from PHP files from now on:
While the plugin is mostly considered to be a betaBetaA 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. testing plugin, it has been tested and established to a degree where it should be okay to use in production. Still, as with every plugin, you are doing so at your own risk.
It’s also worth noting that the plugin has been successfully tested with common multilingual plugins, such as WPML, Weglot, TranslatePress, MultilingualPress, and Polylang. It also works fine with Loco Translate and the Preferred Languagesfeature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins..
Should you choose to stop testing the Performant Translation plugins, uninstalling it will remove all of its traces.
The performance team’s goal is to get as much feedback as possible and further refine the approach so that it can ultimately be proposed to be merged into WordPress core 6.5. That means testing will last for a few months at least.
A recent in-depth performance analysis of WordPress core showed that loading translations had a significant hit on a site’s server response time. Given that more than half of all WordPress sites use a language other than English (US), the performance team identified this as an area worth looking into more closely. The team spent the last couple of months exploring this in more detail and the results are now shared in this blogblog(versus network, site) post.
This is merely an analysis of the current i18ni18nInternationalization, 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 with some proposed under-the-hood performance improvements. No decisions have been made on any of these proposals.
Context
Initial benchmarks showed that the median loading time for a localized site can be up to 50% slower than for non-localized sites, depending on which themes and plugins are being used. This was measured using both the wpp-research CLI tool and also a dedicated benchmark environment (as elaborated in the Comparison section towards the end).
The WordPress i18n system is based on gettext, which uses source .po (Portable Object) files and binary .mo (Machine Object) files for storing and loading translations. It is not using the C gettext APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. itself but a custom userland implementation that works without any external dependencies.
In addition to coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. itself, each pluginPluginA 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 and theme has its own translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. file, which has to be loaded and parsed on every request. Loading and parsing all these translation files is an expensive task.
In the past, various solutions have been discussed and explored to improve the i18n performance of WordPress. A non-exhaustive list:
Use a more lightweight MO parser
Improve translation lookups by using the hash map in MO files (e.g. with DynaMo)
Caching translations in the object cache
Caching translations in APCu (an in-memory key-value store for PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher)
Other more elaborated forms of caching (e.g. per request)
Using the native PHP gettext extension
Use a custom PHP extension to handle the MO file parsing)
Using lazily evaluated translation calls (see #41305 for details)
Using a different file format than .mo files, e.g. plain .php files
For this analysis, many of these solutions were looked at, focusing on their advantages and disadvantages. At the end of this post there is a comparison table with some much needed numbers as well, based on custom-built benchmarks.
Solutions
Solution A: Use different file format
Use a different file format for translations instead of .mo files to avoid the overhead of loading and parsing binary files.
Design considerations
With this solution, translations will be stored in plain .php files returning an associative array of translation strings. Whenever a .php file is available, it will be preferred over the .mo file, which is still used as a fallback. The rest of the architecture remains the same.
When a localized WordPress site downloads language packs from the translate.wordpress.org translation platform, it downloads .po and .mo files containing all the translations. This will be modified to include .php files. GlotPress, which the platform is built on, will be updated to support this new output format. Additionally, WordPress core itself could be modified to generate PHP files whenever they are missing.
In theory, nothing is faster in PHP than loading and executing another PHP file. .json, .ini, or .xml would all be much slower.
Initial benchmarks show consistent significant performance improvements
Relatively trivial to implement
Maintains backward compatibility thanks to graceful fallback
Makes it easier for users to inspect and change translations (no need to compile .po to .mo)
Avoids loading and parsing binary .mo files, which is the main bottleneck
Lets PHP store translations in OPcache for an additional performance benefit
Battle-tested approach in the PHP ecosystem (for example in Laravel)
Caveats and risks
Requires not only changes to WordPress core, but also tools like GlotPress and WP-CLIWP-CLIWP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is http://wp-cli.org/https://make.wordpress.org/cli/
Adds maintenance overhead by introducing a new file format on top of the existing one
As shown by the proof of concept, the overhead is minimal
In the long term, .mo support could be deprecated
Security considerations due to essentially executing remotely fetched PHP files
Not really different from downloading plugins/themes from WordPress.org
WordPress considers translations to be trusted
Hosting providers could be blocking PHP execution in wp-content/languages
Could potentially use checksum verifications or static analysis at install time to detect anomalies
Effort and timeline
The proof of concept using PHP files is in a very solid state already. There are also examples for changes to WP-CLI (PR) and GlotPress (PR). This makes it suitable for a feature project to expand testing with very little effort required. Even a core merge would be very straightforward in a relatively short time, potentially already in Q4 2023. The security aspect when using PHP files could be a potential blockerblockerA bug which is so severe that it blocks a release., so it’s important to loopLoopThe Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop. in the WordPress security team and hosting providers early on.
More time is required to test other file formats and compare results.
Solution B: Native gettext extension
Use the native gettext PHP extension written in C when available, instead of the custom built-in parser in WordPress.
Design considerations
WordPress has always used a custom MO file parser, because the native gettext extension is not necessarily available on the server. With this solution, the existing system is adapted to use the extension whenever available and falling back to the custom implementation if not.
This has been previously explored in #17268 and implemented in WP Performance Pack and Native Gettext. These implementations can serve as inspiration for the initial design. They all work similarly in that they symlink or copy the translation files to a new directory structure that is compatible with the gettext extension.
As of July 2023, around 66% of all localized WordPress sites have the gettext extension installed, according to information from the WordPress update requests.
Benefits
Significant performance improvements for eligible sites
Initial benchmarks show that loading time and memory usage basically do not differ from non-localized sites
Caveats and risks
The gettext extension is not commonly available
Smaller incentive to implement and lower impact overall
Requires locales to be installed on the server
Servers rarely have many installed locales
Locales often need to be compiled first and take up a lot of space
WordPress on the other hand supports over 200 locales
Potential clashes with the custom locales WordPress supports
For example, locales like pt_PT_ao90, de_DE_formal or roh might not even be supported
Outreach to hosting providers would be necessary
Adds maintenance overhead by essentially adding a second gettext implementation
Poor API
Requires setting environment variables (such as LC_MESSAGES and LANGUAGE), which might not be possible or cause conflicts on certain servers/sites
Requires symlinks or hard file copies
Symlinks might not be possible on the server; copying all translation files means doubling disk usage
Translation files are cached by PHP, thus any translation change requires restarting the web server
There are workarounds such as cache busting using random file names or fstat, however they might not work on all environments
Has not been tested on a wider scale, despite being discussed for years
While there are existing implementations that could be leveraged for this solution, further field testing is required to assess whether the extension actually works under all circumstances. Given the limitations around the poor API and requirements for installing locales, it does not seem like a viable solution at all.
Solution C: Cache translations
Cache translations somehow to avoid expensive .mo parsing.
Design considerations
Cache translations either on disk, in the database, or the object cache to avoid expensive .mo file parsing on subsequent requests. This can be done in a generalized manner or also on a per-request basis to only load translations required for the current URLURLA specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org.
Many different caching strategies have been explored in various forms in the past, each with their own pros and cons. Some could even be combined. Defining the exact implementation requires further exploration and testing, which warrants its own exploration post.
Benefits
Caching translations after one time .mo parsing potentially improves performance for future requests
Caveats and risks
Caching using persistent object cache (e.g. Memcached, Redis) or APCu:
Not available on most sites, making this not an ideal solution
Availability according to data from WordPress update requests:
Memcached: ~25%
Redis: ~25%
APCu: ~6%
Can potentially significantly increase cache size or exceed cache key limits
Database caching:
Moves the problem from disk reads to database reads
Can potentially significantly increase database size
Alternatively, use sqlite as a cache backend
Untested approach
Available on around 90% of sites
Disk caching:
Not always possible, depending on server environment
Still causes file reads, only with fewer or other files
Multiple cache groups (e.g. per-request or frontend/adminadmin(and super admin) split)
Smarter cache logic to only load translations that are needed for the majority of requests
Can potentially significantly increase cache size
Unlikely that different requests use very different translations
Cache retrieval adds overhead
Exact performance gains depend on implementation method and need to be measured first
No performance gains with cold cache
Cache invalidation logic TBD
Effort and timeline
Given the existing solutions in the ecosystem, the engineering effort itself would not be too big, but the right caching implementation (e.g. disk cache or object cache) needs to be evaluated first.
However, the right caching strategy probably does not exist because of all the different hosting environments. Since it’s unrealistic for core to support multiple types of caching, this solution seems better suited for plugins rather than core.
Solution D: Lazily evaluated translation calls
Use lazily evaluated translation calls to reduce the number of function calls in certain cases, leading to improved performance.
Design considerations
The idea of lazily evaluated translation calls has been first discussed in #41305. It enables avoiding string-specific expensive translation lookups until the translations are actually needed, by passing around proxy objects.
In other words: beyond just-in-time loading of translation files (which WordPress already does), this would add just-in-time lookup of individual strings in the translations. Check out this proof of concept to get a better picture.
It can be integrated essentially in two ways, both of which are explained on the core ticketticketCreated for both bug reports and feature development on the bug tracker.:
Change all translation calls to be lazily evaluated by default
Make this opt-in, either with new function arguments or new functions altogether
Benefits
Reduces the number of translation lookups, in some scenarios drastically
On a regular home page request there are ~60% less translation calls, saving around ~10ms (as measured by XHProf)
As a side effect, solves UXUXUser experience issues such as #38643
Caveats and risks
Depending on implementation this either breaks backward compatibility or risks not gaining enough adoption
Documentation, tooling, and developer education can help mitigate this to a certain extent
Adoption could be done gradually, e.g. starting with an opt-in approach and eventually making it the default
Likely will not have a significant impact on typical frontend page loads, as it’s mostly useful for areas like the REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. schema output, where a lot of translation calls are made without actually using the translations
Needs analysis in more scenarios to measure impact
The REST API schema already has a workaround by using a cache in a static variable
Does not improve situation for actually loading translation files
Initial testing shows that this actually hurts performance due to the additional thousands of proxy objects being created
Effort and timeline
Gradual adoption would mean a multi-year effort to establish lazily evaluated translation calls, while enabling this by default is a significant backward compatibility break that could affect thousands of plugins and themes in the ecosystem. And since it does actually slow down performance in some cases, this solution is not a great candidate for implementation.
Solution E: Optimize/Rewrite existing MO parser
Refactor the existing MO parser in WordPress to be more performant.
Design considerations
Completely overhaul the existing MO translation file parser in WordPress with performance in mind. For example by using Ginger MO, WP Performance Pack, or other existing solutions as a base.
While for instance Altis DXP (Human Made) have actually replaced the existing MO parser with a custom-made PHP extension written in Rust, such an approach is obviously not feasible for core. The new solution needs to be written in userland PHP.
Initial testings with an updated fork of Ginger MO show some noticeable speedups and lower memory usage. It also supports multiple translation files per text domain and multiple locales loaded at once, which could prove beneficial for improving the localeLocaleA locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English. switching functionality in WordPress core.
Besides that, plugins like WP Performance Pack and DynaMo have implemented partial lookups using the MO hash table or binary search, avoiding reading the whole file and storing it in memory. That slightly reduces memory usage and performance.
Benefits
Can be used without necessarily introducing another file format
Opens up potential performance enhancements in other areas, i.e. locale switching
Mostly maintains backward compatibility
Caveats and risks
Still a risk of breaking backward compatibility
Effort and timeline
There already is a working proof of concept for this solution, but more testing is required to further refine it and improve its backward compatibility layer. With such an effort being an ideal candidate for a feature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins., this could be achieved relatively quickly in a few months.
Solution F: Splitting up translation files
Split translation files from plugins and themes into smaller chunks to make loading them more efficient.
Design considerations
Depending on the project’s size, translation files can be quite big. That’s why WordPress itself uses separate translation files for the admin and everything else, so that not too many strings are unnecessarily loaded.
This strategy could be applied to plugins and themes as well. Either by allowing them to use multiple text domains (which would require developer education and changes to tooling), or by somehow doing this automatically (exact method TBD)
Benefits
Faster loading times due to loading smaller files
Caveats and risks
Risk of breaking backward compatibility
Opt-in approach requires tooling and distribution changes and risks slow adoption
Effort and timeline
Further research is required to evaluate this properly.
Comparison
At first glance, solution A (PHP translation files) is a relatively straightforward enhancementenhancementEnhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. that maintains backward compatibility and shows promising improvements. However, it does not only require changes to core itself, but also to the translation platform. The security aspect remains a risk, although discussing it early on with stakeholders and gathering more testers would help mitigate it.
Leveraging the native gettext extension as in solution B shows stunning results, but the lack of availability and the non-ideal API are a concern. Still, it’s a progressive enhancement that cannot be ignored. Especially since it could pretty much eliminate the need for additional caching as in solution C.
Caching already loaded translations as in solution C does not eliminate the root cause of the i18n slowness, but can speed up subsequent requests. Unfortunately, persistent object caches or APCu are rather uncommon (though we do not have exact data on the former yet, see #58808), and implementing more complex types of caching (e.g. per-request caching) would require significant exploration effort before becoming a viable option.
Lazily evaluated translation calls (solution D) can shave time off translation calls in some situations, but overall actually decrease performance. While it could help solve some actual UX issues in core, the backward compatibility and adoption concerns make it even less of a suitable solution.
Existing plugins like Ginger MO and WP Performance Pack show that the existing MO parser in WordPress can be further improved (solution E).
Benchmarks
Now to the most interesting part: the hard numbers!
These benchmarks are powered by a custom-built performance testing environment using @wordpress/env and Playwright. The environment has been configured with some additional plugins and the PHP extensions required for some of the solutions. Tests have been performed against the 6.3 RCrelease candidateOne of the final stages in the version release cycle, this version signals the potential to be a final release to the public. Also see alpha (beta). by visiting the home page and the dashboard 30 times each and then using the median values.
BlockBlockBlock is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. Theme
Locale
Scenario
Object Cache
Memory Usage
Total Load Time
en_US
Default
15.60 MB
133.58 ms
de_DE
Default
29.14 MB
181.95 ms
de_DE
Ginger MO (MO)
19.24 MB
159.18 ms
de_DE
Ginger MO (PHP)
16.98 MB
138.14 ms
de_DE
Ginger MO (JSONJSONJSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML.)
19.24 MB
153.39 ms
de_DE
Native Gettext
15.99 MB
142.12 ms
de_DE
DynaMo
19.62 MB
157.93 ms
de_DE
Cache in APCu
50.37 MB
181.51 ms
en_US
Default
✅
15.67 MB
121.53 ms
de_DE
Default
✅
29.01 MB
167.67 ms
de_DE
Ginger MO (MO)
✅
19.11 MB
147.19 ms
de_DE
Ginger MO (PHP)
✅
16.85 MB
127.97 ms
de_DE
Ginger MO (JSON)
✅
19.11 MB
144.43 ms
de_DE
Native Gettext
✅
15.86 MB
129.19 ms
de_DE
DynaMo
✅
18.57 MB
133.46 ms
de_DE
Cache in APCu
✅
50.30 MB
170.19 ms
de_DE
Cache in object cache
✅
29.07 MB
173.19 ms
Benchmarks using the Twenty Twenty-Three block theme
Classic Theme
Locale
Scenario
Object Cache
Memory Usage
Total Load Time
en_US
Default
15.35 MB
120.79 ms
de_DE
Default
28.79 MB
172.10 ms
de_DE
Ginger MO (MO)
18.85 MB
145.68 ms
de_DE
Ginger MO (PHP)
16.56 MB
124.73 ms
de_DE
Ginger MO (JSON)
18.84 MB
140.78 ms
de_DE
Native Gettext
15.58 MB
128.26 ms
de_DE
DynaMo
19.24 MB
146.09 ms
de_DE
Cache in APCu
50.13 MB
167.28 ms
en_US
Default
✅
15.19 MB
107.26 ms
de_DE
Default
✅
28.59 MB
154.30 ms
de_DE
Ginger MO (MO)
✅
18.64 MB
133.21 ms
de_DE
Ginger MO (PHP)
✅
16.37 MB
112.94 ms
de_DE
Ginger MO (JSON)
✅
18.64 MB
128.94 ms
de_DE
Native Gettext
✅
15.38 MB
115.11 ms
de_DE
DynaMo
✅
18.10 MB
120.72 ms
de_DE
Cache in APCu
✅
49.99 MB
151.82 ms
de_DE
Cache in object cache
✅
28.65 MB
156.36 ms
Benchmarks using the Twenty Twenty-One classic theme
Admin
Locale
Scenario
Object Cache
Memory Usage
Total Load Time
en_US
Default
15.42 MB
139.83 ms
de_DE
Default
31.92 MB
187.76 ms
de_DE
Ginger MO (MO)
20.07 MB
164.94 ms
de_DE
Ginger MO (PHP)
17.09 MB
139.66 ms
de_DE
Ginger MO (JSON)
20.06 MB
160.87 ms
de_DE
Native Gettext
15.95 MB
143.43 ms
de_DE
DynaMo
20.58 MB
166.79 ms
de_DE
Cache in APCu
58.13 MB
190.38 ms
en_US
Default
✅
15.66 MB
112.69 ms
de_DE
Default
✅
31.84 MB
164.26 ms
de_DE
Ginger MO (MO)
✅
19.99 MB
140.70 ms
de_DE
Ginger MO (PHP)
✅
17.01 MB
118.52 ms
de_DE
Ginger MO (JSON)
✅
19.98 MB
138.49 ms
de_DE
Native Gettext
✅
15.87 MB
120.01 ms
de_DE
DynaMo
✅
19.73 MB
120.26 ms
de_DE
Cache in APCu
✅
58.07 MB
162.41 ms
de_DE
Cache in object cache
✅
31.86 MB
164.28 ms
Benchmarks visiting the WordPress admin
Conclusion
Finding the right path forward means weighing all the pros and cons of each solution and looking at both horizontal and vertical impact, i.e. how much faster can i18n be made for how many sites.
When looking at all these factors, it appears that a revamped translations parser (solution E) could bring the most significant improvements to all localized WordPress sites. Especially when combined with a new PHP translation file format (solution A), which Ginger MO supports, the i18n overhead becomes negligible. Of course the same risks associated with introducing a new format apply.
On top of that, a revamped i18n library like Ginger MO could also be combined with other solutions such as caching or dynamic MO loading to potentially gain further improvements. However, those routes have yet to be explored.
Next steps
The WordPress performance team wants to further dive into this topic and test some of the above solutions (and combinations thereof) on a wider scale through efforts like the Performance Lab feature project. We are looking forward to hearing your feedback on this analysis and welcome any additional comments, insights, and tinkering.
Deadline August 6, 2023
After the deadline passes, the performance team will discuss the received feedback and determine next steps.
In #58035 / [55928], a new pre_load_textdomainfilterFilterFilters 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. was introduced. This is useful for plugins to develop and test alternative loading/caching strategies for translations. This brings consistency with the existing pre_load_script_translations filter for JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. translations.
Improvements to just-in-time translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. loading
In #58321, it was reported that _load_textdomain_just_in_time() was firing too often if no translations were found for a given text domain, which typically is the case on site running English (US).
[55865] addresses this issue, which resulted in some minor performance improvements.
Since the last update on the Preferred Languages feature plugin, a lot of work has been accomplished both on the pluginPluginA 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 side and in coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. to make the solution more robust in a variety of ways. Today, I want to provide a bit more details on these accomplishments, which resulted in the recent release of Preferred Languages 2.0, advancing the project a huge step closer towards a core merge proposal
But first, make sure to check out the previous update:
Over the last year, a lot of work has gone into making the plugin more stable by adding more tests and fixing bugs. This includes improving compatibility with other plugins and making translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. merging and localeLocaleA locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English. switching more robust. As a result, pure unit testunit testCode written to test a small piece of code or functionality within a larger application. Everything from themes to WordPress core have a series of unit tests. Also see regression. code coverage is near 100%, with end-to-end tests adding another layer of confidence.
With WordPress adding several i18ni18nInternationalization, 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. improvements in WordPress 6.1 and 6.2, the Preferred Languages plugin is now fully compatible with WP_Textdomain_Registry and switch_to_user_locale(). The minimum required WordPress version has been bumped to 6.1 as a result.
Certainly the biggest change, however, was the full refactoring of the UIUIUser interface itself. The whole JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. portion of the code base was over 6 years old and using jQuery and jQuery UI. But not anymore! The UI has been completely refactored to use ReactReactReact is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/., with the same components that also power the blockBlockBlock is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor. In the process, drag & drop sorting functionality was removed to simplify the UI, and accessibilityAccessibilityAccessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility) has improved, but otherwise everything looks mostly the same.
How to help
So, what’s next? The latest version of the Preferred Languages feature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins. needs more eyes testing it and providing feedback.
One big remaining question mark is the concept of translation merging. By default, if there are only some missing strings in a selected locale, these would be displayed in English. But with translation merging, the missing strings will be taken from the locale next in line instead. While this works great, it could be a tad slow due to the way translations are loaded in WordPress. Any help addressing this potential performance concern would be greatly appreciated.
Note: The merging feature can be enabled with add_filter( 'preferred_languages_merge_translations', '__return_true' );.
Active development is taking place on GitHub. If you want to get involved, check out the open issues and join the #core-i18n channel on Slack.
Make it easier to switch to a user’s localeLocaleA locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English.
A while back, WordPress 4.7 introduced user admin languages and locale switching. With every user being able to set their preferred locale, it’s crucial to use locale switching to ensure things like emails are sent in that locale. That’s why you would see a lot of code like switch_to_locale( get_user_locale( $user ) ) in coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. and in plugins.
Not only is this very repetitive, it also causes limitations when used in combination with things like the Preferred Languagesfeature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins., where one would like to fall back to another locale if the desired one is not available.
To improve this, WordPress 6.2 provides a new switch_to_user_locale() function that takes a user ID, grabs the user’s locale and stores the ID in the stack, so that at each moment in time you know whose locale is supposed to be used.
Together with this enhancementenhancementEnhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature., the WP_Locale_Switcher class has been updated to filterFilterFilters 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. both locale and determine_locale with the switched locale. This way, anyone using the determine_locale() function will get the correct locale information when switching is in effect.
Core already makes use of this new function, and plugins and themes are of course encouraged to do so as well.
In #56698, the locale’s word count type (i.e. whether they count words or characters), has been made part of the WP_Locale class.
Previously, to get that information, plugins and themes had to do something similar as core and use code like _x( 'words', 'Word count type. Do not translate!' ). All such translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. strings in core have already been replaced with the new wp_get_word_count_type() function (which is a wrapper around WP_Locale::get_word_count_type()). So if you have been using those translation strings in your code, you can now switch to this new function too!
Install new translations when editing your profile
Ever since the aforementioned user admin language feature was introduced, users have been able to change their preferred language in the user profile by choosing from the list of already installed languages. New languages could only be installed via the General Settings page.
Starting with WordPress 6.2, you don’t have to go to the settings page anymore if you quickly want to change your user language to a new one—if you have the necessary capabilities to install languages of course, which by default only admins have.
Users with the necessary capabilities can now install new languages via the profile edit screen.
Translator comments for screen reader strings
In r55276 / #29748, all translatable strings intended for screen readers have been marked as such via translator comments.
This aims to provide better context for translators and make it easier to determine that some strings contain hidden accessibilityAccessibilityAccessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility) text and are not displayed in the UIUIUser interface.
More than half of all WordPress sites in the world use a language other than US English. For these sites and users, the options to change the site and user language are great. But when there’s no translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. for a given pluginPluginA 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 or theme, WordPress falls back to US English. That’s a poor user experience for many non-English speakers.
The Preferred Languages plugin solves this issue by doing the same thing operating systems, browsers, and other types of software have been doing for years. It lets you select multiple preferred languages in your settings. WordPress then tries to load the translations for the first language that’s available, falling back to the next language in your list.
The Preferred Languages UIUIUser interface
Recent New Features
After stabilizing the initial prototype, the feature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins. has lived a mostly dormant life, with only irregular updates here and there. Adding support for JavaScript i18n introduced in WordPress 5.0 was the biggest enhancementenhancementEnhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature.. With the plugin being stable and used on thousands of sites without issues, there was simply no need for any other change. Yet, the plugin was far from feature complete.
Over the past year, I blew the dust off and made significant improvements to the plugin:
Bringing UI and code up-to-date with latest WordPress version
Improved MultisitemultisiteUsed to describe a WordPress installation with a network of multiple blogs, grouped by sites. This installation type has shared users tables, and creates separate database tables for each blog (wp_posts becomes wp_0_posts). See also network, blog, site support, bringing Preferred Languages to Networknetwork(versus site, blog) settings
Site Health integration
Increased test coverage
Improved compatibility with other plugins, especially those accessing the locale user metaMetaMeta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress.
Added an option to merge incomplete translations to avoid fallbacks to US English
The latter is actually a pretty cool enhancement and can be enabled using a filterFilterFilters 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.. Here’s what it does:
Let’s say your preferred languages are es_CR, es_MX, es_ES, en_US (in this order). With this feature, if some of the translations are incomplete, your site will be displayed in es_CR, with missing strings taken from es_MX, es_ES etc. Without this feature, missing strings would simply be displayed in US English straight away. Merging translations this way makes for a much better user experience.
What’s Still Missing / Open Questions
Textdomain Registry
Since the Preferred Languages feature plugin also needs to work well when switching locales, #39210 has been a missing feature for a long time. While the plugin has its own implementation of a textdomain registry originally created (but then reverted) in that ticketticketCreated for both bug reports and feature development on the bug tracker., it is required for this change to finally land in coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress..
My hope is that this can be implemented in WordPress 6.1+.
JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. Code Base
The initial version of the Preferred Languages plugin was built in a pre-GutenbergGutenbergThe Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ era, using jQuery and jQuery UI Sortable. It doesn’t make much sense to potentially merge such a new UI component into core that is built with those technologies.
Rather, some time should be spent to rebuild the client-side code. There are two possible options here:
Rewrite in vanilla JavaScript, replacing jQuery with modern Web APIs. This is most feasible when removing the capability to sort languages using drag & drop, for which jQuery UI Sortable is currently used.
If we’re okay with dropping drag & drop functionality, then this would be a straightforward change.
Rewrite everything in ReactReactReact is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/.. A prototype of this actually exists, so it’s mostly a matter of finishing it up & perhaps submitting some upstream PRs to Gutenberg for any missing features. Using React would be more in line with current best practices and expansion of Gutenberg throughout WordPress adminadmin(and super admin). Such a rewrite would also facilitate potential use of the component directly within a Gutenberg context. On the other hand, it would significantly increase overall code size and thus maintenance burden, for little immediate benefit.
While I am currently heavily leaning towards the first option, inputs are always welcome!
Of course, if we are okay with keeping jQuery & jQuery UI Sortable, then no change is needed at all.
The Next Steps
The Preferred Languages feature plugin can always use help with development and testing. Right now resolving the two open questions is the main priority before proposing merging this functionality into core.
Active development is taking place on GitHub. If you want to get involved, check out open issues and join the #core-i18n channel on Slack.
As part of the 5.6 release, we’ll be hosting a i18ni18nInternationalization, 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. focused bugbugA bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. scrub this Wednesday, September 16, 2020, 18:00 UTC in the #core channel on SlackSlackSlack is a Collaborative Group Chat Platform https://slack.com/. The WordPress community has its own Slack Channel at https://make.wordpress.org/chat/..
They are many tickets for this component that will need feedback and help to move forward, as part of WordPress 5.6 release cycle.
In WordPress 5.5, several legacy JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. localization variables have been removed in wp-admin in favor of using the modern form wp.i18n.__().
The i18n package was introduced with WordPress 5.0 to facilitate JavaScript and dynamic UIs translationtranslationThe process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization.. It was therefore possible to use it instead of other functions used for localization as wp_localize_script().
Several interfaces in wp-adminadmin(and super admin) already use the functions and utilities provided by i18ni18nInternationalization, 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. for translatable strings. With WordPress 5.5, many others are now adopting this new approach.
If you develop WordPress themes or extensions some previously globally accessible localization variables could stop working and cause breaks in your code.
You can see how to use the functions and utilities of i18n package in this p2 post.
Build a WordPress.orgWordPress.orgThe 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/ directory for discovering blocks, and a way to seamlessly install them is one of the 9 priorities in the 2019 roadmap. This post tries to summarize work done so far and identify all the next steps required to land this project in WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. later this year.
It has been over two months since the MetaMetaMeta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. team published a post intended as a starting point for discussion and new ideas for the Block Directory, and a new type of plugin:
Put briefly, I’d like to propose a new type of WordPress pluginPluginA 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 that provides blocks and nothing else: Single BlockBlockBlock is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. Plugins. These will be hosted in a separate Block Directory section of the Plugin Directory. They will be JavaScriptJavaScriptJavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/.-based, and each plugin will register a single Block. And they will be searchable and installable from within the GutenbergGutenbergThe Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ editor itself.
@tellyworth
Currently, new Gutenberg blocks can be provided by plugins, which often register many blocks, and which are managed from outside the editor. The proposal mentioned above outlines a new type of simple block-based plugin that is intended to be seamlessly installed from within Gutenberg itself. It was later followed up with the call for design on installing blocks from within Gutenberg. There was an essential technical aspect highlighted in the post:
The WordPress.org APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. will provide an endpoint for searching for blocks by name and description, and return metadata similar to that of plugins. Gutenberg’s Inserter could use that endpoint to also show relevant block plugins that are available to install, with a button and process for seamless installation.
@tellyworth
This new endpoint is going to be based on the Block Registration API RFC which intends to address the server-side awareness of blocks and simplify the block discovery for the block directory project. In practical terms, it means that we are seeking for a solution where block type registration is declarative and context-agnostic. Any runtime (PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher, JSJSJavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors., Android, iOSiOSThe operating system used on iPhones and iPads. or other) should be able to interpret the basics of a block type and should be able to fetch or retrieve the definitions of the context-specific implementation details.
Core Editor team reached the point where we believe that the Request for Comments is close to being finalized. However, there are still some areas where we feel that other WordPress teams could have a significant impact on the proposal.
Internationalization
The way how translations are handled inside JSONJSONJSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. files is something novel for WordPress core. The current proposal for PHP follows the existing get_plugin_data core function which parses the plugin contents to retrieve the plugin’s metadata, and it selectively applies translations dynamically. It would be also similar for JavaScript, with the remark that we plan to implement a custom Babel plugin which would mirror PHP behavior for ESNext code. The transformation would happen during the build step to ensure that files can be statically analyzed before scripts are enqueued. You can find more details in RFC document in the Internationalization section. There is also an issue#15169 opened which describes the technical details of the proposed JavaScript implementation of the Babel plugin.
Core JS team discussed this topic at the end of the weekly meeting on Apr 2nd. We have received great feedback from @swissspidy which helped to shape the proposal. However, we still encourage other Polyglots teamPolyglots TeamPolyglots Team is a group of multilingual translators who work on translating plugins, themes, documentation, and front-facing marketing copy. https://make.wordpress.org/polyglots/teams/. members to voice their opinions.
New REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. endpoints
The long term vision for the block discovery in WordPress includes:
Fetching the available block types through REST APIs.
Fetching block objects from posts through REST APIs.
The proposed implementation for the server-side awareness of block types should ensure that it stays intact with all recommendations that REST API team might have. That’s why we encourage the REST API team to get involved in the process early on. There is no immediate need to start working on new block type related API endpoints. However, it would be great to have it included on the roadmap. Ideally, they should stay as close as possible to the WordPress.org API and the final shape of the endpoint for searching for blocks.
It’s been a while since new post type labels have been introduced in WordPress.
In WordPress 5.0, five additional labels have been made available for custom post types. These get passed in via the labels argument when using register_post_type(). The following labels are new:
item_published — The label used in the editor notice after publishing a post. Default “Post published.” / “Page published.”
item_published_privately — The label used in the editor notice after publishing a private post. Default “Post published privately.” / “Page published privately.”
item_reverted_to_draft — The label used in the editor notice after reverting a post to draft. Default “Post reverted to draft.” / “Page reverted to draft.”
item_scheduled — The label used in the editor notice after scheduling a post to be published at a later date. Default “Post scheduled.” / “Page scheduled.”
item_updated — The label used in the editor notice after updating a post. Default “Post updated.” / “Page updated.”
Please note the trailing period for all of these strings.
You must be logged in to post a comment.