Design wechseln

Next.js Mehrsprachige SEO: Vollständiger Leitfaden – Suchmaschinen indexieren jede Sprache korrekt

Easton editorial illustration: island architecture model

Sie haben eine mehrsprachige Website sorgfältig aufgebaut – und trotzdem zeigt die Suche die falsche Sprachversion. Nutzer suchen auf Chinesisch, landen aber auf der englischen Seite. Verschiedene Sprachversionen konkurrieren in den Suchergebnissen miteinander und drücken das Gesamtranking.

Typische Symptome einer schlecht konfigurierten Mehrsprach-SEO. Laut Google-Statistiken haben über 60 % der mehrsprachigen Websites hreflang-Konfigurationsfehler – mit spürbaren Auswirkungen auf Internationalisierung und Nutzererfahrung.

60%+
Fehlerrate bei mehrsprachigen Websites
Über 60 % der mehrsprachigen Websites haben hreflang-Konfigurationsfehler

Dieser Artikel zeigt, wie Sie Mehrsprach-SEO in Next.js korrekt umsetzen:

  • hreflang-Tags richtig konfigurieren – Sprachversionen nicht verwechseln
  • Mehrsprachige Sitemaps generieren – Indexierung beschleunigen
  • URL-Struktur best practices – die optimale Internationalisierungsstrategie wählen
  • Typische Fehler finden und beheben – Probleme schnell lokalisieren

Ob Pages Router oder App Router – für beide finden Sie passende Lösungen.

1. Kernkonzepte der Mehrsprach-SEO

1.1 Was ist hreflang?

hreflang ist ein HTML-Attribut, das Suchmaschinen Zielsprache und -region einer Seite mitteilt. Hauptfunktionen:

  1. Duplicate-Content-Strafe vermeiden – Suchmaschinen erkennen: verschiedene Sprachversionen sind Übersetzungen desselben Inhalts, kein Duplicate Content
  2. Nutzer präzise matchen – passende Sprachversion je nach Sprache und Region des Nutzers
  3. UX verbessern – Nutzer sehen nicht die falsche Sprache

1.2 Wie Google Mehrsprach-Inhalte verarbeitet

Beim Crawlen einer mehrsprachigen Website geht Google so vor:

  1. Sprache erkennen (HTML-lang, hreflang, Inhaltsanalyse)
  2. hreflang-Tags auswerten und Beziehungen zwischen Sprachversionen verstehen
  3. passende Version in den Suchergebnissen anzeigen
  4. SEO-Signale der Sprachversionen bündeln (statt gegeneinander konkurrieren zu lassen)

1.3 Typische SEO-Fehler

Fehler 1: Fehlende hreflang-Tags

<!-- ❌ Fehler: keine hreflang-Tags -->
<head>
  <title>My Website</title>
  <link rel="canonical" href="https://example.com/en/about" />
</head>

Folge: Suchmaschinen erkennen die Sprachbeziehungen nicht – falsche Sprachversionen können in den Ergebnissen erscheinen.

Fehler 2: Asymmetrische hreflang-Konfiguration

<!-- Englische Seite -->
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />

<!-- ❌ Chinesische Seite – Fehler: hreflang fehlt -->
<!-- Auf jeder Sprachversion müssen vollständige hreflang-Tags stehen -->

Folge: Google verlangt symmetrische hreflang-Konfiguration – einseitige Angaben werden ignoriert.

Fehler 3: Falsche Sprachcodes

<!-- ❌ Fehler: ungültige Sprachcodes -->
<link rel="alternate" hreflang="cn" href="..." /> <!-- korrekt: zh -->
<link rel="alternate" hreflang="en-us" href="..." /> <!-- korrekt: en-US, Groß-/Kleinschreibung beachten -->

Folge: Suchmaschinen erkennen die Codes nicht – hreflang wirkt nicht.

2. URL-Strategie wählen

Vor der Umsetzung eine passende URL-Strategie festlegen – sie beeinflusst SEO, UX und technische Implementierung.

2.1 Drei gängige URL-Strategien im Vergleich

StrategieBeispielSEO-AuswirkungImplementierungEmpfehlung
Unterverzeichnisexample.com/en/
example.com/zh/
⭐⭐⭐⭐⭐ Beste⭐⭐⭐ Mittel⭐⭐⭐⭐⭐
Subdomainen.example.com
zh.example.com
⭐⭐⭐ Mittel⭐⭐⭐⭐ Komplex⭐⭐⭐
URL-Parameterexample.com?lang=en⭐⭐ Schwach⭐⭐⭐⭐⭐ Einfach⭐⭐

2.2 Strategien im Detail

Option 1: Unterverzeichnis (empfohlen)

Vorteile:

  • SEO-Signale bündeln sich auf der Hauptdomain – besseres Gesamtranking
  • Einfache Konfiguration, keine extra Domains oder SSL-Zertifikate
  • Wartbar und skalierbar, einheitliches Deployment
  • Native Next.js-Unterstützung

Nachteile:

  • Alle Sprachen teilen sich eine Domain – kein DNS-Tuning pro Markt

Next.js-Implementierung:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'zh', 'ja', 'de'],
    defaultLocale: 'en',
    localeDetection: true // Nutzersprache automatisch erkennen
  }
}

