Tutorials

i18n SEO: The Complete Guide to Hreflang Tags, Locale URLs, and Multilingual Search Rankings

Eray Gündoğmuş
Eray Gündoğmuş
·12 min read
Share
i18n SEO: The Complete Guide to Hreflang Tags, Locale URLs, and Multilingual Search Rankings

i18n SEO: The Complete Guide to Hreflang Tags, Locale URLs, and Multilingual Search Rankings

Multilingual SEO is not just "regular SEO but in another language." It's a different problem with different failure modes, and most i18n guides never mention it. You can have perfectly translated content, a beautiful localized UI, and still watch your French pages get served to German users — because you skipped a few technical details that Google cares about.

This guide covers the full stack of international SEO: URL structure, hreflang implementation, locale-specific metadata, sitemaps, structured data, and the content localization decisions that actually move rankings. There are code examples for Next.js, SvelteKit, and Vue, because the implementation details matter.

URL Structure Strategies

Before you write a single hreflang tag, you need to decide how your URLs are structured. This decision is permanent — changing it later requires migration work and temporary ranking loss. Get it right the first time.

You have three options:

StrategyExampleProsCons
ccTLDexample.de, example.frStrongest geo-signal to Google; builds trust with local usersExpensive; requires separate domain management; harder to share domain authority
Subdomainde.example.com, fr.example.comEasy to host separately; clear separationGoogle treats subdomains semi-independently; shared link equity is inconsistent
Subdirectoryexample.com/de/, example.com/fr/Shares domain authority; easiest to implement; best for most teamsSlightly weaker geo-signal than ccTLD

The recommendation for most teams: subdirectory.

Unless you have strong reasons to use ccTLDs (e.g., you're a large enterprise entering markets where local domain trust matters significantly) or subdomains (e.g., you need separate hosting infrastructure per region), subdirectories are the pragmatic choice. They inherit your root domain's authority, they're the easiest to implement correctly, and Google handles them well when combined with proper hreflang configuration.

One more thing: always use lowercase, hyphen-separated locale codes in URLs. /en-us/ is fine; /en_US/ is not. Consistency matters.

Hreflang Implementation

Hreflang is the mechanism that tells Google which version of a page to serve to which user. Get it wrong, and Google either ignores it or serves the wrong locale to the wrong user — which tanks both rankings and bounce rates.

The Basic Syntax

<link rel="alternate" hreflang="en" href="https://example.com/en/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />

Each locale page must reference all other locale variants, including itself. If your English page doesn't have a self-referencing hreflang tag, Google may ignore the entire set.

The x-default Tag

x-default tells Google which page to show when no other locale matches. Use it for your catch-all page — typically your root URL or a language selector page. It is not optional. Missing x-default is one of the most common hreflang mistakes.

Locale Codes: Language vs Region

Google accepts both language-only codes (en, de, fr) and language-region codes (en-US, en-GB, de-AT). Use the most specific code you actually support:

  • If you have one English version for all English speakers: en
  • If you have distinct US and UK versions: en-US and en-GB
  • If you have German content targeting Austria specifically: de-AT

Mixing en and en-US in the same hreflang set is a common mistake that confuses Google's signal. Pick one convention and stick to it across your entire site.

Three Ways to Implement Hreflang

1. HTML <link> tags in <head> (recommended for most setups)

Best for server-rendered or statically generated sites. Fast, explicit, easy to validate.

2. HTTP headers

Best for non-HTML content (PDFs, etc.) or when you can't modify the HTML head.

Link: <https://example.com/en/>; rel="alternate"; hreflang="en",
      <https://example.com/de/>; rel="alternate"; hreflang="de"

3. XML sitemap annotations

Best for large sites where adding hreflang to every page's HTML is impractical.

<url>
  <loc>https://example.com/en/</loc>
  <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/"/>
  <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/"/>
  <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/"/>
</url>

Don't mix methods on the same site. Pick one and use it consistently.

Common Hreflang Mistakes That Tank Rankings

  • Non-reciprocal tags: Page A references page B, but page B doesn't reference page A. Google ignores the entire set.
  • Wrong locale codes: en-uk instead of en-GB, or zh when you mean zh-Hans. Use BCP 47 codes.
  • Missing self-reference: Every page must include itself in its own hreflang set.
  • Broken URLs: Any hreflang URL that returns non-200 invalidates the tag.
  • Mixing implementation methods: HTML tags on some pages, sitemap on others, HTTP headers on a few more. Google gets confused.

Locale-Specific Metadata

Translated page content is not enough. Every locale needs its own fully translated metadata — title tags, meta descriptions, Open Graph tags, and canonical URLs.

The Required Set Per Locale

<!-- Locale-specific title and description -->
<title>Vollständiger Leitfaden zur i18n-SEO | Better i18n</title>
<meta name="description" content="Alles, was Sie über mehrsprachige SEO wissen müssen..." />

<!-- Canonical URL for this locale -->
<link rel="canonical" href="https://example.com/de/blog/i18n-seo-guide/" />

<!-- Open Graph with locale -->
<meta property="og:locale" content="de_DE" />
<meta property="og:locale:alternate" content="en_US" />
<meta property="og:title" content="Vollständiger Leitfaden zur i18n-SEO" />
<meta property="og:url" content="https://example.com/de/blog/i18n-seo-guide/" />

<!-- Hreflang set -->
<link rel="alternate" hreflang="en" href="https://example.com/en/blog/i18n-seo-guide/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/blog/i18n-seo-guide/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/blog/i18n-seo-guide/" />

Note that Open Graph uses underscore-separated locale codes (de_DE, en_US) while hreflang uses hyphen-separated BCP 47 codes (de-DE, en-US). This inconsistency is a known pain point and a frequent source of bugs.

Next.js Implementation

Next.js 13+ generateMetadata() makes locale-aware metadata straightforward:

// app/[locale]/blog/[slug]/page.tsx
import { Metadata } from 'next'

const locales = ['en', 'de', 'fr', 'es']

export async function generateMetadata({
  params,
}: {
  params: { locale: string; slug: string }
}): Promise<Metadata> {
  const { locale, slug } = params
  const post = await getPost(slug, locale)

  const alternates = locales.reduce(
    (acc, l) => ({
      ...acc,
      [l]: `https://example.com/${l}/blog/${slug}/`,
    }),
    {} as Record<string, string>
  )

  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: `https://example.com/${locale}/blog/${slug}/`,
      languages: {
        ...alternates,
        'x-default': `https://example.com/en/blog/${slug}/`,
      },
    },
    openGraph: {
      title: post.title,
      description: post.excerpt,
      url: `https://example.com/${locale}/blog/${slug}/`,
      locale: locale.replace('-', '_'),
      alternateLocale: locales
        .filter((l) => l !== locale)
        .map((l) => l.replace('-', '_')),
    },
  }
}

