WordPress Localization (i18n/l10n) API Overview
WordPress internationalization (i18n) and localization (l10n) system for translating text strings.
Core Concepts
Internationalization (i18n)
WordPress internationalization (i18n) and localization (l10n) system for translating text strings.
Localization (l10n)
WordPress internationalization (i18n) and localization (l10n) system for translating text strings.
Text Domains
The process of developing software so it can be localized. In WordPress, this means wrapping all user-facing strings in translation functions.
// Core WordPress uses 'default' domain
__( 'Hello', 'default' );
// Plugins/themes use their own domain
__( 'Hello', 'my-plugin' );Translation File Formats
MO Files (Machine Object)
The process of translating internationalized software for a specific locale. WordPress uses gettext-style .mo and .l10n.php files for translations.
- Location:
wp-content/languages/ - Naming:
{locale}.mofor core,{domain}-{locale}.mofor plugins/themes - Example:
fr_FR.mo,my-plugin-de_DE.mo
PHP Translation Files (since WP 6.5)
A unique identifier that ties translated strings to a specific plugin, theme, or WordPress core. Each component should use its own text domain to avoid conflicts.
- Extension:
.l10n.php - Location: Same as MO files
- Format: PHP array with translations and metadata
// Example .l10n.php structure
return [
'project-id-version' => 'My Plugin 1.0.0',
'po-revision-date' => '2024-01-15 10:00:00+0000',
'messages' => [
'Hello' => 'Bonjour',
'Goodbye' => 'Au revoir',
],
];PO Files (Portable Object)
Binary compiled translation files. The traditional format used by WordPress.
msgid "Hello"
msgstr "Bonjour"
msgctxt "greeting"
msgid "Hi"
msgstr "Salut"Translation Flow
1. String Extraction
New format that’s faster to load than MO files. Preferred format since WP 6.5.
2. Translation
Human-readable translation files used by translators. Not loaded at runtime.
3. Compilation
Developers wrap translatable strings in gettext functions. Tools like WP-CLI or POEdit extract these to .pot files.
4. Loading
Translators create .po files from .pot templates, providing translations for each string.
- Automatic just-in-time loading (since WP 4.6)
- Explicit loading via
load_textdomain(),load_plugin_textdomain(), etc.
5. Translation Lookup
PO files are compiled to binary .mo files (or converted to .l10n.php files).
WP_Translation_Controllerchecks loaded translations- Returns translated string or falls back to original
Directory Structure
wp-content/
└── languages/
├── de_DE.mo # Core German translation
├── de_DE.l10n.php # Core German (PHP format)
├── admin-de_DE.mo # Admin German translation
├── plugins/
│ └── my-plugin-de_DE.mo
└── themes/
└── my-theme-de_DE.moLocale Determination
WordPress loads translations via:
- WPLANG constant in
wp-config.php - Database option
WPLANG - User preference (admin area)
- URL parameter
wp_lang(login page) - Cookie
wp_lang
// Get current locale
$locale = get_locale(); // Site locale
$locale = get_user_locale(); // Current user's locale
$locale = determine_locale(); // Best locale for requestArchitecture Components
WP_Translation_Controller
When a translation function is called:
$controller = WP_Translation_Controller::get_instance();
$controller->load_file( '/path/to/translation.mo', 'my-plugin', 'de_DE' );
$translation = $controller->translate( 'Hello', '', 'my-plugin' );WP_Translations
WordPress determines the current locale through several methods:
WP_Locale
Singleton that manages all translation file loading and lookups. Introduced in WP 6.5.
WP_Locale_Switcher
Wrapper class providing backward compatibility with the old $l10n global. Acts as bridge to WP_Translation_Controller.
POMO Classes
Stores locale-specific data: weekday/month names, date formats, text direction, number formats.
MO– Reads binary MO filesTranslations– Base translation containerNOOP_Translations– No-op fallbackTranslation_Entry– Single translation entry
Plural Forms
Manages temporary locale switching, maintaining a stack of locales.
Plural-Forms: nplurals=2; plural=(n != 1); # English
Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 ? 1 : 2); # Czech// Translate with correct plural form
printf(
_n( '%d comment', '%d comments', $count, 'my-plugin' ),
$count
);Just-in-Time Loading (since WP 4.6)
Legacy classes for reading MO/PO files:
- A translation function is called with a text domain
get_translations_for_domain()checks if domain is loaded- If not,
_load_textdomain_just_in_time()attempts to find and load the translation file WP_Textdomain_Registrytracks custom paths for each domain
Languages have different plural rules. WordPress handles this via the Plural-Forms header:
RTL Support
WordPress automatically loads translations when first needed:
// Check if current locale is RTL
if ( is_rtl() ) {
// Load RTL stylesheets
}
// WP_Locale stores text direction
$wp_locale->text_direction; // 'ltr' or 'rtl'Best Practices
- Always use text domains – Never call
__()without a domain (except core) - Use context for ambiguous strings –
_x( 'Post', 'verb', 'domain' )vs_x( 'Post', 'noun', 'domain' ) - Keep strings complete – Don’t concatenate translated strings
- Use placeholders –
sprintf( __( 'Hello %s', 'domain' ), $name ) - Escape output – Use
esc_html__(),esc_attr__()for safe output - Load translations at the right time – Hook to
initor later, not before
Related Files
wp-includes/l10n.php– Core translation functionswp-includes/l10n/class-wp-translation-controller.php– Translation managementwp-includes/l10n/class-wp-translations.php– Compatibility wrapperwp-includes/class-wp-locale.php– Locale datawp-includes/class-wp-locale-switcher.php– Locale switchingwp-includes/pomo/– MO/PO file handling