All WordPress HTML Templates Forms & Webhooks AI & Tools
WordPress

Multilingual WordPress: hreflang, Structure and the Plugin Decision

A practitioner’s guide to multilingual WordPress: hreflang rules that actually matter, URL structure, and choosing between Polylang, WPML, TranslatePress and

A real working moment illustrating the theme of an article about multilingual wordpress. Wide 16:9 banner, one strong focal point, magazine editorial quality, authentic and unstaged.

A client came to us last year with a four-language site and a problem: German and French were ranking fine, Spanish and Italian had maybe 11 pages indexed between them out of roughly 400. The plugin was configured. The language switcher worked. Everything looked correct in the admin.

The hreflang tags pointed at URLs with a trailing slash. The server redirected those to the non-slash version with a 301. Google saw an alternate URL that immediately redirected, treated the annotation as unreliable, and quietly dropped the whole cluster. One character per URL, four months of invisibility.

That is what multilingual WordPress actually is in practice: not the translating, which is the easy part now, but the hundred small structural decisions that decide whether search engines believe you. Here’s how we set these up, what we’ve broken, and which plugin we reach for depending on the job.

Key Takeaways

  • Pick subdirectories (/de/, /fr/) unless you have a specific legal, hosting or brand reason not to. Subdomains and ccTLDs split your authority and triple your infrastructure work for a benefit most sites never see.
  • hreflang must be reciprocal, self-referencing, absolute, and point at URLs that return 200 with no redirect. A single 301 in the set invalidates the cluster, and nothing in WordPress warns you.
  • Polylang stores translation relationships as taxonomy terms and adds almost nothing to query cost. WPML adds joins against wpicltranslations to effectively every query, which is survivable on a brochure site and painful on WooCommerce with 5,000 products.
  • Never auto-redirect by IP address. Googlebot crawls predominantly from US IPs, so an IP redirect means it may never see your localised pages at all. Show a dismissible banner instead.
  • Full-page caching and cookie-based language detection are mutually exclusive. If language lives in a cookie rather than the URL, you will serve German to Spanish visitors the moment your cache warms.

Decide the URL structure before you install anything

Three options, and the decision is mostly made for you.

Subdirectories (example.com/de/) are the default answer. One WordPress install, one SSL certificate, one set of backlinks feeding one domain’s authority. Every major plugin supports them. Deployment stays simple because there’s nothing to deploy twice.

Subdomains (de.example.com) make sense when different regional teams genuinely own different sites, or when you need separate hosting per region for latency or data residency. They’re also the natural fit for WordPress Multisite. The cost is real: link equity is harder to consolidate, and you’ll be maintaining separate caches, separate certificates and separate everything else.

ccTLDs (example.de) are the strongest geo-targeting signal available and the most expensive thing on this list. Buy them when you’re selling in Germany with a German legal entity, German pricing, and a German support team. Don’t buy them because somebody read that ccTLDs rank better. You’re building five sites, not one.

One thing worth deciding at the same time: are you translating the slugs? /de/ueber-uns/ ranks for German queries. /de/about-us/ does not, and it looks like an untranslated afterthought to a German reader. Translate them, and set up the redirects from the old English slugs before launch rather than after.

A real working moment that fits an article section about

How hreflang wordpress setups actually break

hreflang is a hint, not a command. Google uses it to decide which version of a page to surface in which locale, and it will discard the entire annotation set if it doesn’t trust it. The trust conditions are strict and mostly undocumented in any single place.

The rules you cannot bend:

  • Self-referencing. The English page must include an hreflang entry pointing at itself. Missing self-reference is the most common error we find in audits.
  • Reciprocal. If /de/ points at /fr/, then /fr/ must point back at /de/. One-way links are ignored.
  • Absolute URLs. Protocol and host included. Relative hreflang does nothing.
  • 200 status, no redirect. Trailing slash mismatches, http to https upgrades, and www normalisation all produce 301s that silently kill the cluster.
  • Canonical agreement. Each page’s canonical must point to itself, not to the English original. Yoast and the SEO plugins occasionally get this wrong on taxonomy archives.
  • Correct codes. ISO 639-1 for language, ISO 3166-1 Alpha-2 for region, in that order. en-GB, not en-UK. zh-Hans for Simplified Chinese. Region alone (hreflang="GB") is invalid.

Add x-default pointing at whatever you serve to a visitor whose locale you don’t target. Usually that’s your English page or a language selector landing page. Omitting it isn’t fatal, but it gives Google something sensible to fall back on.

If you’re hand-rolling this in a custom theme rather than relying on a plugin, the output is trivial. Getting the map right is the work:

addaction( 'wphead', 'scwhreflangtags', 1 );

