I got a frantic call from a client with a big multilingual WooCommerce site. Their marketing head in the UK decided to A/B test some button text, changing “Proceed to Checkout” to “Confirm and Pay.” A tiny, sensible change. Except it wasn’t. The moment they hit save, the German, French, and Spanish versions of that button broke, reverting to the new English text. Total nightmare. Their support tickets lit up. The problem isn’t the client; it’s the default way WordPress handles translations.
WordPress uses the literal string (“Proceed to Checkout”) as the lookup key. So when the key changed, all the connections to the existing translations were severed. This is a huge, fragile flaw if you’re running a serious site. You can’t have a content editor accidentally breaking localization for three other languages. This is where WordPress translation placeholders, an idea I saw detailed over at carlalexander.ca, become essential.
Why your translation method is brittle
My first, cynical thought was to tell the client’s team, “Here’s a list of strings you are forbidden to edit. Ever.” But that’s not a solution. That’s just me being a difficult developer. It creates friction and stops the marketing team from doing their job. A developer’s job is to build robust systems, not to tell people “you’re holding it wrong.”
The real fix is to stop using the display text as the identifier. Instead of this:
_e( 'Confirm and Pay', 'my-text-domain' );You use a stable, unchanging key. A placeholder. Like this:
_e( 'checkout.payment_button', 'my-text-domain' );This key, checkout.payment_button, never changes. Now the marketing team can edit the English translation of that key from “Confirm and Pay” to “Buy Now!” and it won’t affect the German version one bit. Problem solved, right?
Almost. Here’s the kicker: if a translation doesn’t exist for a given language, WordPress will just print the raw key. A user might see “checkout.payment_button” on the frontend. Not good. We need a reliable fallback.
Forcing a fallback locale
This is the part that feels like a clever hack but is actually the proper way to do it. You need to create a complete English (en_US) translation file for your plugin, mapping your keys to their English values. Then, you force WordPress to use this file as the ultimate default for your plugin’s text domain. You can do it with a simple filter.
/**
* Enforces the 'en_US' locale for our plugin's text domain.
*
* This ensures that even if a translation is missing for the site's
* current language, we fall back to our English strings instead of
* showing the raw placeholder keys.
*
* @param string $locale The locale for the given domain.
* @param string $domain The text domain.
* @return string The modified locale.
*/
function myplugin_enforce_default_locale( $locale, $domain ) {
if ( 'my-text-domain' === $domain ) {
// Here's the magic. We just force it to American English.
return 'en_US';
}
return $locale;
}
add_filter( 'plugin_locale', 'myplugin_enforce_default_locale', 10, 2 );With this filter in place, if WordPress is running in German (de_DE) and can’t find a translation for checkout.payment_button in your German .mo file, the plugin_locale filter kicks in for your text domain. It tells WordPress, “Fine, just use the en_US file for this one string.” The user sees proper English text instead of a broken key. And that was it.
So what’s the point?
Using translation placeholders isn’t the default in WordPress, but it holds up better. It separates content from code, so your plugin or theme keeps working when editors change the wording on a live site.
- It’s stable: content editors can’t break translations by fixing a typo.
- It’s clearer: a key like
admin.error.api_key_invalidtells you more than the bare string “An error occurred.” - It’s maintainable: it keeps international users from seeing broken text, and keeps you off emergency support calls.
This stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.