Option 2: Subdomain

Vorteile:

  • Pro Markt eigener Server möglich (z. B. China separat)
  • Technologie-Stack pro Subdomain unabhängig
  • CDN- und Geo-Optimierung einfacher

Nachteile:

  • SEO-Signale verteilen sich – jede Subdomain braucht eigenes Ranking
  • Extra Domain- und SSL-Verwaltung
  • Höhere Implementierungs- und Wartungskosten

Option 3: URL-Parameter (nicht empfohlen)

Vorteile:

  • Am einfachsten umzusetzen

Nachteile:

  • Schlechteste SEO-Wirkung – Parameter werden oft ignoriert
  • Schlechte UX, URLs wenig sprechend
  • CDN-Caching schwierig
  • Sprachversionen in der Suche schwer unterscheidbar

Fazit:

Für die meisten Projekte: Unterverzeichnis-Strategie. Bestes Gleichgewicht aus SEO, Aufwand und Wartung.

3. hreflang im Detail

3.1 Aufgabe der hreflang-Tags

hreflang teilt Suchmaschinen mit:

  1. welche Sprachversionen es gibt
  2. die vollständige URL jeder Version
  3. Sprach- und Regionscode pro Version

3.2 hreflang im Next.js App Router

Methode 1: Metadata API (empfohlen)

Next.js 13+ App Router mit schlanker Metadata API:

// app/[lang]/about/page.tsx
import { Metadata } from 'next'

type Props = {
  params: { lang: string }
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { lang } = params

  // Unterstützte Sprachen
  const languages = ['en', 'zh', 'ja', 'de']

  // Alternate-Links für alle Sprachen
  const alternates = {
    canonical: `https://example.com/${lang}/about`,
    languages: languages.reduce((acc, locale) => {
      acc[locale] = `https://example.com/${locale}/about`
      return acc
    }, {} as Record<string, string>)
  }

  return {
    title: 'About Us',
    alternates,
    // x-default für nicht gematchte Sprachen
    other: {
      'x-default': 'https://example.com/en/about'
    }
  }
}

export default function AboutPage({ params }: Props) {
  return <div>About page in {params.lang}</div>
}

Methode 2: Eigene Head-Komponente

Für mehr Kontrolle:

// components/I18nHead.tsx
import Head from 'next/head'

interface I18nHeadProps {
  currentLang: string
  pathname: string
  languages?: string[]
}

export default function I18nHead({
  currentLang,
  pathname,
  languages = ['en', 'zh', 'ja', 'de']
}: I18nHeadProps) {
  const baseUrl = 'https://example.com'

  return (
    <Head>
      {/* Canonical der aktuellen Seite */}
      <link rel="canonical" href={`${baseUrl}/${currentLang}${pathname}`} />

      {/* hreflang für alle Sprachversionen */}
      {languages.map(lang => (
        <link
          key={lang}
          rel="alternate"
          hrefLang={lang}
          href={`${baseUrl}/${lang}${pathname}`}
        />
      ))}

      {/* x-default auf Standardsprache */}
      <link
        rel="alternate"
        hrefLang="x-default"
        href={`${baseUrl}/en${pathname}`}
      />
    </Head>
  )
}

Verwendung:

// app/[lang]/about/page.tsx
import I18nHead from '@/components/I18nHead'

export default function AboutPage({ params }: { params: { lang: string } }) {
  return (
    <>
      <I18nHead
        currentLang={params.lang}
        pathname="/about"
      />
      <div>About page content</div>
    </>
  )
}

3.3 hreflang im Next.js Pages Router

Pages Router nutzt andere APIs:

// pages/about.tsx
import { GetStaticProps } from 'next'
import Head from 'next/head'
import { useRouter } from 'next/router'

export default function AboutPage() {
  const router = useRouter()
  const { locale, locales, asPath } = router
  const baseUrl = 'https://example.com'

  return (
    <>
      <Head>
        {/* Canonical der aktuellen Seite */}
        <link rel="canonical" href={`${baseUrl}/${locale}${asPath}`} />

        {/* hreflang für alle Sprachversionen */}
        {locales?.map(loc => (
          <link
            key={loc}
            rel="alternate"
            hrefLang={loc}
            href={`${baseUrl}/${loc}${asPath}`}
          />
        ))}

        {/* x-default Standardsprache */}
        <link
          rel="alternate"
          hrefLang="x-default"
          href={`${baseUrl}/en${asPath}`}
        />
      </Head>

      <div>About page content</div>
    </>
  )
}

export const getStaticProps: GetStaticProps = async ({ locale }) => {
  return {
    props: {
      messages: (await import(`../locales/${locale}.json`)).default
    }
  }
}

3.4 Regionale Codes (language-REGION)

Für länderspezifische Inhalte das Format language-REGION:

// Englisch für verschiedene Regionen
const hreflangConfig = {
  'en-US': 'https://example.com/en-us/about', // US-Englisch
  'en-GB': 'https://example.com/en-gb/about', // UK-Englisch
  'en-AU': 'https://example.com/en-au/about', // Australien
  'zh-CN': 'https://example.com/zh-cn/about', // Festlandchina vereinfachtes Chinesisch
  'zh-TW': 'https://example.com/zh-tw/about', // Taiwan traditionelles Chinesisch
  'zh-HK': 'https://example.com/zh-hk/about', // Hongkong traditionelles Chinesisch
}