function scwhreflangtags() {
    if ( is404() || issearch() ) {
        return;
    }

    // ['en' => 'https://example.com/pricing/', 'de' => 'https://example.com/de/preise/']
    // This map MUST include the current page: that is the self-reference.
    $alts = scwtranslationmap( getqueriedobject_id() );

    if ( count( $alts ) < 2 ) {
        return; // A single-language page should emit nothing at all.
    }

    foreach ( $alts as $code => $url ) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr( $code ),
            escurl( usertrailingslashit( $url ) ) // Match your permalink structure exactly.
        );
    }

    $default = $alts['en'] ?? reset( $alts );
    printf(
        '<link rel="alternate" hreflang="x-default" href="%s" />' . "\n",
        escurl( usertrailingslashit( $default ) )
    );
}

Verify from the outside, not from the admin. This is the check we run before every multilingual launch:

# Every hreflang target on a page should return 200 with no redirect_url.
for u in $(curl -s "$1" \
  | grep -o 'rel="alternate" hreflang="[^"]" href="[^"]"' \
  | grep -o 'href="[^"]*"' | cut -d'"' -f2); do
  printf '%s  ' "$(curl -s -o /dev/null -w '%{httpcode} %{redirecturl}' "$u")"
  echo "$u"
done

Any 301 or 302 in that output is a bug to fix before it costs you a quarter of indexing.

The multilingual WordPress plugin decision

Four real options plus Multisite. They differ less in features than in where translations live, and that determines how the site performs and how painful the exit is.

Polylang

Our default for content sites. Translations are separate posts, linked by a hidden post_translations taxonomy, with language itself stored as a term. The relationship lives in core tables, query overhead is close to nothing, and if you deactivate Polylang tomorrow your content is all still there as ordinary posts. The free tier is genuinely usable. Pro adds translated slugs, media duplication, and WooCommerce support via a separate add-on. The translation UI is utilitarian: you’re editing two posts side by side, not in a unified interface.

WPML

The most complete, and the heaviest. It maintains wpicltranslations, wpiclstrings and wpiclstring_translations, and filters queries to join against them. On a 60-page site that’s invisible. On a WooCommerce catalogue with thousands of products and a faceted filter, we’ve seen the string translation table pass 80,000 rows and query times climb accordingly. Buy it when you need the translation management workflow, translator roles, professional translation service integrations, or deep ACF and page builder support. Then budget for object caching and read our notes on why WooCommerce product pages crawl, because you’ll need them.

TranslatePress

Works at the output buffer: it renders the page, diffs the strings, and swaps them using its own trpdictionary* tables. The front-end visual editor is the best in this group and non-technical clients actually use it. The trade-off is that you’re translating rendered output, so anything generated after the buffer (certain JS-injected content, some AJAX fragments) needs manual handling. Good for marketing sites. Not what we’d pick for a 10,000-SKU shop.

Weglot

SaaS. Machine translation out of the box, subdirectory URLs handled by its PHP layer, and you can be live in an afternoon. Pricing is per word per year and scales uncomfortably: a large site can hit four figures annually. Your translations live on their servers. For a startup that needs five languages next week and will revisit later, it’s a defensible call. For a site you plan to own for a decade, you’re renting your own content.

Multisite

One site per language, linked manually or with MultilingualPress. Clean separation, independent plugins and themes per region, no query overhead. Also: five sets of updates, five caches, and hreflang you have to wire yourself. Worth it when regional teams publish independently and the content genuinely diverges rather than being translations of one source.

Whichever you choose, get the whole thing under version control with a real staging flow first. Language plugins touch the database in ways that make “just try it on production” an expensive sentence. Our write-up on staging and deploys for WordPress teams covers the setup we use.

Translation workflow in 2026

Machine translation crossed the usability threshold for European language pairs some time ago. DeepL and the current GPT-class models produce German, French, Spanish and Italian marketing copy that a native speaker will call acceptable but slightly odd. That gap matters on a pricing page and doesn’t matter much on a changelog.

The workflow that works: machine-translate everything, then pay a human to post-edit the pages that convert. Homepage, pricing, the top five landing pages, checkout, legal. Leave the blog archive machine-translated and mark it for review later. This typically cuts translation spend by 70 to 80 percent versus full human translation while keeping quality where revenue happens.

Two things machine translation reliably ruins: brand terminology and UI microcopy. Build a glossary before you start (DeepL and WPML both support one) and lock your product names, feature names and CTA verbs. Otherwise “Get started” becomes four different phrases across four templates in the same language.

Right-to-left is its own project, not a checkbox. Arabic and Hebrew need dir="rtl" on the html element, mirrored layouts, flipped icons that imply direction, and logical CSS properties (margin-inline-start rather than margin-left) throughout. If RTL is in scope, start from a theme that already ships it rather than retrofitting; CanvasWP includes RTL stylesheets out of the box, which removes a week of CSS archaeology.

Caching, redirects and the things that silently break

