Skip to main content

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

ConceptMeaningExample
Text domainIdentifier for translationsgeneratepress-child
Translation functionsWrap strings for translation__( 'Text', 'domain' )
Language files.pot, .po, .molanguages/ folder
Loading domainRegister translations at runtimeload_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

FunctionUse whenOutput
esc_html__()plain textsafe HTML text
esc_attr__()HTML attributessafe attribute
esc_html_e()echo plain textprints 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

NeedUseNotes
translate + escape HTML textesc_html__()safest default
translate + escape attributeesc_attr__()for title, aria-label
echo translated textesc_html_e()prints immediately

Best Practices

PracticeWhy
Use one consistent text domainPrevents missing translations
Wrap strings immediatelyAvoid future refactors
Keep language files in languages/WordPress convention
Use escaping translation functionsSecurity + correctness
Keep strings stableAvoid breaking translations

Troubleshooting

SymptomCauseFix
Strings not translatedDomain mismatchEnsure domain matches Text Domain: and load_*textdomain()
POT generation failsWP-CLI i18n missingInstall WP-CLI i18n package or generate POT elsewhere
Weird charactersEncoding mismatchUse UTF-8 and consistent tooling
Translations load but not usedWrong domain in codeEnsure every string uses generatepress-child

Hands-On

  1. Add one banner string using esc_html__().
  2. Confirm languages/ exists and domain is loaded.
  3. Generate a POT file (if possible) and verify your string appears.
  4. 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