Quick answer: Making a WordPress theme translation-ready means loading a text domain and wrapping every user-facing string in gettext functions such as __(), _e() and _n(), then generating a POT template that translators turn into PO/MO files for each language. It does not translate the site by itself. It prepares the theme so plugins like Loco Translate, or teams using Poedit, can supply translations without editing PHP templates.
If you build themes for clients who might need Spanish, French, German or any other language later, translation readiness is cheaper on day one than hunting hard-coded English months later.
This guide explains the file types, the code patterns, and the practical checks for WordPress themes in 2018. It focuses on themes; plugins follow the same gettext model.
Translation-ready versus multilingual
These terms get mixed up:
- Translation-ready: the theme’s strings can be translated through WordPress’s i18n system.
- Multilingual site: content (posts, pages, products) exists in multiple languages, usually with WPML, Polylang or similar.
You need both for a fully bilingual site. A translation-ready theme without a multilingual plugin still shows one content language. A multilingual plugin with a theme full of hard-coded English leaves buttons and labels untranslated.
The file types: POT, PO and MO
- POT (Portable Object Template): the master list of extractable strings from your theme. No translations yet.
- PO (Portable Object): a language-specific file with source strings and translations (
es_ES.po,de_DE.po). - MO (Machine Object): the compiled binary WordPress actually loads. Generated from PO.
WordPress looks for MO files in the theme’s languages folder (or in WP_LANG_DIR for certain setups) based on the site locale.
Tools: Poedit is widely used on the desktop. wp i18n make-pot from WP-CLI is increasingly handy for generating POT files. Loco Translate helps inside wp-admin for teams that prefer a browser UI.
Loading the text domain
In your theme’s functions.php (or a setup file):
load_theme_textdomain( 'mytheme', get_template_directory() . '/languages' );
Hook it on after_setup_theme. The text domain string (mytheme) must match across all gettext calls and usually matches the theme slug.
For child themes, load the child text domain as well if the child adds its own strings.
Wrapping strings correctly
Replace hard-coded English with:
__( 'Save changes', 'mytheme' )returns the translated string_e( 'Save changes', 'mytheme' )echoes itesc_html__( ... )/esc_attr__( ... )when outputting into HTML contexts_n( 'One seat', '%d seats', $count, 'mytheme' )for plurals_x( 'Post', 'noun', 'mytheme' )when a word needs translator context
Do not concatenate pieces in ways that break grammar in other languages. Prefer full sentences with placeholders:
sprintf( __( 'Hello, %s', 'mytheme' ), $name )
Avoid variables as the whole gettext string (__( $foo )) because automated extraction cannot see them.
What must be wrapped
- Template labels and button text
- Widget titles your theme registers by default
- Theme Customizer labels and descriptions
- Error messages and notices the theme prints
- Block or shortcode UI strings the theme provides
What usually stays outside theme translation files:
- Post content and titles (CMS content, handled by multilingual plugins or manual translation)
- Options the client types into settings fields (those are content)
Generating and shipping the POT
After strings are wrapped:
- Generate
languages/mytheme.potwith WP-CLI or Poedit - Ship the POT with the theme
- Translators create
mytheme-es_ES.poand compile MO - Place MO files where
load_theme_textdomainexpects them
When you add new strings in an update, regenerate the POT and merge into existing PO files so translators only handle new or changed entries.
Classic editor, Customizer and Gutenberg timing
In May 2018 most sites still use the classic editor day to day, while Gutenberg development is public and heading toward WordPress 5.0. Theme strings in PHP templates remain the main i18n surface either way. If you ship editor styles or block-related strings early, wrap those too. Keep an eye on core i18n guidance as the block editor lands later this year.
The WordPress Customizer labels you register are user-facing: wrap them. Editors notice untranslated Customizer sections quickly on multilingual sites.
Testing that it works
- Install a language pack or a custom MO for a test locale
- Set the site language under Settings → General
- Visit front-end templates and the Customizer
- Confirm no English leftovers in theme chrome
- Switch back and confirm English still works
Also test RTL languages if you claim support: loading translations is necessary but not sufficient; layout mirroring needs CSS consideration.
Common mistakes
- Mismatched text domains (
'mytheme'in one file,'my-theme'in another) - Forgetting to load the text domain
- Translating strings that include raw HTML badly (keep HTML out or give translators clear guidance)
- Editing core or parent theme files instead of using a child theme for extras
- Shipping PO but forgetting compiled MO on environments without auto-compile
Working with translators
Give translators context: screenshots of the UI, character limits for buttons, and notes for ambiguous words. A glossary for product terms keeps “cart”, “basket” and brand phrases consistent.
Do not ask translators to edit PHP. They should work in PO files or a translation UI. When strings change in an update, provide a fresh POT and a short changelog of new strings so they are not retranslating everything blindly.
Continuous maintenance
Every theme release that adds labels should update the POT. Treat i18n as part of QA, not an afterthought before a market expansion. If you maintain multiple client themes, standardise on one extraction command so nobody invents a private process.
FAQ
Is a theme “translation-ready” if it only includes an English POT?
Yes, if strings are wrapped and the POT is generated. Language files for specific locales can be contributed later.
Do I need WPML to be translation-ready?
No. WPML/Polylang handle content and can also help with string translation UIs. Gettext readiness is independent and still required for clean theme chrome.
Can page builders break translation readiness?
Some builder content lives in post meta as English JSON/HTML. That is a content problem as much as a theme problem. Prefer builders and workflows with multilingual support if you know you need multiple languages.
Plugins that help without replacing gettext
Loco Translate, Say What?, and similar tools help edit strings in place. They do not excuse hard-coded English in new templates. Use them for overrides and quick fixes, then fold lasting translations back into proper PO files when you can.
For site content in multiple languages, pair a translation-ready theme with Polylang or WPML and a clear editorial workflow. Theme chrome and post content are different pipelines. Treat them that way in project plans and budgets.
Prepare the theme, then add languages
WordPress translation files give your theme a proper pipeline: text domain, wrapped strings, POT/PO/MO. Do that once, cleanly, and you avoid a painful retrofit when a client asks for a second language.
If you need a WordPress theme or site prepared for multilingual use, our Turn visitors into customers WordPress work includes translation-ready builds and practical setup with the multilingual tools your team chooses.