[wp-trac] [WordPress Trac] #64249: Automatic translation (JIT) loading doesn't work for network-activated plugins in multisite

WordPress Trac noreply at wordpress.org
Fri Mar 13 06:12:56 UTC 2026


#64249: Automatic translation (JIT) loading doesn't work for network-activated
plugins in multisite
--------------------------+------------------------
 Reporter:  pelentak      |       Owner:  (none)
     Type:  defect (bug)  |      Status:  new
 Priority:  normal        |   Milestone:  7.1
Component:  I18N          |     Version:  6.7
 Severity:  normal        |  Resolution:
 Keywords:  needs-patch   |     Focuses:  multisite
--------------------------+------------------------

Comment (by sanket.parmar):

 I've reviewed the issue and can confirm the root cause.

 In `wp-settings.php`, the JIT translation registration introduced in
 [59461] only runs for site-level active plugins. The network-activated
 plugins loop (which runs earlier in the bootstrap) does not register text
 domains with `$wp_textdomain_registry`, so the JIT mechanism
 (`_load_textdomain_just_in_time()`) has no path to resolve translations
 for those plugins.

 The fix requires two related changes in `wp-settings.php`:

 === 1. Move the `plugin.php` include earlier ===

 Currently, `get_plugin_data()` is made available ''after'' the network
 plugins loop via:

 {{{
 // To make get_plugin_data() available in a way that's compatible with
 plugins also loading this file, see #62244.
 require_once ABSPATH . 'wp-admin/includes/plugin.php';
 }}}

 This line needs to be moved to just ''before'' the `if ( is_multisite() )`
 network plugins block, so that `get_plugin_data()` is available when
 processing network plugins.

 === 2. Add text domain registration to the network plugins loop ===

 Mirror the same registration logic already present in the site plugins
 loop:

 {{{
 #!php
 // Load network activated plugins.
 if ( is_multisite() ) {
     foreach ( wp_get_active_network_plugins() as $network_plugin ) {
         wp_register_plugin_realpath( $network_plugin );

         $plugin_data = get_plugin_data( $network_plugin, false, false );
         $textdomain  = $plugin_data['TextDomain'];
         if ( $textdomain ) {
             if ( $plugin_data['DomainPath'] ) {
                 $GLOBALS['wp_textdomain_registry']->set_custom_path(
                     $textdomain,
                     dirname( $network_plugin ) .
 $plugin_data['DomainPath']
                 );
             } else {
                 $GLOBALS['wp_textdomain_registry']->set_custom_path(
                     $textdomain,
                     dirname( $network_plugin )
                 );
             }
         }

         $_wp_plugin_file = $network_plugin;
         include_once $network_plugin;
         $network_plugin = $_wp_plugin_file;

         do_action( 'network_plugin_loaded', $network_plugin );
     }
     unset( $network_plugin, $_wp_plugin_file, $plugin_data, $textdomain );
 }
 }}}

 === Notes on safety ===

  * Moving the `require_once` earlier is low risk — `plugin.php` was
 already made safe for early loading as part of #62244.
  * `get_plugin_data()` only reads the plugin file's comment header; it
 makes no database calls and has no side effects.
  * `set_custom_path()` is idempotent, so plugins that currently call
 `load_plugin_textdomain()` explicitly as a workaround will continue to
 work correctly — the registry entry will simply be set twice with the same
 value.
  * JavaScript translations are unaffected (they already work, as noted in
 the ticket).

 I'm happy to prepare a patch if this approach is agreeable.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/64249#comment:4>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list