Regionales Routing in Next.js:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'en-GB', 'en-AU', 'zh-CN', 'zh-TW', 'zh-HK'],
    defaultLocale: 'en-US',
  }
}

3.5 Typische Konfigurationsfehler und Fixes

Fehler 1: Fehlende Selbstreferenz

<!-- ❌ Fehler: aktuelle Seite referenziert sich nicht selbst -->
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />

<!-- ✅ Korrekt: Selbstreferenz erforderlich -->
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />

Warum Selbstreferenz?
Google verlangt symmetrische hreflang – jede Sprachversion muss alle anderen inklusive sich selbst referenzieren.

Fehler 2: Fehlendes x-default

<!-- ✅ Empfohlen: x-default als Fallback -->
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />

x-default liefert die Standardversion für nicht unterstützte Sprachen, z. B.:

  • Nutzer mit arabischer Browser-Sprache, Website ohne Arabisch
  • Suchmaschine liefert die unter x-default angegebene Seite

Fehler 3: hreflang und canonical im Konflikt

<!-- ❌ Fehler: canonical zeigt auf andere Sprachversion -->
<link rel="canonical" href="https://example.com/en/about" />
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />

<!-- ✅ Korrekt: canonical zeigt auf die aktuelle Sprachversion -->
<link rel="canonical" href="https://example.com/zh/about" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />

Grundregel: canonical muss die URL der aktuellen Seite sein – nie eine andere Sprachversion.

4. Mehrsprachige Sitemap

Sitemaps helfen Suchmaschinen, Seiten zu finden und zu indexieren. Bei mehrsprachigen Sites ist die richtige Sitemap-Konfiguration entscheidend.

4.1 Warum eine mehrsprachige Sitemap?

Drei Vorteile:

  1. Schnellere Indexierung – alle Sprachversionen aktiv melden, statt auf den Crawler zu warten
  2. Vollständigkeit – keine Sprachversion übersehen, besonders bei tiefen Linkstrukturen
  3. hreflang in der Sitemap – Sprachbeziehungen zusätzlich verstärken

4.2 Sitemap-Strategie

Je nach Site-Größe:

Option 1: Eine Sitemap (kleine Sites)

Alle Sprach-URLs in einer sitemap.xml – geeignet bis ca. 5.000 Seiten:

<?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">
  <!-- Englische Version -->
  <url>
    <loc>https://example.com/en/about</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about"/>
    <xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/about"/>
    <xhtml:link rel="alternate" hreflang="ja" href="https://example.com/ja/about"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/about"/>
  </url>
  <!-- Chinesische Version -->
  <url>
    <loc>https://example.com/zh/about</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about"/>
    <xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/about"/>
    <xhtml:link rel="alternate" hreflang="ja" href="https://example.com/ja/about"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/about"/>
  </url>
</urlset>

Option 2: Sitemap pro Sprache (große Sites)

Pro Sprache eine Sitemap, gebündelt per Sitemap-Index – ab ca. 5.000 Seiten oder vielen Sprachen:

<!-- sitemap-index.xml -->
<?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>2025-01-01</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-zh.xml</loc>
    <lastmod>2025-01-01</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-ja.xml</loc>
    <lastmod>2025-01-01</lastmod>
  </sitemap>
</sitemapindex>

4.3 Sitemap im Next.js App Router

Next.js 13+ mit eingebauter Sitemap-Generierung:

// app/sitemap.ts
import { MetadataRoute } from 'next'

const languages = ['en', 'zh', 'ja', 'de']
const routes = ['', '/about', '/blog', '/contact']

export default function sitemap(): MetadataRoute.Sitemap {
  const baseUrl = 'https://example.com'
  const sitemap: MetadataRoute.Sitemap = []

  routes.forEach(route => {
    languages.forEach(lang => {
      const url = `${baseUrl}/${lang}${route}`

      sitemap.push({
        url,
        lastModified: new Date(),
        changeFrequency: 'weekly',
        priority: route === '' ? 1 : 0.8,
        alternates: {
          languages: languages.reduce((acc, l) => {
            acc[l] = `${baseUrl}/${l}${route}`
            return acc
          }, {} as Record<string, string>)
        }
      })
    })
  })

  return sitemap
}

4.4 Sitemap für dynamische Inhalte

Bei Blog-Artikeln oder Produkten aus DB/CMS:

// app/sitemap.ts
import { MetadataRoute } from 'next'

const languages = ['en', 'zh', 'ja']
const baseUrl = 'https://example.com'