Next.js automatically renders the alternates.languages object as <link rel="alternate" hreflang> tags. For a full treatment of Next.js i18n patterns with App Router and Server Components, see Next.js App Router i18n: Server Components and RSC Patterns for 2026.

SvelteKit Implementation

<!-- src/routes/[locale]/blog/[slug]/+page.svelte -->
<script lang="ts">
  export let data: PageData

  const { post, locale, slug, locales } = data
  const baseUrl = 'https://example.com'
</script>

<svelte:head>
  <title>{post.title}</title>
  <meta name="description" content={post.excerpt} />
  <link rel="canonical" href="{baseUrl}/{locale}/blog/{slug}/" />

  {#each locales as l}
    <link
      rel="alternate"
      hreflang={l}
      href="{baseUrl}/{l}/blog/{slug}/"
    />
  {/each}
  <link
    rel="alternate"
    hreflang="x-default"
    href="{baseUrl}/en/blog/{slug}/"
  />

  <meta property="og:locale" content={locale.replace('-', '_')} />
  <meta property="og:url" content="{baseUrl}/{locale}/blog/{slug}/" />
</svelte:head>

For a complete guide to building multilingual SvelteKit apps with type-safe translations, see SvelteKit i18n: Building Type-Safe Multilingual Apps with Svelte 5.

Vue (with @vueuse/head)

// composables/useSeoMeta.ts
import { useHead } from '@vueuse/head'

export function useLocalizedMeta(
  post: Post,
  locale: string,
  slug: string,
  locales: string[]
) {
  const baseUrl = 'https://example.com'

  useHead({
    title: post.title,
    meta: [
      { name: 'description', content: post.excerpt },
      { property: 'og:title', content: post.title },
      { property: 'og:locale', content: locale.replace('-', '_') },
      { property: 'og:url', content: `${baseUrl}/${locale}/blog/${slug}/` },
    ],
    link: [
      { rel: 'canonical', href: `${baseUrl}/${locale}/blog/${slug}/` },
      ...locales.map((l) => ({
        rel: 'alternate' as const,
        hreflang: l,
        href: `${baseUrl}/${l}/blog/${slug}/`,
      })),
      {
        rel: 'alternate',
        hreflang: 'x-default',
        href: `${baseUrl}/en/blog/${slug}/`,
      },
    ],
  })
}

Sitemaps for Multilingual Sites

A well-structured sitemap is especially important for multilingual sites because it gives Google an explicit map of every locale variant.

XML Sitemap with Hreflang Annotations

<?xml version="1.0" encoding="UTF-8"?>
<urlset
  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
>
  <url>
    <loc>https://example.com/en/blog/i18n-seo-guide/</loc>
    <lastmod>2026-03-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/blog/i18n-seo-guide/"/>
  </url>

  <url>
    <loc>https://example.com/de/blog/i18n-seo-guide/</loc>
    <lastmod>2026-03-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/i18n-seo-guide/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/blog/i18n-seo-guide/"/>
  </url>
</urlset>

Every locale variant of a page must appear as its own <url> entry, and each entry must include hreflang annotations for all variants. This is redundant by design.

Sitemap Index for Large Sites

If you have many locales and many pages, a flat sitemap will become unwieldy. Use a sitemap index with separate locale sitemaps:

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://example.com/sitemap-en.xml</loc>
    <lastmod>2026-03-01</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-de.xml</loc>
    <lastmod>2026-03-01</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-fr.xml</loc>
    <lastmod>2026-03-01</lastmod>
  </sitemap>
</sitemapindex>

Submit the sitemap index URL to Google Search Console, not the individual sitemaps. GSC will crawl all the children automatically.

Content Localization vs Literal Translation

This is where multilingual SEO diverges most sharply from multilingual UX. For usability, a competent translation might be sufficient. For SEO, it usually isn't.

Why Machine Translation Underperforms

Machine-translated content tends to fail for a few reasons:

  1. Keyword mismatch: Users in different markets search differently. The German phrase for "project management software" might be literally "Projektmanagementsoftware" — but German-speaking users might actually search for "Aufgabenverwaltung" or "Team-Kollaborationstool." A literal translation of your English keywords misses these real search patterns.

  2. Thin content signals: Google's quality systems can detect when content is thin or unnatural. Auto-translated pages often lack the depth and specificity that native-language pages have.

  3. Local context is missing: A page about "invoicing software" for the US market might emphasize IRS compliance. The same page for Germany needs to emphasize DATEV integration and GoBD compliance. That context doesn't come from translation.

What to Do Instead

  • Per-locale keyword research: Use Google Keyword Planner or Ahrefs with the target locale and language set explicitly. Don't assume your English keywords translate.
  • Search intent varies by market: The same product query might have commercial intent in one market and informational intent in another. Adjust your content accordingly.
  • Localize, don't just translate: Dates, currencies, units, cultural references, legal requirements, and local competitors all need to be adapted, not just converted.

For an in-depth treatment of keyword research across languages and the full multilingual SEO strategy, see Multilingual SEO: The Complete Guide to Ranking in Every Language.

This is also where a platform like Better i18n helps — not just for translation management, but for maintaining glossaries that ensure your localized terminology stays consistent and intentional across every market.

Technical Implementation Checklist

Before shipping multilingual support, verify:

URL structure:

  • Consistent locale prefix format across all pages (/en/, /de/, etc.)
  • Lowercase, hyphen-separated locale codes
  • All locale URLs return 200 (not redirects)

Hreflang:

  • Every locale page includes hreflang tags for all other locales
  • Every page includes a self-referencing hreflang tag
  • x-default is set on all pages
  • All hreflang URLs are absolute (not relative)
  • Hreflang is implemented with one method only (HTML tags OR sitemap OR HTTP headers)
  • All hreflang tags are reciprocal

Metadata:

  • Title tags translated per locale
  • Meta descriptions translated per locale
  • Canonical URLs point to the correct locale URL
  • Open Graph og:locale set per locale
  • Open Graph og:locale:alternate lists other locales

Sitemap:

  • All locale variants included in sitemap
  • Hreflang annotations in sitemap match HTML head tags
  • Sitemap submitted to Google Search Console

Google Search Console for i18n

Google Search Console is your primary diagnostic tool for international SEO. Use it actively.

International Targeting Report

In GSC, navigate to Legacy tools and reports > International Targeting. Here you can:

  • Set a geographic target for the entire site (useful for ccTLDs and subdomains)
  • View the hreflang tab for detected errors in your hreflang implementation

The hreflang report surfaces the most common implementation mistakes: missing return tags, invalid locale codes, and URLs returning non-200 responses.

Coverage Report by Locale

Use the URL prefix property type in GSC to set up separate properties per locale (e.g., https://example.com/de/). This lets you monitor crawl coverage, indexing status, and search performance for each locale independently.

Performance Report Filtering

In the Performance report, filter by country to see how each locale is ranking in its target market. Cross-reference with your locale configuration to verify that the right pages are ranking in the right countries.

Common Mistakes

1. Duplicate Content Across Locales

Serving the same content at multiple URLs — example.com/en/ and example.com/ both returning identical English content — without proper canonical or hreflang configuration creates duplicate content signals that dilute ranking authority. Always set explicit canonicals and ensure your root URL either redirects or has a clear relationship to a specific locale.

2. Wrong Locale Codes

The most frequent errors:

  • en-uk instead of en-GB (region codes are uppercase)
  • zh instead of zh-Hans or zh-Hant for simplified/traditional Chinese
  • pt instead of pt-BR or pt-PT when you serve distinct Portuguese variants
  • Using IETF language tags inconsistently (mixing en-US and en_US)

Reference the IANA Language Subtag Registry or BCP 47 when in doubt.

3. Mixing Hreflang Methods

If some pages use HTML <link> tags and others use sitemap annotations, Google may apply inconsistent treatment. Choose one method for your entire site.

4. Orphaned Locale Pages

A locale page that isn't linked to from any other page — and isn't in your sitemap — is effectively invisible. Ensure that every locale of every page is discoverable through internal linking and sitemap coverage.

5. Not Translating Metadata

Translating the body content but leaving title tags and meta descriptions in English is a common shortcut. It results in English search snippets appearing in non-English SERPs, which lowers click-through rates even when rankings are good.

Structured Data for Multilingual Sites

Structured data (JSON-LD) needs to be localized alongside your HTML content.

Organization Schema with Language Variants

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Acme Corp",
  "url": "https://example.com/de/",
  "inLanguage": "de-DE",
  "sameAs": [
    "https://example.com/en/",
    "https://example.com/fr/"
  ]
}

Localized Article Schema

{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "Vollständiger Leitfaden zur i18n-SEO",
  "inLanguage": "de-DE",
  "url": "https://example.com/de/blog/i18n-seo-guide/",
  "datePublished": "2026-03-01",
  "author": {
    "@type": "Organization",
    "name": "Acme Corp"
  }
}

Multilingual FAQ Schema

If you have localized FAQ content, each locale's FAQ schema should use that locale's questions and answers — not the English version:

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "inLanguage": "de-DE",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Was ist hreflang?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Hreflang ist ein HTML-Attribut, das Google mitteilt, welche Sprachversion einer Seite für welche Nutzer gedacht ist."
      }
    }
  ]
}

