In einer globalisierten Welt sind mehrsprachige Websites keine Ausnahme, sondern eine Notwendigkeit. Internationalisierung (i18n) ermöglicht es, Inhalte an verschiedene Sprachen und Regionen anzupassen, was nicht nur die Nutzererfahrung verbessert, sondern auch neue Zielgruppen erreicht.
Mit dem App Router von Next.js wird die Einrichtung mehrsprachiger Seiten einfacher denn je. Next.js unterstützt nativ dynamische Routen für verschiedene Sprachen und bietet leistungsstarke Tools wie Middleware, um Benutzer automatisch in ihrer bevorzugten Sprache weiterzuleiten.
In diesem Artikel zeigen wir dir, wie du i18n in Next.js einrichtest, Inhalte lokalisiert darstellst und SEO-optimierte mehrsprachige Seiten erstellst. Dabei verwenden wir Best Practices und Tools, um den Entwicklungsprozess effizient zu gestalten.
Grundlagen: Was ist Internationalisierung (i18n)?
Internationalisierung (i18n) ist der Prozess, eine Anwendung so zu gestalten, dass sie Inhalte und Funktionalitäten für verschiedene Sprachen und Regionen bereitstellen kann. Es handelt sich dabei um eine wichtige Voraussetzung, um globale Zielgruppen zu erreichen und die Benutzererfahrung zu optimieren.
Internationalisierung (i18n) vs. Lokalisierung (l10n)
-
Internationalisierung (i18n):
Der technische Prozess, eine Anwendung anpassungsfähig für verschiedene Sprachen und Regionen zu machen. Dazu gehören dynamische Routen, flexible Datumsformate und Unterstützung für mehrere Schriftsysteme. -
Lokalisierung (l10n):
Die tatsächliche Anpassung der Inhalte an eine spezifische Sprache und Region, wie z. B. Übersetzungen von Texten oder regionale Anpassungen wiede-DE
für Deutschland oderen-US
für die Vereinigten Staaten.
Sprache und Regionen
Eine Sprache wird in der Regel durch einen Sprachcode identifiziert, manchmal ergänzt durch eine Regionsangabe. Beispiele:
en-US
: Englisch (USA).de-DE
: Deutsch (Deutschland).de
: Deutsch, ohne spezifische Region.
Herausforderung in der Praxis
Internationalisierung kann technische und organisatorische Herausforderungen mit sich bringen, wie z. B.:
- Verwaltung von Übersetzungen für mehrere Sprachen.
- Dynamische Anpassung von Routen und Inhalten basierend auf der bevorzugten Sprache des Nutzers.
- Sicherstellung, dass mehrsprachige Inhalte SEO-optimiert sind.
Next.js bietet im App Router native Funktionen, um diese Herausforderungen zu bewältigen, wie z. B. dynamische Routen (app/[lang]/
), Middleware für automatische Sprachweiterleitung und einfache Integration von Übersetzungsdateien.
i18n im Next.js App Router einrichten
Der App Router von Next.js bietet eine native Unterstützung für Internationalisierung (i18n), die sich leicht in neue und bestehende Projekte integrieren lässt. Dynamische Routen, Middleware und Konfigurationsoptionen ermöglichen es, mehrsprachige Websites effizient zu erstellen.
Dynamische Routen im App Router
Im App Router werden Routen durch dynamische Segmente definiert, z. B. app/[lang]/
. Dieses Segment repräsentiert die aktuelle Sprache und kann verwendet werden, um Inhalte sprachspezifisch darzustellen.
Beispiel: Dynamische Route für Sprachen
// app/[lang]/page.tsx
export default function Page({ params }: { params: { lang: string } }) {
const { lang } = params; // Sprache aus der URL, z. B. "en" oder "de"
return <h1>Aktuelle Sprache: {lang}</h1>;
}
Konfiguration der next.config.ts
Um unterstützte Sprachen und eine Standard-Sprache festzulegen, kannst du die i18n-Optionen in der next.config.ts
konfigurieren:
Beispiel: Konfigurationsdatei
// next.config.ts
import { NextConfig } from "next";
const config: NextConfig = {
i18n: {
locales: ["en", "de"], // Unterstützte Sprachen
defaultLocale: "en", // Standardsprache
},
};
export default config;
Automatische Sprachweiterleitung mit Middleware
Die Middleware von Next.js kann verwendet werden, um Benutzer basierend auf ihrer bevorzugten Sprache (aus dem Accept-Language
-Header) automatisch weiterzuleiten. Dies sorgt für eine bessere Nutzererfahrung.
Beispiel: Middleware für Sprachweiterleitung
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { match } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';
const locales = ['en', 'de']; // Unterstützte Sprachen
const defaultLocale = 'en'; // Standardsprache
function getLocale(request: NextRequest): string {
const negotiator = new Negotiator({
headers: { 'accept-language': request.headers.get('accept-language') || '' },
});
const languages = negotiator.languages();
return match(languages, locales, defaultLocale);
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Prüfen, ob bereits eine Sprache in der URL enthalten ist
if (locales.some((locale) => pathname.startsWith(`/${locale}`))) {
return NextResponse.next();
}
// Sprache bestimmen und umleiten
const locale = getLocale(request);
request.nextUrl.pathname = `/${locale}${pathname}`;
return NextResponse.redirect(request.nextUrl);
}
export const config = {
matcher: ['/((?!_next).*)'], // Middleware für alle außer interne Pfade
};
Vorteile der Integration im App Router
- Flexibilität durch dynamische Routen: Sprachen können einfach durch die URL dargestellt werden (
/de
,/en
). - Automatische Sprachweiterleitung: Die Middleware sorgt für eine bessere Nutzererfahrung durch Lokalisierung basierend auf Browserpräferenzen.
- Einfache Konfiguration: Die i18n-Optionen in
next.config.ts
sind intuitiv und leistungsstark.
Lokalisierung von Inhalten
Nachdem die grundlegende i18n-Konfiguration im App Router eingerichtet ist, besteht der nächste Schritt darin, Inhalte basierend auf der ausgewählten Sprache zu lokalisieren. Dies wird oft durch Übersetzungsdateien und eine zentrale Funktion zur Verwaltung der Übersetzungen erreicht.
Verwendung von Übersetzungsdateien
Eine typische Methode zur Lokalisierung ist die Verwendung von JSON-Dateien, die für jede unterstützte Sprache spezifische Übersetzungen enthalten.
Beispiel: Übersetzungsdateien
// app/[lang]/dictionaries/en.json
{
"welcome": "Welcome to our website!",
"cart": {
"add": "Add to Cart",
"checkout": "Proceed to Checkout"
}
}
// app/[lang]/dictionaries/de.json
{
"welcome": "Willkommen auf unserer Website!",
"cart": {
"add": "In den Warenkorb",
"checkout": "Zur Kasse"
}
}
Laden von Übersetzungen mit getDictionary
Eine Funktion wie getDictionary
kann verwendet werden, um die Übersetzungsdatei basierend auf der aktuellen Sprache zu laden. Dank des App Routers und der standardmäßigen Nutzung von Server Components ist dies effizient und beeinflusst nicht die Größe des Client-Bundles.
Beispiel: getDictionary
// app/[lang]/dictionaries.ts
import 'server-only';
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
de: () => import('./dictionaries/de.json').then((module) => module.default),
};
export async function getDictionary(lang: 'en' | 'de') {
return dictionaries[lang]();
}
Integration der Übersetzungen in eine Seite
Die geladenen Übersetzungen können in einer Seite oder einem Layout verwendet werden, um Inhalte dynamisch anzuzeigen.
Beispiel: Lokalisierte Seite
// app/[lang]/page.tsx
import { getDictionary } from './dictionaries';
export default async function Page({ params }: { params: { lang: 'en' | 'de' } }) {
const dictionary = await getDictionary(params.lang);
return (
<div>
<h1>{dictionary.welcome}</h1>
<button>{dictionary.cart.add}</button>
</div>
);
}
Vorteile des Ansatzes
- Effizienz: Übersetzungsdateien werden serverseitig geladen und belasten nicht das Client-Bundle.
- Flexibilität: Unterstützung für beliebig viele Sprachen durch einfaches Hinzufügen weiterer JSON-Dateien.
- Modularität: Übersetzungen können leicht in verschiedenen Komponenten oder Layouts wiederverwendet werden.
Routing und SEO für mehrsprachige Seiten
Die Optimierung des Routings und der Suchmaschinenfreundlichkeit (SEO) ist ein zentraler Bestandteil der Internationalisierung (i18n). Next.js bietet leistungsstarke Funktionen, um sicherzustellen, dass mehrsprachige Inhalte korrekt dargestellt und von Suchmaschinen optimal indexiert werden.
Subpath- und Domain-basierte Lokalisierung
Es gibt zwei Hauptansätze, um unterschiedliche Sprachen im Routing darzustellen:
-
Subpath-basierte Lokalisierung
Die Sprache wird in der URL als Pfadsegment angegeben. Beispiel:- Englisch:
/en/products
- Deutsch:
/de/products
Dies ist die empfohlene Methode im App Router, da sie flexibel und leicht zu konfigurieren ist.
Beispiel: Subpath-Konfiguration
// next.config.ts import { NextConfig } from 'next'; const config: NextConfig = { i18n: { locales: ['en', 'de'], defaultLocale: 'en', }, }; export default config;
- Englisch:
-
Domain-basierte Lokalisierung
Jede Sprache hat ihre eigene Domain. Beispiel:- Englisch:
example.com
- Deutsch:
example.de
Diese Methode eignet sich für größere Projekte oder regionale Ausrichtungen. Sie erfordert jedoch zusätzliche DNS- und Hosting-Konfiguration.
- Englisch:
Verwendung von hreflang
-Tags
hreflang
-Tags helfen Suchmaschinen zu verstehen, welche Sprachversion einer Seite angezeigt werden soll. Next.js ermöglicht es, hreflang
-Tags dynamisch zu generieren.
Beispiel: Generierung von hreflang
-Tags
// app/[lang]/layout.tsx
export default function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: 'en' | 'de' };
}) {
return (
<html lang={params.lang}>
<head>
<link rel="alternate" href="/en/products" hrefLang="en" />
<link rel="alternate" href="/de/products" hrefLang="de" />
</head>
<body>{children}</body>
</html>
);
}
Generierung statischer Routen mit generateStaticParams
Um für jede Sprache statische Seiten zu erstellen, kannst du die Funktion generateStaticParams
verwenden. Dadurch wird sichergestellt, dass alle Sprachversionen einer Seite während der Build-Phase generiert werden.
Beispiel: Generierung von statischen Routen
// app/[lang]/layout.tsx
export async function generateStaticParams() {
return [{ lang: 'en' }, { lang: 'de' }];
}
export default function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: 'en' | 'de' };
}) {
return (
<html lang={params.lang}>
<body>{children}</body>
</html>
);
}
Vorteile von SEO-Optimierungen
- Verbesserte Sichtbarkeit: Suchmaschinen erkennen sprachspezifische Inhalte besser.
- Vermeidung von Duplicate Content: Durch
hreflang
wird klar, welche Sprachversion für welchen Nutzer relevant ist. - Benutzerfreundlichkeit: Nutzer werden direkt zur richtigen Sprachversion weitergeleitet.
Tools und Libraries für i18n
Neben den nativen i18n-Funktionen von Next.js gibt es eine Vielzahl von Tools und Libraries, die die Internationalisierung erleichtern. Diese bieten zusätzliche Features wie dynamische Übersetzungen, Pluralformen und Kontextvariablen.
next-intl
next-intl ist eine beliebte Library, die speziell für Next.js entwickelt wurde. Sie bietet eine einfache Möglichkeit, Übersetzungen zu verwalten und dynamisch zu laden. Die Integration mit dem App Router ist unkompliziert.
Vorteile:
- Nahtlose Integration mit dem App Router.
- Unterstützung für Pluralformen und dynamische Werte.
- Kompakte API für den Zugriff auf Übersetzungen.
Beispiel: Verwendung von next-intl
// app/[lang]/layout.tsx
import { NextIntlProvider } from 'next-intl';
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: string };
}) {
const messages = (await import(`./messages/${params.lang}.json`)).default;
return (
<NextIntlProvider messages={messages} locale={params.lang}>
{children}
</NextIntlProvider>
);
}
i18next
i18next ist eine leistungsstarke Library, die für verschiedene Plattformen verfügbar ist. Sie unterstützt tiefgehende Lokalisierungsanforderungen, darunter Namespaces und Fallback-Strategien.
Vorteile:
- Flexibilität durch Namespaces und Hierarchien.
- Erweiterbar durch Plugins, z. B. für Backends oder Zeitformate.
- Breite Community-Unterstützung.
Beispiel: Verwendung von i18next
// app/[lang]/i18n.ts
import i18next from 'i18next';
import Backend from 'i18next-fs-backend';
i18next.use(Backend).init({
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json',
},
lng: 'en',
fallbackLng: 'en',
ns: ['common'],
});
formatjs
formatjs bietet eine umfassende Lösung für die Lokalisierung, einschließlich Unterstützung für Formatierungen von Zahlen, Datumsangaben und Währungen. Es eignet sich besonders für Projekte mit komplexen Anforderungen.
Vorteile:
- Erweiterte Unterstützung für internationale Datums- und Zahlenformate.
- APIs für Server- und Client-seitige Anwendungen.
- Kompatibilität mit React über
react-intl
.
Beispiel: Verwendung von react-intl
// app/[lang]/page.tsx
import { IntlProvider } from 'react-intl';
export default function Page({ params }: { params: { lang: string } }) {
const messages = require(`./locales/${params.lang}.json`);
return (
<IntlProvider locale={params.lang} messages={messages}>
<LocalizedComponent />
</IntlProvider>
);
}
// app/components/localized-component.tsx
"use client"
import { useIntl } from 'react-intl';
function LocalizedComponent() {
const { formatMessage } = useIntl();
return <h1>{formatMessage({ id: 'welcome' })}</h1>;
}
Fazit zu Tools
- Extra Tools wie next-intl, i18next oder formatjs können den Entwicklungsprozess vereinfachen, sind aber nicht immer notwendig.
- Next.js bietet von Haus aus alle notwendigen Funktionen, um Internationalisierung und Lokalisierung zu realisieren. Mit einer Minimallösung, die nur Next.js nutzt, kannst du viele Anforderungen abdecken, z. B. durch die Verwendung dynamischer Routen und einfacher Übersetzungsdateien.
- Der Verzicht auf zusätzliche Libraries reduziert Abhängigkeiten, vereinfacht die Wartung und verbessert die Performance, da weniger Code geladen werden muss.
Die Wahl des richtigen Ansatzes hängt von den Anforderungen deines Projekts ab. Für kleine bis mittlere Projekte ist die native Unterstützung von Next.js oft ausreichend. Bei größeren Projekten oder spezifischen Anforderungen bieten Tools wie next-intl oder i18next zusätzliche Flexibilität und Komfort.
Beispielprojekt: Mehrsprachige Website
Hier ist eine Schritt-für-Schritt-Anleitung zur Einrichtung einer zweisprachigen Website (Englisch und Deutsch) mit dem Next.js App Router, dynamischen Routen und Übersetzungsdateien.
1. Projektstruktur erstellen
Erstelle die Basisstruktur für dynamische Routen und Übersetzungsdateien:
app/
├── [lang]/
│ ├── page.tsx
│ ├── layout.tsx
│ ├── dictionaries.ts
│ ├── dictionaries/
│ │ ├── en.json
│ │ ├── de.json
2. Übersetzungsdateien anlegen
Definiere die Texte für die einzelnen Sprachen:
// app/[lang]/dictionaries/en.json
{
"welcome": "Welcome to our website!",
"cart": {
"add": "Add to Cart",
"checkout": "Proceed to Checkout"
}
}
// app/[lang]/dictionaries/de.json
{
"welcome": "Willkommen auf unserer Website!",
"cart": {
"add": "In den Warenkorb",
"checkout": "Zur Kasse"
}
}
3. Funktion zum Laden der Übersetzungen implementieren
Erstelle eine serverseitige Funktion, um die entsprechenden Übersetzungen zu laden:
// app/[lang]/dictionaries.ts
import 'server-only';
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
de: () => import('./dictionaries/de.json').then((module) => module.default),
};
export async function getDictionary(lang: 'en' | 'de') {
return dictionaries[lang]();
}
4. Sprachparameter und Layout integrieren
Nutze den Sprachparameter aus der dynamischen Route, um die Übersetzungen im Layout bereitzustellen:
// app/[lang]/layout.tsx
import { getDictionary } from './dictionaries';
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: 'en' | 'de' };
}) {
const dictionary = await getDictionary(params.lang);
return (
<html lang={params.lang}>
<body>
<header>
<nav>
<a href={`/${params.lang}`}>{dictionary.welcome}</a>
</nav>
</header>
{children}
</body>
</html>
);
}
5. Mehrsprachige Inhalte anzeigen
Zeige Inhalte basierend auf den Übersetzungen in der page.tsx
-Datei an:
// app/[lang]/page.tsx
import { getDictionary } from './dictionaries';
export default async function Page({ params }: { params: { lang: 'en' | 'de' } }) {
const dictionary = await getDictionary(params.lang);
return (
<div>
<h1>{dictionary.welcome}</h1>
<button>{dictionary.cart.add}</button>
</div>
);
}
6. Middleware für Sprachweiterleitung hinzufügen (optional)
Leite Benutzer automatisch auf ihre bevorzugte Sprache weiter:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (!pathname.startsWith('/en') && !pathname.startsWith('/de')) {
return NextResponse.redirect('/en');
}
}
Best Practices für i18n in Next.js
Strukturierung von Übersetzungsdateien
- Modularisierung: Gruppiere Übersetzungen thematisch in Namespaces (z. B.
common.json
,cart.json
). - Konsistenz: Verwende konsistente Schlüssel in allen Sprachdateien, z. B.
cart.add
. - Skalierbarkeit: Nutze separate Ordner für jede Sprache, um die Übersichtlichkeit zu erhöhen.
Umgang mit Pluralformen und Variablen
- Pluralformen: Verwende Libraries wie
react-intl
oderi18next
, die Pluralformen unterstützen. - Variablen: Integriere dynamische Inhalte mit Platzhaltern:
"cart_items": "You have {count} items in your cart."
const message = dictionary.cart_items.replace('{count}', '3');
Qualitätssicherung bei Übersetzungen
- Automatische Prüfungen: Nutze Tools wie
i18n-lint
, um fehlende oder inkonsistente Übersetzungen zu identifizieren. - Manuelle Reviews: Lasse Übersetzungen von muttersprachlichen Personen überprüfen.
- Fallback-Strategien: Implementiere Fallbacks, um fehlende Übersetzungen elegant zu behandeln.
Fazit
Die Internationalisierung (i18n) in Next.js ist dank des App Routers und nativer Funktionen effizient umsetzbar. Mit dynamischen Routen, Übersetzungsdateien und Middleware kannst du eine mehrsprachige Website aufbauen, die sowohl benutzerfreundlich als auch SEO-optimiert ist.
Zusammenfassung:
- Minimallösung: Next.js allein reicht oft aus, um i18n umzusetzen, und reduziert Abhängigkeiten.
- Zusätzliche Tools: Libraries wie
next-intl
oderi18next
bieten erweiterte Funktionen und Flexibilität. - Best Practices: Eine saubere Struktur, Pluralformen und Qualitätssicherung sorgen für eine skalierbare und benutzerfreundliche Lösung.
Ob mit nativen Funktionen oder zusätzlichen Tools – Next.js bietet alle notwendigen Bausteine, um eine mehrsprachige Website effizient umzusetzen.