async function getArticles() {
  return [
    { slug: 'getting-started', lastModified: '2025-01-01' },
    { slug: 'advanced-guide', lastModified: '2025-01-15' },
  ]
}

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const sitemap: MetadataRoute.Sitemap = []

  const staticPages = ['', '/about', '/contact']
  staticPages.forEach(page => {
    languages.forEach(lang => {
      sitemap.push({
        url: `${baseUrl}/${lang}${page}`,
        lastModified: new Date(),
        changeFrequency: 'monthly',
        priority: page === '' ? 1 : 0.8,
        alternates: {
          languages: languages.reduce((acc, l) => {
            acc[l] = `${baseUrl}/${l}${page}`
            return acc
          }, {} as Record<string, string>)
        }
      })
    })
  })

  const articles = await getArticles()
  articles.forEach(article => {
    languages.forEach(lang => {
      sitemap.push({
        url: `${baseUrl}/${lang}/blog/${article.slug}`,
        lastModified: new Date(article.lastModified),
        changeFrequency: 'weekly',
        priority: 0.6,
        alternates: {
          languages: languages.reduce((acc, l) => {
            acc[l] = `${baseUrl}/${l}/blog/${article.slug}`
            return acc
          }, {} as Record<string, string>)
        }
      })
    })
  })

  return sitemap
}

4.5 Sitemap im Pages Router

Manuell über API-Route:

// pages/api/sitemap.xml.ts
import { NextApiRequest, NextApiResponse } from 'next'

const baseUrl = 'https://example.com'
const languages = ['en', 'zh', 'ja']

function generateSiteMap(pages: string[]) {
  return `<?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">
${pages.map(page => {
  return languages.map(lang => {
    const url = `${baseUrl}/${lang}${page}`
    const alternates = languages.map(l =>
      `    <xhtml:link rel="alternate" hreflang="${l}" href="${baseUrl}/${l}${page}"/>`
    ).join('\n')

    return `  <url>
    <loc>${url}</loc>
    <lastmod>${new Date().toISOString()}</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
${alternates}
    <xhtml:link rel="alternate" hreflang="x-default" href="${baseUrl}/en${page}"/>
  </url>`
  }).join('\n')
}).join('\n')}
</urlset>`
}

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const pages = ['', '/about', '/blog', '/contact']
  const sitemap = generateSiteMap(pages)

  res.setHeader('Content-Type', 'text/xml')
  res.write(sitemap)
  res.end()
}

4.6 Sitemap bei Suchmaschinen einreichen

Nach der Generierung aktiv einreichen:

Methode 1: In robots.txt deklarieren

Einfachste Variante – Crawler lesen automatisch:

# public/robots.txt
User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml

Methode 2: Google Search Console

Manuelles Einreichen beschleunigt die Indexierung:

  1. Google Search Console öffnen
  2. Property auswählen
  3. Menü „Sitemaps“
  4. URL der sitemap.xml eingeben
  5. „Senden“ klicken

Methode 3: Bing Webmaster Tools

Bing hat relevanten Marktanteil:

  1. Bing Webmaster Tools öffnen
  2. Website hinzufügen
  3. Sitemap im Bereich „Sitemaps“ einreichen

4.7 Sitemap validieren

Validierungstools:

  1. XML Sitemap Validator: https://www.xml-sitemaps.com/validate-xml-sitemap.html
  2. Google Search Console: Indexstatus und Fehler nach Einreichung
  3. Online-XML-Validator: Standardkonformität prüfen

5. Best Practices und Hinweise

5.1 Qualität der Übersetzungen

Google erkennt minderwertige Übersetzungen – das beeinflusst das Ranking.

Vermeiden:

  • ❌ Automatische Übersetzung (Google Translate) direkt veröffentlichen
  • ❌ Nur Navigation und Titel übersetzen, Body in einer Sprache lassen
  • ❌ Starke inhaltliche Unterschiede zwischen Sprachversionen

Empfohlen:

  • ✅ Professionelle Übersetzer oder Muttersprachler
  • ✅ Lokalisierung statt reiner Übersetzung (Kultur, Ausdruck)
  • ✅ Einheitliche Struktur und Qualität aller Sprachversionen

5.2 SEO-Risiko automatischer Client-Übersetzung

Clientseitige Auto-Übersetzung wirkt für SEO nicht – Crawler sehen den Originalinhalt:

// ❌ Nicht empfohlen: Client-Auto-Übersetzung (nicht indexierbar)
import GoogleTranslate from 'google-translate-api'

export default function Page() {
  const [content, setContent] = useState('')

  useEffect(() => {
    GoogleTranslate(originalText, { to: 'zh' })
      .then(res => setContent(res.text))
  }, [])

  return <div>{content}</div>
}
// ✅ Empfohlen: SSR mit echten Übersetzungen
export default function Page({ params }: { params: { lang: string } }) {
  const content = await getTranslatedContent(params.lang)

  return <div>{content}</div>
}

5.3 Performance

Mehrsprachige Sites erreichen Nutzer weltweit – Performance zählt.

1. CDN für mehrere Regionen

// next.config.js
module.exports = {
  images: {
    domains: ['cdn.example.com'],
  },
  compress: true,
}

2. Sprachpakete lazy laden

Nicht alle Übersetzungsdateien auf einmal laden:

const messages = await import(`@/locales/${lang}.json`)

3. Cache-Strategie

// app/[lang]/layout.tsx
export const revalidate = 3600 // stündliche Revalidierung

5.4 Monitoring und Wartung

Mehrsprach-SEO ist kein Einmal-Task.

1. hreflang-Fehler prüfen

Google Search Console, Bericht „Internationale Ausrichtung“:

  • hreflang-Fehler und Warnungen
  • Indexstatus pro Sprache
  • Suchperformance und Klickrate pro Sprache

