2023 habe ich eine mehrsprachige E-Commerce-Website für einen Client aus Berlin veröffentlicht. Zwölf Sprachen, App Router, alles sah ordentlich aus. Drei Monate später rankten ihre deutschen Produktseiten in den UK-SERPs und ihre englischen Seiten wurden Nutzern in München angezeigt. Die hreflang-Tags waren technisch vorhanden. Sie waren nur falsch auf eine Weise, die mich einen beschämenden Nachmittag gekostet hat zu entwirren. Diese Erfahrung hat mir mehr über Next.js i18n beigebracht als jede Dokumentationsseite je gekonnt hätte.
Das ist kein „hier ist die offizielle Dokumentation zusammengefasst"-Post. Es ist, was ich beim Bau mehrsprachiger Websites bei Seahawk Media gelernt habe, wo Menschen wiederholt Probleme bekommen, und die konkreten Entscheidungen, die Rankings 2026 wirklich beeinflussen.
---
Der App Router hat alles verändert (und nicht alles zum Besseren)
Der Pages Router hatte eingebauten i18n-Support. Du packtest einen Block in next.config.js und Next.js kümmerte sich um Locale-Erkennung, Sub-Path-Routing und Locale-Wechsel der Link-Komponente fast automatisch. Es war nicht perfekt, aber es war eigensinnig, was weniger Fallstricke bedeutete.next.config.js and Next.js handled locale detection, sub-path routing, and Link component locale switching almost automatically. It wasn't perfect, but it was opinionated, which meant fewer footguns.
Der App Router hat natives i18n-Routing vollständig entfernt. Kein i18n-Key in next.config.js. Keine automatische Locale-Erkennung. Du bist jetzt verantwortlich für die gesamte Routing-Logik, und genau diese Verantwortung ist es, wo die meisten Teams still und leise ein Durcheinander anrichten.i18n key in next.config.js. No automatic locale detection. You are now responsible for the full routing logic, and that responsibility is where most teams quietly make a mess.
Der bewährte Ansatz 2026 ist ein [locale] Dynamic Segment im Root deines app/-Verzeichnisses. Etwa so: app/[locale]/page.tsx. Kombiniert mit next-intl oder i18next via react-i18next, eine Middleware-Datei, die Accept-Language-Header liest und entsprechend umleitet, und du hast eine funktionierende Grundlage.[locale] dynamic segment at the root of your app/ directory. Something like app/[locale]/page.tsx . Pair that with next-intl or i18next via react-i18next, a middleware file that reads Accept-Language headers and redirects accordingly, and you have a working foundation.
Warum Middleware missverstanden wird
Die Middleware-Datei verrichtet zwei separate Aufgaben, die Leute vermischen. Erstens, Locale-Erkennung: den Accept-Language-Header lesen (oder ein Cookie, oder ein URL-Präfix) und entscheiden, welche Locale zu servieren ist. Zweitens, die URL umleiten oder neu schreiben, damit der Nutzer bei /de/produkte landet statt bei /produkte mit irgendeinem versteckten Locale-Zustand.Accept-Language header (or a cookie, or a URL prefix) and deciding which locale to serve. Second, redirecting or rewriting the URL so the user ends up at /de/produkte rather than /produkte with some hidden locale state.
Beides muss passieren, bevor die Seite rendert. Das klingt offensichtlich. Aber ich habe Teams gesehen, die Locale-Erkennung in einen React Context Provider laufen lassen, was bedeutet, dass die initiale HTML an Googlebot kein Locale-Signal hat. Der Bot sieht eine Seite, indexiert sie unter der Root-URL, und plötzlich hast du ein kanonisches Chaos.before the page renders. That sounds obvious. But I've seen teams run locale detection inside a React context provider, which means the initial HTML served to Googlebot has no locale signal at all. The bot sees a page, it indexes it under the root URL, and suddenly you have a canonical mess.
Halte Locale-Auflösung in Middleware. Immer.
---
Sub-Pfad vs Sub-Domain: Wähle eins und bleibe dabei
Die ewige Debatte. /en/about vs en.yoursite.com/about. Bei den meisten Projekten empfehle ich Sub-Pfad-Routing, und hier ist warum: Es konzentriert deine Domain Authority. Jeder Link, der auf deine .com zeigt, kommt allen Locales zugute. Mit Sub-Domains führst du effektiv separate Seiten aus Googles Sicht, was bedeutet, dass du Authority für jede unabhängig aufbauen musst./en/about vs en.yoursite.com/about . For most projects I recommend sub-path routing, and here's why: it concentrates your domain authority. Every link pointing at your .com benefits all locales. With sub-domains you're effectively running separate sites in Google's eyes, which means you need to build authority for each one independently.
Es gibt eine Ausnahme. Wenn du in einem Markt bist, wo die lokale Top-Level-Domain enorm für Vertrauen zählt (.de in Deutschland, .fr in Frankreich), schlägt eine Country-Code-TLD sowohl Sub-Pfade als auch Sub-Domains bei wahrgenommener lokaler Glaubwürdigkeit. Das ist keine SEO-Aussage, das ist eine Conversions-Aussage. Ich habe A/B-Tests gesehen, wo .de vs yourbrand.com/de Trust-Metriken sichtbar verschoben haben, selbst wenn Rankings identisch waren..de in Germany, .fr in France), a country-code TLD beats both sub-paths and sub-domains for perceived local credibility. That's not an SEO claim, it's a conversion claim. I've seen A/B tests where .de vs yourbrand.com/de moved trust metrics meaningfully even when rankings were identical.
Aber du kannst ein ccTLD-Setup nicht einfach in einer einzigen Next.js-App betreiben. Also für 99% der Projekte: Sub-Pfade, eine App, eine Domain.
---
hreflang: Das Tag, das dich anlügt
Hier sehe ich die meisten Schäden. hreflang ist konzeptionell nicht schwer. Es teilt Suchmaschinen mit: „Diese Seite hat Äquivalente in anderen Sprachen, hier sind sie." In der Praxis gibt es drei spezifische Fehlermodi, auf die ich immer wieder stoße.
Fehlermodus 1: Die Tags sind nicht reziprok
Jede Seite in deinem hreflang-Set muss auf jede andere Seite verweisen, auch auf sich selbst. Wenn /en/about ein hreflang hat, das auf /de/about verweist, aber /de/about kein hreflang hat, das zurück auf /en/about verweist, ignoriert Google das gesamte Set. Nicht bestrafen. Ignorieren. Als würden die Tags nicht existieren./en/about has an hreflang pointing to /de/about but /de/about does not have an hreflang pointing back to /en/about, Google ignores the whole set. Not penalises. Ignores. As if the tags don't exist.
Seahawk hatte letztes Jahr ein Fintech-Projekt, bei dem 40% der hreflang-Tags nicht reziprok waren, weil zwei Entwickler sie unabhängig voneinander in verschiedenen Komponenten implementiert hatten, ohne sich abzustimmen. Die Behebung dauerte eine Stunde. Die Ranking-Wiederherstellung dauerte zwei Monate.
Fehlermodus 2: Das x-default-Tag fehlt oder ist falsch platziert
x-default ist für Seiten, die keine spezifische Sprache oder Region anvisieren. Typischerweise deine Startseite / oder eine Sprachauswahl-Seite. Einige Teams setzen x-default auf ihre englische Seite, was in Ordnung ist, wenn Englisch wirklich dein Fallback für alle nicht zugeordneten Locales ist. Aber wenn du eine andere Seite für Nutzer bereitstellst, deren Sprache du nicht unterstützt, sollte x-default dorthin verweisen. is for pages that don't target a specific language or region. Typically your root / or a language-selector page. Some teams put x-default on their English page, which is fine if English is genuinely your fallback for all unmatched locales. But if you serve a different page to users whose language you don't support, x-default should point there.
Ich habe gesehen, dass x-default komplett weggelassen wurde. Google wirft keinen Fehler. Es trifft einfach selbst eine Entscheidung darüber, welche Version für nicht zugeordnete Anfragen angezeigt werden soll, und diese Entscheidung ist oft falsch.x-default omitted entirely. Google doesn't throw an error. It just makes its own decision about which version to show for unmatched queries, and that decision is often wrong.
Fehlermodus 3: Trailing Slashes und kanonische Konflikte
Dein hreflang-Tag sagt https://yoursite.com/de/ueber-uns . Dein kanonischer Tag auf der gleichen Seite sagt https://yoursite.com/de/ueber-uns/ (beachte den Trailing Slash). Google behandelt diese als unterschiedliche URLs. Dein kanonischer Tag und hreflang zeigen nun auf verschiedene Dinge, und du hast einen Signalkonflikt erzeugt, der Rankings unauffällig unterdrückt, ohne einen sichtbaren Fehler in der Search Console zu hinterlassen.https://yoursite.com/de/ueber-uns . Your canonical tag on the same page says https://yoursite.com/de/ueber-uns/ (note the trailing slash). Google treats those as different URLs. Your canonical and hreflang are now pointing at different things, and you've created a signal conflict that quietly suppresses rankings without any visible error in Search Console.
Setze trailingSlash in next.config.js und sei konsistent überall. Überprüfe es. Überprüfe es wirklich, indem du deine eigenen URLs curlist und die Ausgabe liest.trailingSlash in next.config.js and be consistent everywhere. Check it. Actually check it by curling your own URLs and reading the output.
Die Google Search Central-Dokumentation zu hreflang ist wirklich wert, vollständig gelesen zu werden. Das ist eine dieser seltenen offiziellen Dokumentationen, die spezifisch genug sind, um nützlich zu sein.Google Search Central documentation on hreflang is genuinely worth reading in full. It's one of those rare official docs that's specific enough to be useful.
---
hreflang im großen Maßstab in Next.js generieren
Für eine 5-Sprachen-Website könntest du hreflang-Tags von Hand schreiben. Für 12 Locales über 3.000 Produktseiten brauchst du einen systematischen Ansatz.
Hier ist, was ich verwende:
- Definiere deine Locale-Liste einmal, in einer einzelnen Config-Datei. So etwas wie locales.config.ts, das ein Array exportiert: ['en', 'de', 'fr', 'es', 'nl'...].
locales.config.tsexporting an array:['en', 'de', 'fr', 'es', 'nl'...]. - Baue eine Utility-Funktion getHreflangAlternates(pathname: string), die einen locale-unabhängigen Pfad (/about) nimmt und ein Array von { hreflang, href } Objekten für jedes Locale zurückgibt.
getHreflangAlternates(pathname: string)that takes a locale-agnostic path (/about) and returns an array of{ hreflang, href }objects for every locale. - Rufen Sie diese Funktion in Ihrer root layout.tsx auf und injizieren Sie die Ergebnisse über Next.js's metadata API unter Verwendung des alternates.languages Feldes. Dies wurde in Next.js 13.3 hinzugefügt und generiert automatisch die korrekten <link rel="alternate" hreflang="..."> Tags im <head>.
layout.tsxand inject the results via Next.js'smetadataAPI using thealternates.languagesfield. This was added in Next.js 13.3 and it generates the correct<link rel="alternate" hreflang="...">tags in<head>automatically. - Für Seiten mit dynamischen Inhalten (Blogposts, Produktseiten), bei denen nicht jedes Locale eine Übersetzung hat, übergeben Sie ein supportedLocales Array an Ihr Utility und generieren Sie Tags nur für Locales, bei denen der Inhalt tatsächlich existiert. Ein hreflang Tag, der auf eine Seite mit 404 zeigt, ist schlechter als kein hreflang Tag.
supportedLocalesarray to your utility and only generate tags for locales where the content actually exists. A hreflang tag pointing to a page that returns 404 is worse than no hreflang tag.
Der alternates.languages Ansatz in der metadata API ist die sauberste Lösung, die ich dafür gefunden habe. Kein Custom <Head> Jonglieren, keine Third-Party Pakete nur für die Tags.alternates.languages approach in the metadata API is the cleanest way I've found to handle this. No custom <Head> juggling, no third-party packages for the tags themselves.
---
Locale-Erkennung ohne lästig zu sein
Hier lauert eine UX-Falle, die auch SEO-Auswirkungen hat. Aggressive automatische Umleitung basierend auf dem Browser-Locale ist schlecht für beide.
Wenn ein Nutzer in Deutschland yoursite.com/en/article besucht und Ihre Middleware ihn ohne zu fragen auf /de/artikel hard-redirected, haben Sie gerade deren beabsichtigte Navigation zerstört. Sie haben möglicherweise auch das Teilen von Links beschädigt: Jemand teilt die englische URL, der Empfänger in Frankreich wird ins Französische umgeleitet, und der Inhalt, den er sehen sollte, existiert möglicherweise noch gar nicht auf Französisch.yoursite.com/en/article and your middleware hard-redirects them to /de/artikel without asking, you've just broken their intended navigation. You've also potentially broken link sharing: someone shares the English URL, the recipient in France gets bounced to French, and the content they were meant to see might not exist in French yet.
Meine Regel: erkennen, vorschlagen, nicht erzwingen. Nutzen Sie den Accept-Language Header, um beim ersten Besuch einen sinnvollen Standard zu setzen (gespeichert in einem Cookie), aber lassen Sie Nutzer diesen manuell überschreiben und respektieren Sie diese Überschreibung bei jedem folgenden Besuch. Der Cookie hat Vorrang vor dem Header.detect, suggest, don't force. Use the Accept-Language header to set a sensible default on first visit (stored in a cookie), but let users override it manually and honour that override on every subsequent visit. The cookie takes priority over the header.
Spezifisch für SEO: Googlebot sendet keine aussagekräftigen Accept-Language Header. Es crawlt Ihre URLs so wie sie sind. Stellen Sie also sicher, dass jede Locale-URL direkt zugänglich ist ohne Umleitung, und dass Ihre Sitemap alle Locale-Varianten explizit enthält.Accept-Language headers. It will crawl your URLs as-is. So make sure every locale URL is directly accessible without redirection, and that your sitemap includes all locale variants explicitly.
Die next-intl Middleware-Dokumentation behandelt diese Erkennungsstrategie gut, und das ist, wohin ich jeden mit einem neuen Projekt schicken würde.next-intl middleware documentation covers this detection strategy well, and it's where I'd send anyone starting a new project.
---
Sitemaps für mehrsprachige Websites
Eine einzelne sitemap.xml, die jede Locale-Variante enthält, ist für kleinere Websites in Ordnung. Bei großen Websites (zehntausende URLs über mehrere Locales hinweg) teile ich das auf: einen Sitemap-Index unter /sitemap.xml, dann separate Sitemap-Dateien pro Locale unter /sitemaps/en.xml, /sitemaps/de.xml und so weiter.sitemap.xml that includes every locale variant is fine for smaller sites. For large sites (tens of thousands of URLs across multiple locales) I split it: one sitemap index at /sitemap.xml , then separate sitemap files per locale at /sitemaps/en.xml , /sitemaps/de.xml, and so on.
Jeder URL-Eintrag sollte <xhtml:link rel="alternate">-Tags enthalten, die dein hreflang-Setup widerspiegeln. Das ist redundant zu den Tags im Seitenkopf, aber es hilft Googlebot, die Punkte auf Seiten zu verbinden, die es noch nicht gecrawlt hat.<xhtml:link rel="alternate"> tags mirroring your hreflang setup. This is redundant with the in-page tags but it helps Googlebot connect the dots on pages it hasn't crawled yet.
In Next.js 14+ kannst du Sitemaps dynamisch über app/sitemap.ts generieren. Der Return-Type unterstützt alternates.languages in der gleichen Form wie die Metadata API. Verbinde es mit dem gleichen getHreflangAlternates Utility, das du früher gebaut hast, und du erhältst kostenlos Konsistenz.app/sitemap.ts . The return type supports alternates.languages in the same shape as the metadata API. Wire it up to the same getHreflangAlternates utility you built earlier and you get consistency for free.
Eine konkrete Zahl: Bei einem Projekt mit 8 Locales und 4.500 Produkten reduzierte die Aufteilung der Sitemaps die durchschnittliche Crawl-Zeit pro Locale basierend auf GSC-Crawl-Statistiken um etwa 30%. Nicht weltbewegend, aber bedeutsam.
---
Translation Libraries: What I Actually Use
Es gibt drei, die es wert sind zu kennen. next-intl ist mein Standard für App Router Projekte. Es hat First-Class-Unterstützung für Server Components, handhabt Pluralisierung ordnungsgemäß, und die API ist sauber. Ich habe es seit Mitte 2023 bei etwa 40 Projekten eingesetzt.next-intl is my default for App Router projects. It has first-class support for server components, handles pluralisation properly, and the API is clean. I've used it on about 40 projects since mid-2023.
react-i18next ist kampferprobt und hat ein riesiges Ökosystem, aber sein mentales Modell wurde für Client Components gebaut und Server Component Unterstützung über den i18next Core hinzuzufügen fühlt sich im Vergleich zum nativen Ansatz von next-intl angeklebt an.i18next core feels bolted-on compared to next-intl's native approach.
Lingui lohnt sich, wenn dein Translation-Workflow professionelle Übersetzer statt eines CMS umfasst. Seine Extraktions-Tools sind überlegen. Aber für die meisten Agency-Projekte, bei denen Content aus Contentful oder Sanity kommt, ist next-intl einfacher.
Was ich vermeiden würde: dein eigenes System bauen. Ich habe es 2020 einmal für ein Projekt gemacht, bei dem der Client darauf bestand, seine bestehenden PHP-Translationsdateien direkt wiederzuverwenden. Es funktionierte, kaum, und ich verbrachte zwei Jahre damit, etwas zu pflegen, das next-intl jetzt in einem einzigen useTranslations()-Call handhabt.useTranslations() call.
---
FAQ
Unterstützt Next.js App Router den alten `next.config.js` i18n Block?
Nein. Der i18n-Konfigurationsschlüssel war spezifisch für den Pages Router. Wenn du zu App Router migrierst, musst du das Routing über ein [locale] Dynamic Segment und eine Middleware-Datei implementieren. Der alte Config-Block wird in App Router-Projekten stillschweigend ignoriert.i18n configuration key was specific to the Pages Router. If you're migrating to App Router, you need to implement routing via a [locale] dynamic segment and a middleware file. The old config block is silently ignored in App Router projects.
Sollte ich `lang` im URL-Pfad verwenden oder mich auf Subdomains verlassen?
Für die meisten Projekte ist lang im URL-Pfad (Sub-Path Routing wie /de/) die richtige Wahl. Subdomains fragmentieren deine Domain Authority und erfordern separate DNS-Konfiguration. ccTLDs lohnen sich nur bei der Komplexität, wenn lokale Vertrauenssignale tatsächlich die Conversion Rates in deinen spezifischen Märkten bewegen.lang in the URL path (sub-path routing like /de/) is the right call. Subdomains fragment your domain authority and require separate DNS configuration. ccTLDs are only worth the complexity if local trust signals are genuinely moving conversion rates in your specific markets.
Wie handhabt Googlebot JavaScript-gerenderte Locale-Inhalte?
Schlecht, manchmal. Wenn deine Locale-Erkennung rein clientseitig in einem React-Context läuft und das initiale HTML keinen Locale-Marker hat, kann Googlebot den Pre-Hydration-State indizieren. Löse Locale immer in Middleware auf und stelle sicher, dass das korrekte lang-Attribut auf dem <html>-Element im Server-gerenderten HTML ist. Überprüfe das, indem du die Quelle anschaust, nicht indem du Browser-DevTools verwendest (die den Post-Hydration-State zeigen).lang attribute is on the <html> element in the server-rendered HTML. Check this by viewing source, not by using browser devtools (which show post-hydration state).
Welcher `x-default` hreflang-Wert ist richtig für eine Website ohne Language-Selector-Seite?
Verweisen Sie auf die Homepage Ihres primären Gebietsschemas, normalerweise /en/. Das signalisiert Google, dass Englisch Ihr Fallback für Nutzer ist, deren Sprache Sie nicht unterstützen. Es ist keine Strafe, das zu tun — es ist nur ein schwaches Preference-Signal, das Google berücksichtigen kann oder auch nicht./en/. It signals to Google that English is your fallback for users whose language you don't support. It's not a penalty to do this, it's just a soft preference signal Google may or may not honour.
Kann ich meine hreflang-Implementierung ohne bezahltes SEO-Tool überprüfen?
Ja. Googles URL Inspection Tool in der Search Console zeigt Ihnen erkannte hreflang-Tags für jede indexierte URL. Es validiert Gegenseitigkeit nicht automatisch, aber Sie können manuell ein paar repräsentative Seiten überprüfen. Für Bulk-Validierung bietet Screaming Frog (kostenlos bis 500 URLs) Gegenseitigkeitsprüfungen im hreflang-Tab.Google Search Console's URL Inspection tool will show you detected hreflang tags for any indexed URL. It won't validate reciprocity automatically, but you can manually check a few representative pages. For bulk validation, Screaming Frog (free up to 500 URLs) handles reciprocity checks in its hreflang tab.
---
Multilinguales Next.js richtig hinzubekommen ist echte undankbare Arbeit. Es sind Config-Dateien, URL-Konsistenz und sicherzustellen, dass zwei Entwickler dieselbe Sache nicht zweimal in unterschiedlichen Komponenten implementiert haben. Aber auf den Websites, wo ich mir Zeit für eine ordentliche Umsetzung genommen habe, halten sich die Rankings über Regionen, und die, wo ich Abkürzungen genommen habe, haben mir die Lektionen beigebracht, die ich hier aufgeschrieben habe.
Der Berliner Kunde bekam übrigens seine deutschen Seiten schließlich in Deutschland zu ranken. Hat uns einen kompletten Crawl-Zyklus und viel Kaffee gekostet.
