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.

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​