2. Empfohlene Tools

3. Monitoring-Skript

Automatisierte hreflang-Prüfung:

// scripts/check-hreflang.ts
import { JSDOM } from 'jsdom'

async function checkHreflang(url: string) {
  const response = await fetch(url)
  const html = await response.text()
  const dom = new JSDOM(html)
  const document = dom.window.document

  const hreflangLinks = document.querySelectorAll('link[rel="alternate"][hreflang]')

  console.log(`Found ${hreflangLinks.length} hreflang links on ${url}`)

  hreflangLinks.forEach(link => {
    const hreflang = link.getAttribute('hreflang')
    const href = link.getAttribute('href')
    console.log(`  ${hreflang}: ${href}`)
  })

  const currentUrl = new URL(url).href
  const hasSelfReference = Array.from(hreflangLinks).some(
    link => link.getAttribute('href') === currentUrl
  )

  if (!hasSelfReference) {
    console.warn('⚠️ Warning: Missing self-reference hreflang tag')
  }

  const hasXDefault = Array.from(hreflangLinks).some(
    link => link.getAttribute('hreflang') === 'x-default'
  )

  if (!hasXDefault) {
    console.warn('⚠️ Warning: Missing x-default hreflang tag')
  }
}

checkHreflang('https://example.com/en/about')
checkHreflang('https://example.com/zh/about')

6. Praxisbeispiel: vollständiges Projekt

So setzen Sie Mehrsprach-SEO in einem Next.js App Router-Projekt um.

6.1 Projektstruktur

my-i18n-site/
├── app/
│   ├── [lang]/
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   ├── about/
│   │   │   └── page.tsx
│   │   └── blog/
│   │       ├── page.tsx
│   │       └── [slug]/
│   │           └── page.tsx
│   ├── sitemap.ts
│   └── robots.ts
├── components/
│   └── I18nMetadata.tsx
├── lib/
│   ├── i18n.ts
│   └── articles.ts
├── locales/
│   ├── en.json
│   ├── zh.json
│   └── ja.json
└── next.config.js

6.2 Konfiguration

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  // App Router nutzt i18n in next.config nicht – Routing manuell
}

module.exports = nextConfig
// lib/i18n.ts
export const languages = ['en', 'zh', 'ja'] as const
export type Language = (typeof languages)[number]

export const defaultLanguage: Language = 'en'

export const languageNames: Record<Language, string> = {
  en: 'English',
  zh: '中文',
  ja: '日本語',
}

export function isValidLanguage(lang: string): lang is Language {
  return languages.includes(lang as Language)
}

6.3 Layout-Komponente

// app/[lang]/layout.tsx
import { languages, isValidLanguage } from '@/lib/i18n'
import { notFound } from 'next/navigation'

export async function generateStaticParams() {
  return languages.map(lang => ({ lang }))
}

export default function LangLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: { lang: string }
}) {
  if (!isValidLanguage(params.lang)) {
    notFound()
  }

  return (
    <html lang={params.lang}>
      <body>{children}</body>
    </html>
  )
}

6.4 Seite mit Metadata

// app/[lang]/about/page.tsx
import { Metadata } from 'next'
import { languages, Language } from '@/lib/i18n'

type Props = {
  params: { lang: Language }
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { lang } = params
  const baseUrl = 'https://example.com'
  const pathname = '/about'

  const alternates = {
    canonical: `${baseUrl}/${lang}${pathname}`,
    languages: languages.reduce((acc, locale) => {
      acc[locale] = `${baseUrl}/${locale}${pathname}`
      return acc
    }, {} as Record<string, string>)
  }

  const titles: Record<Language, string> = {
    en: 'About Us - Learn More About Our Company',
    zh: '关于我们 - 了解更多关于我们公司的信息',
    ja: '私たちについて - 当社についてもっと知る',
  }

  const descriptions: Record<Language, string> = {
    en: 'Learn about our mission, values, and the team behind our success.',
    zh: '了解我们的使命、价值观以及我们成功背后的团队。',
    ja: '私たちの使命、価値観、そして成功を支えるチームについて学びます。',
  }

  return {
    title: titles[lang],
    description: descriptions[lang],
    alternates,
    openGraph: {
      title: titles[lang],
      description: descriptions[lang],
      url: `${baseUrl}/${lang}${pathname}`,
      siteName: 'Example Site',
      locale: lang,
      type: 'website',
    },
  }
}

export default function AboutPage({ params }: Props) {
  const content = {
    en: 'About us content in English...',
    zh: '关于我们的中文内容...',
    ja: '私たちについての日本語コンテンツ...',
  }

  return (
    <div>
      <h1>About Us</h1>
      <p>{content[params.lang]}</p>
    </div>
  )
}

6.5 Dynamische Route mit hreflang

// app/[lang]/blog/[slug]/page.tsx
import { Metadata } from 'next'
import { languages, Language } from '@/lib/i18n'
import { getArticle, getAllArticles } from '@/lib/articles'
import { notFound } from 'next/navigation'

type Props = {
  params: { lang: Language; slug: string }
}

