Solving Plugin Translation in WordPress 6.7

With the release of WordPress 6.7, developers were introduced to some changes regarding how translations are loaded.

WordPress 6.7 Translations

With the release of WordPress 6.7, developers were introduced to some changes regarding how translations are loaded for plugins. However, the guidance provided—especially the PHP warnings prompting developers to adjust their code—led to confusion. Following the recommended changes resulted in translations not being loaded correctly, especially for plugins with translation files stored locally within the plugin directory or for new plugins whose translations are not yet hosted in the WordPress repository.

In this post, I’ll walk you through the issue and the steps I took to resolve this issue for my plugin.

The Problem

Before WordPress 6.7, it was common practice to load translations using the load_plugin_textdomain function within the init action. For example:

add_action( 'init', 'load_textdomain' );

/**
 * Load text domain for internalization.
 */
function load_textdomain() {
    load_plugin_textdomain( 'my-textdomain', false, 'my-plugin-folder/langs/' );
}

This approach worked seamlessly for the following plugin structure:

my-plugin-folder/
├── my-plugin.php
├── langs/
    ├── my-textdomain-ro_RO.mo
    ├── my-textdomain-ro_RO.po
    ├── my-textdomain.pot

However, after WordPress 6.7, translations were no longer being picked up when the plugin was loaded. This was particularly problematic if:

  1. Your plugin’s text domain did not match the base name of the plugin folder.
  2. The translation files were stored locally within the plugin’s directory rather than the global WP_LANG_DIR.
  3. You were developing a new plugin whose translations were not hosted in the WordPress repository.

Understanding the Changes

The changes in WordPress 6.7 adjusted the way translations are handled, emphasizing the use of translations hosted in the WordPress repository and making it more challenging to use local translations directly from the plugin folder. This behavior caused locally stored translation files to be overlooked, even when explicitly specified.

The Solution

To ensure translations are loaded correctly after WordPress 6.7, I made the following adjustments:

/**
 * Load text domain for internalization.
 */
function load_textdomain() {
    $locale      = get_locale(); // Get the current locale.
    $textdomain  = 'my-textdomain';
    $plugin_path = plugin_dir_path( __FILE__ ) . 'langs/' . $textdomain . '-' . $locale . '.mo';
    $global_path = WP_LANG_DIR . '/plugins/' . $textdomain . '-' . $locale . '.mo';

    // Attempt to load translations using load_plugin_textdomain.
    load_plugin_textdomain( $textdomain, false, basename( __DIR__ ) . '/langs/' );

    // Fallback to manually load local translations if not picked up.
    if ( file_exists( $plugin_path ) && is_readable( $plugin_path ) ) {
        load_textdomain( $textdomain, $plugin_path );
    } elseif ( file_exists( $global_path ) && is_readable( $global_path ) ) {
        load_textdomain( $textdomain, $global_path );
    }
}

add_action( 'init', 'load_textdomain' );

Why This Works

  1. Explicit Locale Handling: By explicitly fetching the current locale with get_locale(), the solution ensures that the correct translation file (e.g., my-textdomain-ro_RO.mo) is targeted.
  2. Fallback to Local Files: The use of load_textdomain as a fallback ensures that translations stored in the plugin’s langs directory are loaded if they are not picked up by load_plugin_textdomain.
  3. Global Path Support: For plugins that rely on global translations stored in the WP_LANG_DIR, this solution checks and loads those files as well.
  4. Backward Compatibility: This approach preserves compatibility with the existing plugin structure while addressing the changes introduced in WordPress 6.7.

Key Takeaways

  • Test Local Translations: Always test your plugin’s translations when upgrading WordPress, especially for significant updates like 6.7.
  • Understand the Text Domain Rules: Ensure your plugin’s text domain matches its declaration in the plugin header and translation files.
  • Fallback Logic is Essential: Adding fallback logic with load_textdomain is a robust way to handle edge cases where load_plugin_textdomain fails.

By implementing this solution, I was able to ensure that translations were correctly loaded in my plugin, maintaining compatibility across WordPress versions and avoiding any disruption for users.

Note: The article featured image was generated using Adobe Firefly AI technology.

Time to read:

2–3 minutes

If you would like to support my work, consider making a donation, buy me a coffee, or share this on your feed.

Iulia Cazan
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.