The inLanguage property is the key addition. Without it, Google may apply your structured data to the wrong locale context.

Conclusion

Multilingual SEO is genuinely complex — there's no way to simplify it without hiding the parts that matter. The hreflang spec is verbose and unforgiving. URL structure decisions are hard to reverse. Content localization requires market-specific expertise that translation alone can't provide.

The good news: most of your competitors are getting this wrong. Correct hreflang implementation, locale-specific metadata, and actually localized content (not just translated content) are competitive advantages in international markets.

To summarize the full checklist:

  1. Choose URL structure early — subdirectory for most teams
  2. Implement hreflang with one method — HTML tags are easiest for most setups
  3. Include all four elements: self-referencing tag, all locale variants, x-default, reciprocal tags
  4. Translate all metadata — titles, descriptions, Open Graph, structured data
  5. Build a proper sitemap — with hreflang annotations for every locale of every page
  6. Do locale-specific keyword research — don't assume search terms translate directly
  7. Monitor in GSC — hreflang report catches implementation errors before they cost you rankings

For developer teams managing multiple locales at scale, the translation management layer matters as much as the technical implementation. Check out Better i18n for developers and how it integrates with Next.js — including CDN delivery of translation updates without redeployment and AI translation with glossary enforcement that keeps your localized terminology consistent across markets.

If you're just starting with i18n, the what is i18n primer is a good foundation before diving into the SEO layer.

Better i18n is a developer-first localization platform built for modern frontend teams. Type-safe SDKs, Git-based workflows, CDN delivery, and AI translation with glossary enforcement — without locale files in your repo.


Take your app global with better-i18n

better-i18n combines AI-powered translations, git-native workflows, and global CDN delivery into one developer-first platform. Stop managing spreadsheets and start shipping in every language.

Get started free → · Explore features · Read the docs