export async function generateStaticParams() {
  const articles = await getAllArticles()

  return languages.flatMap(lang =>
    articles.map(article => ({
      lang,
      slug: article.slug,
    }))
  )
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { lang, slug } = params
  const article = await getArticle(slug, lang)

  if (!article) {
    return {}
  }

  const baseUrl = 'https://example.com'
  const pathname = `/blog/${slug}`

  const alternates = {
    canonical: `${baseUrl}/${lang}${pathname}`,
    languages: languages.reduce((acc, locale) => {
      acc[locale] = `${baseUrl}/${locale}${pathname}`
      return acc
    }, {} as Record<string, string>)
  }

  return {
    title: article.title,
    description: article.excerpt,
    alternates,
    openGraph: {
      title: article.title,
      description: article.excerpt,
      url: `${baseUrl}/${lang}${pathname}`,
      type: 'article',
      publishedTime: article.publishedAt,
      authors: [article.author],
    },
  }
}

export default async function BlogArticle({ params }: Props) {
  const { lang, slug } = params
  const article = await getArticle(slug, lang)

  if (!article) {
    notFound()
  }

  return (
    <article>
      <h1>{article.title}</h1>
      <p>{article.content}</p>
    </article>
  )
}

6.6 Sitemap-Generierung

// app/sitemap.ts
import { MetadataRoute } from 'next'
import { languages } from '@/lib/i18n'
import { getAllArticles } from '@/lib/articles'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://example.com'
  const sitemap: MetadataRoute.Sitemap = []

  const staticPages = ['', '/about', '/contact']
  staticPages.forEach(page => {
    languages.forEach(lang => {
      sitemap.push({
        url: `${baseUrl}/${lang}${page}`,
        lastModified: new Date(),
        changeFrequency: 'monthly',
        priority: page === '' ? 1 : 0.8,
        alternates: {
          languages: languages.reduce((acc, l) => {
            acc[l] = `${baseUrl}/${l}${page}`
            return acc
          }, {} as Record<string, string>)
        }
      })
    })
  })

  const articles = await getAllArticles()
  articles.forEach(article => {
    languages.forEach(lang => {
      sitemap.push({
        url: `${baseUrl}/${lang}/blog/${article.slug}`,
        lastModified: new Date(article.updatedAt),
        changeFrequency: 'weekly',
        priority: 0.6,
        alternates: {
          languages: languages.reduce((acc, l) => {
            acc[l] = `${baseUrl}/${l}/blog/${article.slug}`
            return acc
          }, {} as Record<string, string>)
        }
      })
    })
  })

  return sitemap
}

6.7 Robots.txt

// app/robots.ts
import { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
    },
    sitemap: 'https://example.com/sitemap.xml',
  }
}

7. Validierung und Tests

7.1 Lokale Checkliste

Vor dem Produktions-Deploy:

  • <html lang="..."> auf allen Seiten korrekt
  • Vollständige hreflang-Tags auf jeder Seite (alle Sprachversionen)
  • Selbstreferenz in hreflang vorhanden
  • x-default zeigt auf Standardsprache
  • canonical zeigt auf die aktuelle Sprachversion
  • Sitemap enthält alle Sprach-URLs
  • robots.txt verweist auf sitemap.xml
  • Einheitliche Übersetzungsqualität aller Sprachversionen

7.2 Google Rich Results Test

Google Rich Results Test:

  1. Seiten-URL eingeben
  2. Auf Crawl und Analyse warten
  3. Fehler und Warnungen prüfen
  4. hreflang-Erkennung verifizieren

7.3 hreflang-Prüftools

Funktionen:

  • Bulk-Prüfung mehrerer Seiten
  • Symmetrie-Probleme erkennen
  • Sprachcode-Fehler finden

7.4 Google Search Console

Nach dem Deploy:

  1. Sitemap in Search Console einreichen
  2. 1–2 Wochen auf erste Indexierung warten
  3. „Internationale Ausrichtung“ > „Sprache“ prüfen
  4. hreflang-Fehler und Warnungen beheben
  5. Suchperformance pro Sprache überwachen

8. Häufige Fragen

F1: Unterschied zwischen hreflang und canonical?

  • canonical – kanonische URL der Seite, gegen Duplicate Content
  • hreflang – verfügbare Sprachversionen, für Sprach-Targeting

Beides kombinierbar. canonical pro Sprachversion auf sich selbst, hreflang auf alle Versionen.

F2: hreflang auf jeder Seite Pflicht?

Ja. Auf jeder Sprachversion, symmetrisch (gegenseitige Referenz). Nur auf der englischen Seite reicht nicht – Google ignoriert das.

F3: Wohin zeigt x-default?

Meist Standardsprache oder allgemeinste Version:

  • Hauptpublikum Englisch → englische Version
  • Globale Site → internationales Englisch (en-US)
  • Regionale Site → Hauptsprache der Region

F4: Unterverzeichnis vs. Subdomain?

Unterverzeichnis (empfohlen):

  • SEO auf Hauptdomain
  • Einfache Implementierung
  • Für die meisten Projekte

Subdomain:

  • Eigener Server pro Markt
  • Große internationale Sites mit eigenem Markt-Betrieb
  • Extra Domain-Kosten

Fazit: Ohne Spezialbedarf → Unterverzeichnis.

F5: Maschinell übersetzte Inhalte?

