Getting Started with Localized Theme Text Domains and Translations in Multi-Language Site Networks
Understanding WordPress Text Domains
At its core, internationalization (i18n) in WordPress relies on a mechanism called “text domains.” A text domain is a unique string identifier for a theme or plugin. When WordPress encounters translatable strings within your theme’s code, it uses this text domain to look up the corresponding translation in the appropriate language file (.po/.mo). For a theme to be properly translatable, every translatable string must be wrapped in WordPress’s internationalization functions and associated with the theme’s unique text domain.
The standard practice is to use the theme’s slug (the directory name of the theme) as its text domain. For example, if your theme is located in wp-content/themes/my-awesome-theme, its text domain should be my-awesome-theme.
Implementing Text Domains in Theme Files
All user-facing strings within your theme’s PHP files should be wrapped in WordPress i18n functions. The most common ones are __() for translated strings that are immediately displayed and _e() for translated strings that are echoed directly. For strings that might contain variables, _n() is used for plurals, and esc_html__() or esc_html_e() should be used for strings that need HTML escaping.
Here’s a practical example of how to implement text domains in a theme’s functions.php file and a template file (e.g., header.php):
functions.php Example
// Load theme text domain for translation.
function my_awesome_theme_setup() {
load_theme_textdomain( 'my-awesome-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_awesome_theme_setup' );
// Example of a translatable string in a theme function.
function get_custom_greeting() {
return __( 'Welcome to our site!', 'my-awesome-theme' );
}
header.php Example
<?php
/**
* Displays the site title and tagline.
*/
function display_site_identity() {
if ( is_front_page() && is_home() ) : ?>
<h1 class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
<?php else : ?>
<p class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></p>
<?php endif;
$description = get_bloginfo( 'description', 'display' );
if ( $description || is_customize_preview() ) : ?>
<p class="site-description"><?php echo esc_html( $description ); ?></p>
<?php endif;
// Example of a translatable string directly echoed.
_e( 'This is a custom header element.', 'my-awesome-theme' );
}
?>
<div id="site-branding">
<?php display_site_identity(); ?>
</div><!-- #site-branding -->
In the functions.php example, load_theme_textdomain() is crucial. It tells WordPress where to find your theme’s translation files. The first argument is your theme’s text domain, and the second is the absolute path to the directory containing the languages folder. The get_template_directory() function returns the path to the current theme’s directory.
Generating Translation Files (.pot)
To create translation files, you first need a .pot (Portable Object Template) file. This file contains all the translatable strings from your theme, along with their original English text. You can generate this file using command-line tools like wp-cli or dedicated PHP libraries.
Using wp-cli
If you have wp-cli installed, generating a .pot file for your theme is straightforward. Navigate to your WordPress installation’s root directory in your terminal and run the following command:
wp i18n make-pot . languages/my-awesome-theme.pot --headers='{"Language-Team":"My Awesome Theme Devs <[email protected]>", "X-Generator":"WP-CLI/<version>"}'
This command:
wp i18n make-pot .: Initiates the POT file generation process for the current directory (your theme’s root).languages/my-awesome-theme.pot: Specifies the output path and filename for the generated POT file. Ensure thelanguagesdirectory exists within your theme.--headers='...': Allows you to add custom headers to the POT file, such as the Language Team and a generator string.
Creating Language Files (.po/.mo)
Once you have the .pot file, translators can use it to create language-specific translation files. A .po (Portable Object) file is a human-readable text file containing the original strings and their translations. A .mo (Machine Object) file is a compiled binary version of the .po file, which WordPress uses for actual translations.
Tools like Poedit (a desktop application) or online translation platforms can be used to edit .po files. After a translator has completed their work in a .po file (e.g., es_ES.po for Spanish in Spain), it needs to be compiled into a .mo file (es_ES.mo).
You can compile .po files to .mo files using the msgfmt command-line utility, which is part of the GNU gettext package. If you’re on a Linux system, it’s usually pre-installed or easily installable via your package manager (e.g., sudo apt-get install gettext on Debian/Ubuntu).
msgfmt es_ES.po -o es_ES.mo
Place the compiled .mo file (along with its corresponding .po file, though only the .mo is strictly required by WordPress at runtime) into the languages directory of your theme. For example, if your theme is my-awesome-theme and you have a Spanish translation, you would place es_ES.mo in wp-content/themes/my-awesome-theme/languages/es_ES.mo.
Multi-Language Site Networks (Multisite) and Text Domains
In a WordPress Multisite environment, especially when dealing with multiple languages, the concept of text domains remains the same. However, the management and loading of translation files can become more complex depending on your setup.
Theme Translation Loading in Multisite
When load_theme_textdomain() is called in functions.php, WordPress will look for the translation files in the theme’s directory first. If you’re using a plugin like MultilingualPress or WPML, these plugins often provide their own mechanisms for managing and loading translations, which might override or supplement the default behavior.
For a theme to be truly network-aware and easily manageable across multiple sites in a network, it’s best practice to:
- Place the
languagesdirectory at the root of your theme. - Ensure the text domain is consistently used throughout the theme.
- If using a translation management plugin, follow its specific guidelines for placing and registering translation files. Some plugins might prefer translations to be stored in a central location or within the plugin itself.
Advanced Diagnostics: Troubleshooting Translation Issues
When translations aren’t appearing as expected in a multi-language setup, several diagnostic steps can help pinpoint the problem:
1. Verify Text Domain Consistency
Double-check that the text domain used in load_theme_textdomain() and in all your __(), _e(), etc., calls is *exactly* the same. Typos are common culprits.
Diagnostic Command/Check: Search your theme’s codebase for instances of your text domain. Ensure they match perfectly.
2. Check File Paths and Naming Conventions
Ensure your translation files are correctly named and located. For a site using Spanish (Spain) language, the file must be named es_ES.mo (and es_ES.po) and placed in the languages sub-directory of your theme.
Diagnostic Command/Check:
Manually inspect the file path: wp-content/themes/your-theme-slug/languages/your-locale.mo.
Use SSH or FTP to verify the existence and exact spelling of the file and directory.
3. Confirm Language Locale
The WordPress site’s language setting must match the locale of your translation files. For example, if your site is set to “Español (España)”, WordPress will look for es_ES.mo.
Diagnostic Command/Check:
In the WordPress admin, go to Settings > General. Check the “Site Language” setting.
Alternatively, check the WPLANG constant in your wp-config.php file or the locale option in the wp_options table (for single sites) or wp_X_options table (for multisite, where X is the blog ID).
4. Inspect the .po/.mo Files
Sometimes, the translation itself might be missing or incorrect within the .po file, leading to the .mo file being generated without the expected translations. Ensure the strings are correctly translated and that the file has been compiled properly.
Diagnostic Command/Check:
Open the .po file in a text editor or Poedit. Verify that the strings you expect to be translated are present and correctly mapped.
Recompile the .mo file using msgfmt and re-upload it to ensure no compilation errors occurred.
5. Check for Plugin Interference
Multilingual plugins can sometimes interfere with or manage translations in ways that bypass standard theme loading. If you’re using such a plugin, consult its documentation for how it handles theme translations.
Diagnostic Command/Check: Temporarily deactivate your multilingual plugin. If translations start working, the issue lies with the plugin’s configuration or integration. Re-activate the plugin and review its settings related to theme translation loading.
6. Debugging WordPress Core
For deeper debugging, you can enable WordPress’s debugging features. This can sometimes reveal errors related to file loading or string processing.
// Add this to your wp-config.php file define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); // Logs errors to wp-content/debug.log define( 'WP_DEBUG_DISPLAY', false ); // Prevents errors from being displayed on the front-end @ini_set( 'display_errors', 0 );
Review the wp-content/debug.log file for any relevant error messages, especially those mentioning text domains, language files, or translation lookups.