Dokumentation

Internationalisierung

Wie Motoko Base Locales, Routing, Message-Kataloge und SEO für mehrsprachige SaaS-Produkte handhabt.

Öffnen inChatGPT (öffnet in neuem Tab)Claude (öffnet in neuem Tab)Cursor (öffnet in neuem Tab)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

LocaleCodeMarketing-URL-PräfixBeispiel
Englisch (Standard)en(keiner)/, /docs, /terms
Spanisches/es/es/docs, /es/terms
Deutschde/de/de/docs
Französischfr/fr/fr/docs
Brasilianisches Portugiesischpt-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             →  en

Erster 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:

SchichtOrtEnthält
Struktursrc/features/marketing/marketing-structure.tsIDs, hrefs, Tech-Refs, Reihenfolge
Copymessages/{locale}/marketing.jsonHeadlines, 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") };
}

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:

  1. Englische MDX-Datei schreiben
  2. Geschwisterdateien je Locale (.de.mdx, .es.mdx, .fr.mdx, .pt-BR.mdx)
  3. Slug einmal in meta.json des Ordners eintragen — Slugs sind über Locales geteilt

SEO

Lokalisierte Marketing-, Rechts-, Docs- und Changelog-URLs erhalten:

  • Canonical URLs pro Locale via buildPageMetadata() in src/lib/seo/metadata.ts
  • hreflang-Alternates für jede Locale plus x-default
  • Sitemap-Einträge je Locale in src/app/sitemap.ts

Open-Graph-Locale-Tags mappt localeToOpenGraphLocale().

Sprachsteuerung für Nutzer

OberflächeSteuerungEffekt
Marketing-NavbarSprachumschalterNavigiert zu präfixierter URL + setzt Cookie
Einstellungen → App → DarstellungSprachauswahlSpeichert in DB via updateLanguagePreferenceAction, setzt Cookie, aktualisiert App-Locale
Erster Besuch /Accept-LanguageEinmaliger Redirect zur passenden Locale

Auth-Seiten erben Locale aus Cookie oder Accept-Language — ohne eigenes URL-Präfix.

Aktueller Übersetzungsstand

BereichStatus
Marketing-Landing, RechtsseitenVollständig übersetzt
Dokumentation & ChangelogVollstä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.