24.3 Translation of Pages: Linking Translated Versions
Alright, let’s talk about linking translated pages. This is where the rubber meets the road. You’ve painstakingly translated your content, and now you need to tell both users and search engines that “this page in French is the same as that page in English.” Get this wrong, and you’ll have a fragmented, confusing mess where your SEO value is diluted and users can’t easily switch languages. It’s a bad time.
The magic spell for this is the <link> tag with hreflang and rel="alternate" attributes. You stick it in the <head> of your HTML. It’s essentially a set of signposts saying, “Hey, the same content in other languages lives over here.”
The Anatomy of an hreflang Tag
Let’s break down a typical tag. Say you have an English page at /about-us and a Spanish translation at /es/sobre-nosotros.
In the <head> of your English page, you’d include:
<link rel="alternate" hreflang="es" href="https://www.yoursite.com/es/sobre-nosotros" />
<link rel="alternate" hreflang="x-default" href="https://www.yoursite.com/about-us" />
<link rel="alternate" hreflang="en" href="https://www.yoursite.com/about-us" />
And in the <head> of your Spanish page, you’d reciprocate:
<link rel="alternate" hreflang="en" href="https://www.yoursite.com/about-us" />
<link rel="alternate" hreflang="x-default" href="https://www.yoursite.com/about-us" />
<link rel="alternate" hreflang="es" href="https://www.yoursite.com/es/sobre-nosotros" />
A few crucial details:
- Use absolute URLs. Don’t try to be clever with relative paths. The bots aren’t that smart.
- It’s bidirectional. Every version of the page must list itself and all the other alternates. If Page A points to Page B, Page B must point back to Page A. Forgetting this is the most common mistake, and it silently breaks everything.
hreflang="en": The language code (ISO 639-1). “English” in this case.hreflang="es-ES": You can get fancy and specify a region (ISO 3166-1 Alpha 2). This is for when you have Spanish for Spain (es-ES) vs. Spanish for Mexico (es-MX). Only use this if the content actually is different for those regions.hreflang="x-default": This points to the fallback page you want to show when no other language is a better match. It’s not mandatory, but it’s a very good idea. Usually, it’s your primary language (e.g., English) or a language-neutral page.
Why This Isn’t Just a “Good Idea”
This isn’t just a nice-to-have feature for a fancy dropdown menu. Search engines, primarily Google, rely on these tags to:
- Serve the correct language version in search results. A user searching from Paris will get the
fr-FRversion, not theenone. - Consolidate indexing properties. It tells them that all these pages are the same content, so they shouldn’t treat them as duplicate content (which hurts SEO). Instead, they transfer “link equity” and ranking signals between them.
If you don’t implement this, you are essentially asking Google to guess which pages are translations. They’re bad at guessing.
Implementing This Without Losing Your Mind
Doing this manually in your <head> is a recipe for errors and maintenance hell. You’ll forget to add a reciprocal link for the new Taiwanese Mandarin page you just added, and the whole system will fray. So, don’t do it manually.
The Right Way: Do It Programmatically Your backend framework or site generator should build this list for you. Here’s a conceptual example in Python (Django-flavored):
# In your base template or a context processor
def get_hreflang_tags(request, current_page_id):
translations = get_translations_for_page(current_page_id) # Your logic here
absolute_urls = {}
for language_code, relative_path in translations.items():
absolute_url = request.build_absolute_uri(relative_path)
absolute_urls[language_code] = absolute_url
return absolute_urls
# Then in your template
{% for lang_code, url in hreflang_tags.items %}
<link rel="alternate" hreflang="{{ lang_code }}" href="{{ url }}" />
{% endfor %}
<link rel="alternate" hreflang="x-default" href="{{ hreflang_tags.en }}" />
The Even Better Way: Use an HTTP Header For non-HTML files (like PDFs), you can send the same information via an HTTP header. This is also perfectly valid for HTML, though less common.
Link: <https://www.yoursite.com/es/sobre-nosotros>; rel="alternate"; hreflang="es", <https://www.yoursite.com/about-us>; rel="alternate"; hreflang="x-default"
The “Please Don’t Make Me Think” Way: Use a Sitemap You can also declare all these relationships in your XML sitemap. This is often easier for large, complex sites. It’s a perfectly valid method Google supports.
<url>
<loc>https://www.yoursite.com/about-us</loc>
<xhtml:link rel="alternate" hreflang="es" href="https://www.yoursite.com/es/sobre-nosotros"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://www.yoursite.com/about-us"/>
<xhtml:link rel="alternate" hreflang="en" href="https://www.yoursite.com/about-us"/>
</url>
No matter which method you choose (and you can use more than one), the key is consistency. The information must be the same across all of them. The bots will cross-reference everything, and if they find a discrepancy, they’ll probably just ignore all of it. It’s like showing up to a passport check with conflicting IDs. You’re not going anywhere.