Internationalization (i18n)
warning
Hard-coded user-facing strings become tech debt. If you build for clients or teams, make strings translation-ready from the start.
Internationalization (i18n) Explained
| Concept | Meaning | Example |
|---|---|---|
| Text domain | Identifier for translations | generatepress-child |
| Translation functions | Wrap strings for translation | __( 'Text', 'domain' ) |
| Language files | .pot, .po, .mo | languages/ folder |
| Loading domain | Register translations at runtime | load_child_theme_textdomain() |
Why It Matters
- Translation-ready code supports multilingual sites.
- Even single-language projects benefit (copy changes are centralized).
- Teams can edit text without hunting through templates.
How It Works
WordPress looks up translated strings by text domain and locale. Your theme provides language files and loads them early.
flowchart LR
STR["esc_html__('Text', domain)"] --> LOOKUP[Lookup by locale + domain]
LOOKUP --> MO[.mo translations]
MO --> OUT[Translated output]
Practical Walkthrough
Step 1: Ensure Text Domain: Exists in style.css
verify-text-domain-header.sh
cd /var/www/html
grep -n "^Text Domain:" wp-content/themes/generatepress-child/style.css || true
Step 2: Create languages/ Folder
create-languages-folder.sh
cd /var/www/html
mkdir -p wp-content/themes/generatepress-child/languages
ls -lah wp-content/themes/generatepress-child/languages
Step 3: Load the Text Domain
wp-content/themes/generatepress-child/functions.php
<?php
add_action( 'after_setup_theme', function() {
load_child_theme_textdomain( 'generatepress-child', get_stylesheet_directory() . '/languages' );
} );
Step 4: Wrap Strings in Translation Functions
wp-content/themes/generatepress-child/inc/hooks.php
<?php
add_action( 'generate_before_header', function() {
echo '<div class="gp-i18n-banner">' . esc_html__( 'Welcome', 'generatepress-child' ) . '</div>';
} );
Step 5: Generate a POT File (Optional)
If wp i18n is available:
generate-pot.sh
cd /var/www/html/wp-content/themes/generatepress-child
wp i18n make-pot . languages/generatepress-child.pot 2>/dev/null || echo "wp i18n not available"
Step 6: Check Site Language (Optional)
check-site-language.sh
cd /var/www/html
wp option get WPLANG 2>/dev/null || true
Practical Examples
Example 1: Use the Correct Escaping Variant
| Function | Use when | Output |
|---|---|---|
esc_html__() | plain text | safe HTML text |
esc_attr__() | HTML attributes | safe attribute |
esc_html_e() | echo plain text | prints safe text |
Example 2: Keep Strings Out of CSS
Do not hard-code visible text in background images or pseudo-elements. Keep user-facing text in HTML/PHP so it can be translated.
Example 3: Translation Function Cheat Sheet
| Need | Use | Notes |
|---|---|---|
| translate + escape HTML text | esc_html__() | safest default |
| translate + escape attribute | esc_attr__() | for title, aria-label |
| echo translated text | esc_html_e() | prints immediately |
Best Practices
| Practice | Why |
|---|---|
| Use one consistent text domain | Prevents missing translations |
| Wrap strings immediately | Avoid future refactors |
Keep language files in languages/ | WordPress convention |
| Use escaping translation functions | Security + correctness |
| Keep strings stable | Avoid breaking translations |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Strings not translated | Domain mismatch | Ensure domain matches Text Domain: and load_*textdomain() |
| POT generation fails | WP-CLI i18n missing | Install WP-CLI i18n package or generate POT elsewhere |
| Weird characters | Encoding mismatch | Use UTF-8 and consistent tooling |
| Translations load but not used | Wrong domain in code | Ensure every string uses generatepress-child |
Hands-On
- Add one banner string using
esc_html__(). - Confirm
languages/exists and domain is loaded. - Generate a POT file (if possible) and verify your string appears.
- Add one translated attribute string using
esc_attr__().
Quick Reference
i18n-cheatsheet.sh
cd /var/www/html/wp-content/themes/generatepress-child
grep -R "generatepress-child" -n . | head
What's Next
- Next: WooCommerce & Plugin Overrides
- Related: Namespaces & Autoload