Caliency.
SEO

Multilingual SEO: hreflang, Slugs and Common Mistakes

Bilingual sites break quietly. The rules Google actually states, and two failures we found on our own site by crawling it rather than reading the code.

C

Caliency

12 min read
A plain gradient in the Caliency brand colours, from near-black to deep teal, with a bright teal rule along the lower edge.

Bilingual sites fail quietly. That is the whole problem with this subject: nothing looks broken. The pages render, the language switcher works, both versions are online — and Google is either ignoring your annotations entirely or serving half your visitors a 404 you have never seen.

This guide covers the rules Google actually states, and then the two failures we found on this site. We found them by crawling it, not by reading the code, which is the point of the last section.

Key Takeaways

  • hreflang must be reciprocal: "if two pages don't both point to each other, the tags will be ignored" (Google).
  • Each version must list itself as well as every other version.
  • Every language version needs its own sitemap entry, not just a mention as an alternate.
  • A prefix is not a translation — the slug should be in the target language.
  • The failures that matter are invisible from inside the code. Crawl the site.

What hreflang is, and what it is not

hreflang is an annotation that tells Google a page has versions in other languages or regions. Google's framing is deliberately soft: the annotations "tell Google about these different variations". It is information, not an instruction, and Google decides what to do with it.

There are three ways to provide it, and Google states that "the three methods are equivalent from Google's perspective and you can choose the method that's the most convenient for your site":

  • HTML tags — <link rel="alternate" hreflang="…"> in the head
  • HTTP headers — for non-HTML files like PDFs
  • XML sitemap — <xhtml:link> children inside each <url>

Pick one and do it completely. Doing two of them halfway is how sites end up with contradictory annotations.

The rule that breaks most implementations

Google states two rules that decide whether your annotations are used at all:

"Each language version must list itself as well as all other language versions."

"If two pages don't both point to each other, the tags will be ignored."

Two consequences that people miss.

Self-reference is required. The English page must include an hreflang="en" pointing at itself. It feels redundant. It is mandatory, and omitting it is a very common way to have your whole set discarded.

Reciprocity is required. If the English page points to the Turkish one but the Turkish page does not point back, Google ignores the annotation — not just the broken half, the annotation. This is why "we added hreflang and nothing happened" is usually not a patience problem.

The practical implication is that hreflang cannot be maintained by hand once you have more than a handful of pages. It has to be generated from a single source that knows every language version of every page, or it will drift the first time somebody adds a page in one language only.

A prefix is not a translation

If your English page is /seo-services, the Turkish version should be /tr/seo-hizmeti — not /tr/seo-services.

The slug is text in the target language, on a URL, which is a place search engines read. Leaving the English slug under a Turkish prefix throws that away for no benefit, and it tells a Turkish visitor scanning a results page that this is an English site with a translation bolted on.

Here is the part that catches teams out, and it caught us: most routing frameworks translate the static parts of a path and not the dynamic ones.

Our site uses localized pathnames, so /seo-services correctly becomes /tr/seo-hizmeti — that mapping is in the routing configuration. But a blog post's slug is content, not configuration. When a visitor whose browser prefers Turkish arrived on an English article, the framework did what it was told: it added the Turkish prefix and kept the slug it was given.

The result was /tr/blog/<english-slug>, which does not exist. Every Turkish-locale visitor following a search result, a shared link or a backlink to an English post landed on a 404 — on precisely the pages that attract the most inbound links.

Nothing in the code looked wrong. The pages were fine, the switcher worked, the sitemap was valid. The bug only existed for a visitor with a particular browser setting arriving at a particular kind of URL.

The fix is to make the slug translation available to the routing layer — the same table the language switcher already uses — so the redirect lands on the translated slug rather than the original one. Ours is generated from the content, so adding an article cannot desynchronise it.

The sitemap shortcut that costs you half your site

The second failure was quieter and, for a site targeting a second language, worse.

Our sitemap emitted one entry per page: the English URL as the <loc>, with the Turkish version carried as an xhtml:link alternate. That looks reasonable, and it is wrong.

Google's instruction is explicit: "Create a separate <url> element for each URL as you would with any other sitemap", with each entry listing every alternate "including itself".

Under the shortcut, the entire Turkish half of the site — the half targeting the higher-volume Turkish keywords — had no entry of its own, no lastmod and no priority. It was mentioned, not listed.

Fixing it doubled the sitemap, from 27 entries to 54. Nothing else changed.

Language codes, briefly

The first code is the language, in ISO 639-1. The optional second is the region, in ISO 3166-1 Alpha 2.

The trap: you cannot use a country code on its own. Google states you "can't specify the country code by itself". This bites specifically in Turkey, because tr is both the ISO language code for Turkish and the country code for Türkiye. hreflang="tr" means the Turkish language, everywhere. If you want Turkish speakers in Türkiye specifically, that is tr-TR.

For most bilingual businesses, language-only is the right choice. Region targeting is for when the same language needs different content in different markets — different prices, different legal terms — not for saying "this is our Turkish page".

And x-default is the fallback "used when no other language/region matches the user's browser setting". If your homepage redirects by language, you need it.

How to actually check it

Both of our failures were invisible to code review and obvious to a crawl, which is why verification here means requesting pages rather than reading them.

Crawl the site, do not read it. Fetch every URL in your sitemap and check the status code. That alone would have shown us 27 pages missing.

Crawl it as the other language. Request your English URLs with Accept-Language: tr-TR,tr and follow the redirects to the end. This is the single check that would have caught our 404, and it takes one line:

curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" \
  -H "Accept-Language: tr-TR,tr" -L https://example.com/blog/some-post

Verify reciprocity mechanically. For each page, fetch it, read its hreflang set, fetch every URL in that set, and confirm each one points back and includes itself. It is a short script and it is the only reliable way; reading the head of three pages proves nothing about the other ninety.

Check the sitemap has an entry per language, not per page.

We now run the language check on every article as part of the publishing gate, precisely because this class of bug returns silently. If you want the wider picture of what technical SEO covers, what SEO actually consists of sets out the layers, and our SEO service treats internationalisation as part of the technical foundation rather than an add-on.

In short

Make the annotations reciprocal and self-referencing, give every language its own sitemap entry, translate the slug and not just the prefix, and use language codes correctly. Then verify by crawling — as both languages — because the failures in this area do not look like failures.

If you have a bilingual site and no idea whether any of this is right, that is worth twenty minutes of someone's attention before it is worth a project.

Got a project in mind?

We'll scope it and give you a fixed price on one call.

Start a project →