Nicht empfohlen für SEO:

  • Google erkennt schwache Übersetzungen
  • Geringerer Content-Wert
  • Schlechte UX, hohe Absprungrate

Bei knappem Budget:

  1. Kernseiten zuerst (Startseite, Hauptprodukte, Top-Traffic)
  2. MT + menschliches Lektorat
  3. Qualität schrittweise verbessern

F6: Wie lange bis zur Indexierung?

Typischer Zeitplan:

  • Nach Sitemap-Einreichung 1–2 Wochen erste Indexierung
  • Vollständige Indexierung 1–2 Monate
  • SEO-Signale 3–6 Monate

Beschleunigen:

  • Korrekte Sitemap + Einreichung
  • Hohe Content-Qualität und Updates
  • Qualitative Backlinks
  • Indexierung wichtiger Seiten in Search Console anfordern

9. Zusammenfassung

Mehrsprach-SEO ist Schlüssel zum internationalen Erfolg. Kernpunkte:

9.1 Kernpunkte

  1. URL-Strategie

    • Unterverzeichnis empfohlen (example.com/en/, example.com/zh/)
    • Klare, konsistente URL-Struktur
  2. hreflang

    • Vollständige Tags auf jeder Seite
    • Selbstreferenz Pflicht
    • x-default auf Standardsprache
    • ISO-639-1-Sprachcodes
  3. Sitemap

    • Alle Sprachversionen enthalten
    • Optional hreflang in der Sitemap
    • Regelmäßig aktualisieren und einreichen
  4. Content-Qualität

    • Keine rohe MT-Veröffentlichung
    • Konsistenz und Professionalität
    • Lokalisierung, nicht nur Übersetzung
  5. Monitoring

    • Google Search Console
    • Regelmäßige hreflang-Prüfung
    • Performance pro Sprache tracken

9.2 Aktionsliste

  • URL-Strategie wählen und umsetzen (Unterverzeichnis empfohlen)
  • Vollständige hreflang-Tags auf allen Seiten
  • Korrekte canonical-Tags
  • Mehrsprachige Sitemap generieren
  • robots.txt mit Sitemap-Verweis
  • Sitemap an Google Search Console und Bing einreichen
  • hreflang mit Validierungstools prüfen
  • Übersetzungsqualität sichern
  • Monitoring und regelmäßige Reviews einrichten

9.3 Weiterführend

Korrekte Mehrsprach-SEO braucht Zeit – lohnt sich: besseres Ranking, präzisere Nutzer, höhere Conversion. Mit diesen Best Practices performt Ihre Site in der Suche deutlich besser.

Bei Fragen zur Umsetzung – gerne in den Kommentaren diskutieren!

Next.js Mehrsprach-SEO: Vollständiger Konfigurationsablauf

Von hreflang-Tags über mehrsprachige Sitemaps bis zur URL-Strategie – Schritt für Schritt

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: hreflang-Tags konfigurieren

    In Metadata konfigurieren:
    ```tsx
    // app/[locale]/about/page.tsx
    export async function generateMetadata({ params }): Promise<Metadata> {
    const { locale } = params

    return {
    title: 'About Us',
    alternates: {
    languages: {
    'zh': '/zh/about',
    'en': '/en/about',
    'x-default': '/en/about', // Standardsprache
    },
    },
    }
    }
    ```

    Wichtig:
    • Alle Sprachversionen enthalten
    • x-default auf Standardsprache
    • Auf jeder Seite konfigurieren

    HTML-Ausgabe:
    ```html
    <link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
    <link rel="alternate" hreflang="en" href="https://example.com/en/about" />
    <link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />
    ```

    Wirkung:
    • Zielsprache für Suchmaschinen
    • Duplicate-Content-Strafe vermeiden
    • Nutzer präzise matchen
  2. 2

    Step 2: Mehrsprachige Sitemap generieren

    Methode 1: Pro Sprache eigene Sitemap
    ```tsx
    // app/[locale]/sitemap.ts
    export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
    const baseUrl = 'https://example.com'
    const locale = params.locale

    return [
    {
    url: `${baseUrl}/${locale}`,
    lastModified: new Date(),
    changeFrequency: 'daily',
    priority: 1,
    },
    ]
    }
    ```

    Methode 2: Sitemap-Index
    ```tsx
    // app/sitemap.ts
    export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
    const locales = ['zh', 'en']
    const baseUrl = 'https://example.com'

    return locales.flatMap(locale => [
    {
    url: `${baseUrl}/${locale}`,
    lastModified: new Date(),
    changeFrequency: 'daily',
    priority: 1,
    },
    ])
    }
    ```

    Wichtig:
    • Alle Sprachversionen enthalten
    • Korrektes URL-Format
    • In Google Search Console einreichen
  3. 3

    Step 3: URL-Strategie wählen

    Option 1: Unterverzeichnis (empfohlen)
    • URL: /zh/about, /en/about
    • Einfache Konfiguration
    • SEO-freundlich
    • Für die meisten Projekte

    Option 2: Subdomain
    • URL: zh.example.com, en.example.com
    • Mehrere Domains nötig
    • Professioneller Eindruck
    • Große Projekte

    Option 3: Cookie
    • Sprache per Cookie
    • Kein Sprachpräfix in der URL
    • SEO-unfreundlich
    • Nicht empfohlen

    Empfehlung:
    • Meiste Projekte → Unterverzeichnis
    • Große Projekte → Subdomain
    • Vermeiden → Cookie

    Unterverzeichnis ist SEO-technisch am besten.
  4. 4

    Step 4: Validieren und testen

    Validierungstools:

    1. Google Search Console:
    • Mehrsprachige Sitemap einreichen
    • hreflang-Tags prüfen
    • Indexstatus ansehen

    2. hreflang-Testtools:
    • https://www.aleydasolis.com/en/english-tools/international-seo-tools/hreflang-tags-validator/
    • hreflang-Konfiguration verifizieren

    3. Mehrsprach-Sitemap:
    • Sitemap-Format prüfen
    • Alle Sprachversionen enthalten
    • URLs korrekt

    Typische Fehler:
    • Fehlende hreflang-Tags
    • Falsches x-default
    • Sitemap ohne alle Sprachen
    • Inkonsistente URL-Formate

    Tipp: Direkt nach der Konfiguration validieren.