Put the language in the URL. Always. The moment language detection depends on a cookie or a session, your full-page cache has no way to distinguish requests, and the first German visitor after a cache purge locks German into the cache for everyone. We’ve debugged this on three separate sites and the symptom is always the same: “it only happens sometimes, and never for me.”

Other failure modes worth pre-empting:

  • Automatic IP redirects. Googlebot crawls mostly from US IPs. Redirect by geo and your localised pages may never get crawled. A dismissible suggestion banner with a cookie to remember the dismissal is the correct pattern.
  • One sitemap for everything. Generate a sitemap per language and submit all of them. Yoast and Rank Math do this correctly with Polylang and WPML; verify rather than assume.
  • Untranslated core strings. “Read more”, “Leave a reply”, “Search results for” and the WooCommerce checkout labels live in theme and plugin .po files, not in your page content. Run Loco Translate over the theme and check the front end in each language, logged out.
  • Forms. Validation messages, placeholder text, success states and the confirmation email all need translating, and they’re usually in four different places. If you’re running a static or headless front end alongside, WebForms keeps the endpoint language-agnostic so you only translate the markup.
  • Structured data. inLanguage should match the page. Breadcrumb names should be translated. Most SEO plugins handle this; the custom JSON-LD your developer hand-wrote in 2023 does not.

For bulk work, WP-CLI beats clicking. Polylang and WPML both expose commands, and a loop that assigns languages to 400 existing posts takes thirty seconds instead of an afternoon. If the terminal isn’t your home turf, our WP-CLI primer gets you to the useful commands quickly.

The language switcher itself

Flags are not languages. Which flag do you use for Spanish, and how do your Mexican customers feel about it? Use the language name written in that language, with the correct lang and hreflang attributes so screen readers switch pronunciation:

<nav aria-label="Language">
  <ul class="lang-switcher">
    <li><a href="https://example.com/pricing/" lang="en" hreflang="en" aria-current="page">English</a></li>
    <li><a href="https://example.com/de/preise/" lang="de" hreflang="de">Deutsch</a></li>
    <li><a href="https://example.com/ar/alas3ar/" lang="ar" hreflang="ar" dir="rtl">العربية</a></li>
  </ul>
</nav>

Each link should point to the translation of the current page, not to the translated homepage. Dropping a user from an English product page onto the German homepage is the single most common UX failure in multilingual WordPress, and every plugin on this list supports doing it properly. When no translation exists, either hide that language for that page or send the user to the closest parent. Never to a 404.

Frequently Asked Questions

Do I need hreflang if all my languages are on separate domains?

Yes, and arguably more than ever. Separate ccTLDs give Google a country signal but no language relationship between the pages, so it can’t tell that example.de/preise/ is the German version of example.com/pricing/. Cross-domain hreflang is fully supported, just make sure the annotations are reciprocal across every domain in the set.

Will duplicate content in multiple languages hurt my rankings?

No. Translated content isn’t duplicate content, and Google has stated this repeatedly. The actual risk is near-duplicate content within the same language, for example separate en-US and en-GB pages with identical copy. In that case correct hreflang tells Google which to show where, and you should still differentiate pricing, spelling and contact details so the pages justify existing.

Can I switch from WPML to Polylang later without losing translations?

There’s an official importer that handles the common cases, and we’ve run it successfully on content-only sites. It gets messy with WooCommerce products, ACF field groups, and anything stored in WPML’s string translation tables rather than as posts. Budget a full day, run it on a database copy first, and expect to re-link some translations by hand.

How many languages is too many?

The limit is editorial, not technical. Every language multiplies your content maintenance, so a site with six languages and one content person will have five stale versions within a year. Three well-maintained languages beat eight machine-translated ones that contradict each other on pricing. Add a language when you can name the person responsible for keeping it current.

Does Google still support hreflang in XML sitemaps?

Yes, and it’s the better option for large sites. Sitemap-based annotations keep your HTML head lean and are easier to generate correctly at scale, since you build the full cluster in one place rather than per page. The same reciprocity and status code rules apply, and you shouldn’t declare hreflang in both the sitemap and the head unless the two sets match exactly.

Where to start

If you’re building now: subdirectories, Polylang unless you specifically need WPML’s translation management, translated slugs from day one, language in the URL, and no IP redirects. That configuration covers the large majority of the multilingual sites we ship and it’s the cheapest one to maintain three years later.

If you already have a multilingual site that isn’t ranking, don’t touch the plugin yet. Run the curl loop above against twenty URLs across every language, check that each page’s canonical points at itself, and confirm every language has its own sitemap in Search Console. In our experience that finds the problem about four times out of five, and the fix is usually one line in a template rather than a migration.

hreflang wordpress implementation hreflang x-default tag errors multilingual wordpress multilingual wordpress site setup polylang vs wpml performance translated slugs wordpress seo wordpress subdirectory vs subdomain languages wordpress translation plugin comparison