Back to Blog
Lesson 32 of the WordPress Plugin Development: Foundations (PHP & MVC) course
WordPressJune 25, 20263 min read

Mastering WordPress Internationalization (i18n) for Plugins

Learn to implement WordPress internationalization (i18n) to make your plugins translation-ready. Master text domains, translation functions, and POT file generation.

WordPressPHPi18nlocalizationtranslationaccessibilityplugin-development

Previously in this course, we discussed handling plugin updates to keep your database schema in sync with your codebase. Now that our Knowledge Base plugin is stable and secure, it’s time to open it up to the world by implementing internationalization (i18n).

Internationalization—often abbreviated as "i18n" because there are 18 letters between the 'i' and the 'n'—is the process of developing your plugin so it can easily be adapted for different languages and regions. Without this, your plugin is locked into the language you wrote it in, severely limiting your user base.

The Principles of Translation-Ready Code

To make your plugin "translation-ready," you must stop hardcoding display strings directly into your PHP files. Instead, you wrap every user-facing string in specific WordPress functions. These functions tell WordPress that the string should be passed through a translation engine before being sent to the browser.

When you do this, you define a text domain. A text domain is a unique string—usually the slug of your plugin—that acts as a namespace for your translations. This ensures that WordPress knows which language file belongs to your plugin and not another.

Using Translation Functions

There are two primary functions you need to master for basic output:

  1. __(): Retrieves the translated string. Use this when you need to store the string in a variable or pass it to another function.
  2. _e(): Retrieves and echoes the translated string. Use this for direct output in your templates.

Both functions accept two arguments: the string to translate and the text domain.

PHP
#6A9955">// Storing for later use
$label = __('Knowledge Base Article', 'knowledge-base-plugin');

#6A9955">// Echoing directly to the page
_e('Click here to read more', 'knowledge-base-plugin');

Integrating i18n into the Knowledge Base Plugin

Let's update our plugin's core initialization to load our text domain. In your main plugin file, you need to hook into the plugins_loaded action.

PHP
class KnowledgeBasePlugin {
    public function __construct() {
        add_action('plugins_loaded', [$this, 'load_textdomain']);
    }

    public function load_textdomain() {
        load_plugin_textdomain(
            'knowledge-base-plugin', 
            false, 
            dirname(plugin_basename(__FILE__)) . '/languages'
        );
    }
}

Now, every time you output text in your views or admin controllers, use the translation functions. For example, in your AdminController, when you define your menu page:

PHP
add_menu_page(
    __('Knowledge Base Settings', 'knowledge-base-plugin'),
    __('Knowledge Base', 'knowledge-base-plugin'),
    'manage_options',
    'knowledge-base',
    [$this, 'render_admin_page']
);

Generating the POT File

A POT (Portable Object Template) file is a master template containing all the translatable strings in your plugin. Translators use this file to create language-specific .po files.

You don't need to write this file manually. You can use tools like the WP-CLI to generate it automatically:

  1. Open your terminal in your plugin's root directory.
  2. Run the following command: wp i18n make-pot . languages/knowledge-base-plugin.pot

This command scans your entire directory for __() and _e() functions, extracts the strings, and creates the .pot file in your /languages folder.

Hands-on Exercise

  1. Refactor: Audit your AdminController and your frontend shortcode views. Find every hardcoded string (like "Submit," "Settings," or "Article Title") and wrap it in the __() or _e() function.
  2. Namespace: Ensure every function call uses your unique text domain string.
  3. Generate: Run the wp i18n make-pot command and verify that your languages/ folder now contains your .pot file.

Common Pitfalls

  • Forgetting the Text Domain: If you omit the text domain in your function calls, WordPress won't know where to look for your translations.
  • Variable Interpolation: Never do this: __('Hello ' . $user_name, 'domain'). The translation engine cannot parse concatenated strings. Instead, use printf() with placeholders: printf(__('Hello %s', 'domain'), $user_name);.
  • Dynamic Strings: Avoid translating dynamic data stored in the database. Only translate static, hardcoded UI text.
  • Case Sensitivity: Ensure your text domain is consistent throughout every single file in your project.

By following these practices, you ensure that your plugin remains accessible to a global audience, allowing community contributors to localize your work effectively.

Up next: We'll dive into Debugging WordPress Plugins to learn how to identify and resolve issues when things don't go according to plan.

Similar Posts