FAQ

Was sind hreflang-Tags? Warum braucht man sie?
hreflang ist ein HTML-Attribut für Zielsprache und -region.

Hauptfunktionen:
1. Duplicate-Content-Strafe vermeiden – verschiedene Sprachen sind Übersetzungen, kein Duplicate
2. Nutzer präzise matchen – passende Sprachversion in der Suche
3. UX verbessern – keine falsche Sprache

Konfiguration:
```tsx
export async function generateMetadata({ params }): Promise<Metadata> {
return {
alternates: {
languages: {
'zh': '/zh/about',
'en': '/en/about',
'x-default': '/en/about',
},
},
}
}
```

Wichtig:
• Alle Sprachversionen
• x-default auf Standardsprache
• Auf jeder Seite

Laut Google haben über 60 % der mehrsprachigen Websites hreflang-Fehler.
Wie konfiguriere ich hreflang-Tags?
In Metadata:
```tsx
// app/[locale]/about/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
const { locale } = params

return {
title: 'About Us',
alternates: {
languages: {
'zh': '/zh/about',
'en': '/en/about',
'x-default': '/en/about',
},
},
}
}
```

HTML-Ausgabe:
```html
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />
```

Wichtig:
• Alle Sprachversionen inkl. aktueller Seite
• x-default auf Standardsprache
• Absolute URLs verwenden
Wie generiere ich eine mehrsprachige Sitemap?
Methode 1: Pro Sprache eigene Sitemap
```tsx
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://example.com'
const locale = params.locale

return [
{
url: `${baseUrl}/${locale}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
]
}
```

Methode 2: Sitemap-Index
```tsx
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const locales = ['zh', 'en']
const baseUrl = 'https://example.com'

return locales.flatMap(locale => [
{
url: `${baseUrl}/${locale}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
])
}
```

Wichtig:
• Alle Sprachversionen
• Korrektes URL-Format
• In Google Search Console einreichen

Empfehlung: Sitemap-Index für Flexibilität.
Welche URL-Strategie für mehrsprachige Websites?
Drei Strategien:

Option 1: Unterverzeichnis (empfohlen)
• /zh/about, /en/about
• Einfach, SEO-freundlich
• Meiste Projekte

Option 2: Subdomain
• zh.example.com, en.example.com
• Mehr Domains, professioneller
• Große Projekte

Option 3: Cookie
• Sprache per Cookie, kein URL-Präfix
• SEO-unfreundlich – vermeiden

Empfehlung: Meiste Projekte → Unterverzeichnis.

Wichtig: hreflang-URLs müssen zur gewählten Strategie passen.
Wie validiere ich die Mehrsprach-SEO-Konfiguration?
Tools:

1. Google Search Console:
• Mehrsprachige Sitemap einreichen
• hreflang prüfen
• Indexstatus überwachen

2. hreflang-Validator:
• https://www.aleydasolis.com/en/english-tools/international-seo-tools/hreflang-tags-validator/

3. Sitemap-Validierung:
• Format, alle Sprachen, korrekte URLs

Typische Fehler:
• Fehlende hreflang
• Falsches x-default
• Unvollständige Sitemap
• Inkonsistente URLs

Empfehlung: Nach Konfiguration sofort prüfen, regelmäßig Indexstatus kontrollieren.
Welche typischen Mehrsprach-SEO-Fehler gibt es?
Häufige Fehler:

1. Fehlende hreflang-Tags
• Suchmaschine kennt Sprache nicht
• Falsche Sprache in Ergebnissen

2. Falsches x-default
• Nicht gesetzt oder falsche Sprache

3. Unvollständige Sitemap
• Nur Teil der Sprachversionen eingereicht

4. Inkonsistente URL-Formate
• hreflang-URLs passen nicht zur Strategie

5. Duplicate Content
• hreflang fehlt – Sprachversionen gelten als Duplikate

Lösung:
• hreflang auf allen Seiten
• Alle Sprachversionen referenzieren
• Korrekte URLs und vollständige Sitemap

Best Practices aus diesem Artikel befolgen.

16 Min. Lesezeit · Veröffentlicht am: 25. Dez. 2025 · Aktualisiert am: 14. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog