A bilingual site with Astro: wiring hreflang correctly

Semih2 min read

How to set up URL structure, shared content keys and hreflang tags in Astro so search engines understand your Turkish and English pages correctly.

Building a bilingual site isn’t just translating the text. If you don’t tell the search engine “this page’s English version is here and its Turkish version is there”, the two pages behave like rivals, or users in the wrong country get the wrong language. Let me walk through the structure used on this site.

1. Decide on the URL structure

There are three common options:

Structure Example Note
Subdirectory site.com/en/ Easiest setup, one domain’s authority
Subdomain en.site.com Behaves like a separate site
Separate domain site.co.uk Strong country targeting, hard to maintain

On this site Turkish lives at the root and English under /en/. The URLs are translated too: /hakkimda/ and /en/about/ are the same page in two languages.

2. Give every piece of content a shared key

Translated URLs are nice but create a problem: the system can’t tell from the URL which page is the counterpart of which. The fix is to give both files the same key:

---
title: CRM or ERP?
lang: en
key: crm-vs-erp
---

The Turkish file also says key: crm-vs-erp. Finding the match in Astro content collections is a one-liner:

const twin = all.find((e) => e.data.key === entry.data.key && e.data.lang !== entry.data.lang);

3. Write hreflang tags on every page

Every page’s <head> should state its own URL and its counterpart’s. Listing itself is the rule people forget:

<link rel="alternate" hreflang="tr" href="https://semih.fun/hakkimda/" />
<link rel="alternate" hreflang="en" href="https://semih.fun/en/about/" />
<link rel="alternate" hreflang="x-default" href="https://semih.fun/hakkimda/" />

x-default tells the engine which version to show visitors whose language doesn’t match.

The sitemap delivers hreflang information through a second channel. When URLs are translated, off-the-shelf plugins may not find the match, so on this site the sitemap is generated by hand using the same key logic.

5. Make the language switch go to the counterpart

Sending a user who taps the language switch back to the home page is the most common mistake on bilingual sites. The switch should go to the current page’s counterpart, or to that language’s matching list page if there isn’t one.

Common mistakes

  • Non-reciprocal hreflang: A points to B, but B doesn’t point to A.
  • Writing a country code instead of a language code (hreflang="tr" is right, hreflang="tu" is wrong).
  • Leaving translation to a machine and forgetting to translate the page title and description.

This whole structure works together with the Markdown setup I described in A site without a database.

More from the rulebook

  1. 1 min read

    A site without a database: content management with Markdown and Astro

    This site uses no database. Posts, projects and services are generated from Markdown files. Why I chose this path and how it works.

  2. 2 min read

    GEO: how to write content that AI answers cite as a source

    Search isn't just ten blue links anymore. Structure, language and technical tips for content that answer engines like ChatGPT, Perplexity and Gemini understand and quote.

Chance

Got a project in mind? Let’s open the box: tell me what you want to build and I’ll prepare a proposal with the scope and a roadmap.