Dokumentation
Internationalisierung
Wie Motoko Base Locales, Routing, Message-Kataloge und SEO für mehrsprachige SaaS-Produkte handhabt.
Motoko Base liefert eine vollständige Internationalisierungs-Basis (i18n) auf Basis von next-intl. Du erhältst fünf Locales, locale-aware Marketing-URLs, übersetzte Dashboard-Chrome, lokalisierte Docs und Changelog sowie ein klares Muster, um Übersetzungen auf eigene Features auszuweiten.
Diese Seite erklärt wie i18n verdrahtet ist. Schritt-für-Schritt-Anleitungen zum Hinzufügen von Copy findest du unter Übersetzungen hinzufügen.
Unterstützte Locales
| Locale | Code | Marketing-URL-Präfix | Beispiel |
|---|---|---|---|
| Englisch (Standard) | en | (keiner) | /, /docs, /terms |
| Spanisch | es | /es | /es/docs, /es/terms |
| Deutsch | de | /de | /de/docs |
| Französisch | fr | /fr | /fr/docs |
| Brasilianisches Portugiesisch | pt-BR | /pt-br | /pt-br/docs |
Locale-Definitionen liegen in src/i18n/routing.ts. Labels im Sprachumschalter kommen aus localeLabels in derselben Datei.
Hybrides Routing-Modell
Motoko Base nutzt zwei Routing-Strategien bewusst:
Locale-präfixierte Oberflächen (Marketing)
Diese Routen liegen unter src/app/[locale]/:
- Landing Page und Marketing-Sections
- Rechtsseiten (
/terms,/privacy,/licence) - Dokumentation (
/docs) - Changelog (
/changelog)
Englisch nutzt URLs ohne Präfix (localePrefix: 'as-needed'). Andere Locales erhalten ein Präfix (/fr, /de, /es, /pt-br).
Der Marketing-Sprachumschalter (MarketingLanguageSwitcher) navigiert zwischen äquivalenten Pfaden je Locale und behält den aktuellen Seitenkontext.
Unpräfixierte App-Oberflächen
Diese Routen bleiben an festen Pfaden ohne Locale-Segment:
- Dashboard und Demo-Features (
/dashboard/...) - Auth-Flows (
/sign-in,/sign-up, Passwort vergessen/zurücksetzen/verifizieren) - API-Routen, Webhooks, öffentliche Redirects (
/api,/r/[slug])
Die aktive Locale wird zur Request-Zeit aufgelöst — nicht aus der URL.
Locale-Auflösung
Wenn next-intl eine Locale benötigt, wendet src/i18n/resolve-locale.ts diese Priorität an:
Angemeldeter Nutzer → user_preferences.language (Datenbank)
Gast → NEXT_LOCALE Cookie
Fallback → Accept-Language Header
Standard → enErster Besuch von /: src/proxy.ts liest Accept-Language einmal. Bei einer unterstützten Locale außer Englisch erfolgt Redirect zur präfixierten Marketing-URL und das Locale-Cookie wird gesetzt. Danach haben explizite Wahl (Switcher oder Settings) Vorrang.
Angemeldete Nutzer: Speichern der Sprache unter Einstellungen → App → Darstellung (/dashboard/settings/app/appearance) aktualisiert die Datenbank via updateLanguagePreferenceAction, setzt das Locale-Cookie und aktualisiert die UI via LocalePreferenceSync.
Dateistruktur
src/i18n/
├── routing.ts # Locales, Präfixe, Cookie-Name
├── navigation.ts # Locale-aware Link, useRouter, usePathname
├── request.ts # next-intl Server-Config (lädt Messages)
├── resolve-locale.ts # Locale-Auflösung für App/Auth-Routen
├── load-messages.ts # Dynamischer Import der Message-Kataloge
└── fumadocs.ts # Docs/Changelog Content-Locales
messages/
├── en/
│ ├── index.ts # Aggregiert alle Namespaces
│ ├── common.json
│ ├── marketing.json
│ ├── auth.json
│ └── … # Eine JSON-Datei pro Feature/Bereich
├── es/
├── de/
├── fr/
└── pt-BR/
content/docs/ # Lokalisierte MDX via Dot-Suffix (z. B. page.de.mdx)
content/changelog/Gemeinsame Formatierungs-Helfer für Datum und Zahlen liegen in src/lib/i18n/format.ts. Nutze diese statt "en-US" in Intl-Aufrufen zu hardcoden.
Message-Kataloge
Übersetzungen sind typisierte JSON-Namespaces unter messages/{locale}/. Jede Locale exportiert in index.ts ein Objekt für next-intl:
// messages/en/index.ts
import common from "./common.json";
import auth from "./auth.json";
import overview from "./overview.json";
const messages = { common, auth, overview };
export default messages;Nutze einen Namespace pro Feature oder Bereich (auth, settings, billing, overview, …). Halte Keys über alle Locales stabil — nur die String-Werte ändern sich.
Marketing: Struktur vs. Copy
Marketing folgt einem Split-Pattern:
| Schicht | Ort | Enthält |
|---|---|---|
| Struktur | src/features/marketing/marketing-structure.ts | IDs, hrefs, Tech-Refs, Reihenfolge |
| Copy | messages/{locale}/marketing.json | Headlines, Beschreibungen, FAQ, Footer |
Section-Komponenten mergen Struktur + Messages in useMarketingContent().
Übersetzungen in Komponenten
Client-Komponenten
"use client";
import { useTranslations } from "next-intl";
export function MyPage() {
const t = useTranslations("overview");
return (
<h1>{t("welcome.title", { name: "Alex" })}</h1>
);
}Server-Komponenten und Metadata
import { getTranslations } from "next-intl/server";
export async function generateMetadata() {
const t = await getTranslations("docs");
return { title: t("metaTitle") };
}Locale-aware Links
Importiere Navigation-Helfer von @/i18n/navigation — nicht next/link — auf locale-präfixierten Seiten:
import { Link } from "@/i18n/navigation";
<Link href="/docs">Documentation</Link>Auf Dashboard- und Auth-Seiten reichen Standard-Next.js-Links — diese Routen sind nicht locale-präfixiert.
Lokalisierte Docs und Changelog
Docs und Changelog nutzen Fumadocs i18n mit dem Dot-Parser (page.de.mdx, page.es.mdx, …). Englisch ist die Basisdatei (page.mdx).
Sidebar-Reihenfolge und Section-Titel kommen aus meta.json im jeweiligen Ordner. Lokalisierte Sidebar-Titel in meta.de.json, meta.fr.json usw.
Neue lokalisierte Docs-Seite hinzufügen:
- Englische MDX-Datei schreiben
- Geschwisterdateien je Locale (
.de.mdx,.es.mdx,.fr.mdx,.pt-BR.mdx) - Slug einmal in
meta.jsondes Ordners eintragen — Slugs sind über Locales geteilt
SEO
Lokalisierte Marketing-, Rechts-, Docs- und Changelog-URLs erhalten:
- Canonical URLs pro Locale via
buildPageMetadata()insrc/lib/seo/metadata.ts hreflang-Alternates für jede Locale plusx-default- Sitemap-Einträge je Locale in
src/app/sitemap.ts
Open-Graph-Locale-Tags mappt localeToOpenGraphLocale().
Sprachsteuerung für Nutzer
| Oberfläche | Steuerung | Effekt |
|---|---|---|
| Marketing-Navbar | Sprachumschalter | Navigiert zu präfixierter URL + setzt Cookie |
| Einstellungen → App → Darstellung | Sprachauswahl | Speichert in DB via updateLanguagePreferenceAction, setzt Cookie, aktualisiert App-Locale |
Erster Besuch / | Accept-Language | Einmaliger Redirect zur passenden Locale |
Auth-Seiten erben Locale aus Cookie oder Accept-Language — ohne eigenes URL-Präfix.
Aktueller Übersetzungsstand
| Bereich | Status |
|---|---|
| Marketing-Landing, Rechtsseiten | Vollständig übersetzt |
| Dokumentation & Changelog | Vollständig übersetzt |
| Auth-Formulare | Übersetzt |
| Dashboard-Shell-Nav, Settings | Übersetzt |
| Overview-Seite | Übersetzt (Referenzbeispiel) |
| Demo-Features | Übersetzte UI-Strings — gleiches Muster erweiterbar |
Nächste Schritte
Übersetzungen hinzufügen — Praxisleitfaden für neue Features und Keys.
Projektstruktur — Feature-Ordner und gemeinsamer Code.
Konfiguration — Dashboard-Nav, Billing-Labels und Produkt-Config.