Skip to content
SEO

Multilingual SEO & hreflang

Implement hreflang tags and multilingual SEO for international websites.

stellae.design

8 min read

Introduction

If your website serves content in multiple languages or targets multiple regions, you need multilingual SEO. Without it, search engines may show the wrong language version or treat translations as duplicate content.

The hreflang attribute tells search engines which language version to show which users. It prevents duplicate content issues between translations.

This guide covers hreflang implementation, URL structure options, and common international SEO challenges.


Key Concepts

hreflang Tags

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

URL Structure Options

javascript
// Subdirectories (recommended)
https://example.com/about       // English
https://example.com/de/about    // German

// Subdomains
https://en.example.com/about

// Separate domains
https://example.de/about

Practical Examples

1. Next.js i18n with hreflang

typescript
export async function generateMetadata({ params }) {
  const locales = ['en', 'de', 'fr', 'es'];
  return {
    alternates: {
      canonical: `/${params.locale}/about`,
      languages: Object.fromEntries(
        locales.map(l => [l, `/${l}/about`])
      ),
    },
  };
}

2. hreflang in Sitemap

xml
<?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/about</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/about"/>
  </url>
</urlset>

3. Language Detection Middleware

typescript
import { match } from '@formatjs/intl-localematcher';

export function middleware(request) {
  const { pathname } = request.nextUrl;
  const locales = ['en', 'de', 'fr'];
  const hasLocale = locales.some(l => pathname.startsWith(`/${l}`));
  if (hasLocale) return;
  const locale = match(negotiator.languages(), locales, 'en');
  return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}

Best Practices

  • ✅ Include hreflang on every page referencing all language versions
  • ✅ Always include x-default for fallback
  • ✅ Use subdirectory URL structure for simplicity
  • ✅ Each translation canonicals to itself
  • ❌ Don't auto-redirect by IP without language switch option
  • ❌ Don't use machine translation without review

Common Pitfalls

  • 🚫 Asymmetric hreflang — if A links to B, B must link to A
  • 🚫 Missing x-default
  • 🚫 Canonical conflicts — each language should canonical to itself
  • 🚫 Mixing hreflang in head and sitemap — pick one

SEOseo

Was this article helpful?