# Internationalisierung (i18n) Dokumentation Hördle unterstützt vollständige Mehrsprachigkeit (Internationalisierung) für Deutsch und Englisch. ## Übersicht Die i18n-Implementierung basiert auf [next-intl](https://next-intl-docs.vercel.app/) und nutzt den Next.js App Router mit dynamischen `[locale]`-Segmenten. ## Unterstützte Sprachen - **Deutsch (de)** - Standardsprache - **Englisch (en)** ## URL-Struktur Alle Routen sind lokalisiert: - `http://localhost:3000/` → Redirect zu `/de` (Standard) - `http://localhost:3000/de` → Deutsche Version - `http://localhost:3000/en` → Englische Version - `http://localhost:3000/de/admin` → Admin-Dashboard (Deutsch) - `http://localhost:3000/de/Rock` → Rock Genre (Deutsch) - `http://localhost:3000/de/special/Weihnachtslieder` → Special (Deutsch) ## Architektur ### Verzeichnisstruktur ``` app/ [locale]/ # Lokalisierte Routen layout.tsx # Root Layout mit i18n Provider page.tsx # Homepage admin/ page.tsx # Admin Dashboard [genre]/ page.tsx # Genre-spezifische Seite special/ [name]/ page.tsx # Special-Seite i18n/ request.ts # next-intl Konfiguration messages/ de.json # Deutsche Übersetzungen en.json # Englische Übersetzungen lib/ i18n.ts # Helper-Funktionen für lokalisierte DB-Werte navigation.ts # Lokalisierte Navigation (Link, useRouter, etc.) ``` ### Übersetzungsdateien Die Übersetzungen sind in JSON-Dateien unter `messages/` organisiert: ```json { "Common": { "loading": "Laden...", "error": "Ein Fehler ist aufgetreten" }, "Game": { "play": "Abspielen", "pause": "Pause", "won": "Gewonnen!" }, "Home": { "welcome": "Willkommen bei Hördle" } } ``` ### Datenbank-Schema Die folgenden Modelle unterstützen mehrsprachige Felder: #### Genre - `name`: JSON `{ "de": "Rock", "en": "Rock" }` - `subtitle`: JSON `{ "de": "Klassischer Rock", "en": "Classic Rock" }` #### Special - `name`: JSON `{ "de": "Weihnachtslieder", "en": "Christmas Songs" }` - `subtitle`: JSON `{ "de": "Festliche Musik", "en": "Festive Music" }` #### News - `title`: JSON `{ "de": "Neues Feature", "en": "New Feature" }` - `content`: JSON `{ "de": "Markdown Inhalt...", "en": "Markdown content..." }` ### Helper-Funktionen #### `getLocalizedValue(value, locale, fallback?)` Extrahiert den lokalisierten Wert aus einem JSON-Objekt: ```typescript import { getLocalizedValue } from '@/lib/i18n'; const genreName = getLocalizedValue(genre.name, 'de'); // "Rock" const genreNameEn = getLocalizedValue(genre.name, 'en'); // "Rock" ``` **Fallback-Verhalten:** 1. Versucht die angeforderte Locale (`de` oder `en`) 2. Fallback zu `de` falls nicht vorhanden 3. Fallback zu `en` falls nicht vorhanden 4. Fallback zum ersten verfügbaren Schlüssel 5. Fallback zum übergebenen `fallback`-Parameter #### `createLocalizedObject(de, en?)` Erstellt ein lokalisiertes Objekt: ```typescript import { createLocalizedObject } from '@/lib/i18n'; const name = createLocalizedObject('Rock', 'Rock'); // { de: "Rock", en: "Rock" } ``` ## Verwendung in Komponenten ### Server Components ```typescript import { getTranslations } from 'next-intl/server'; import { getLocalizedValue } from '@/lib/i18n'; export default async function Page({ params }: { params: { locale: string } }) { const { locale } = await params; const t = await getTranslations('Home'); const genreName = getLocalizedValue(genre.name, locale); return

{t('welcome')}

; } ``` ### Client Components ```typescript 'use client'; import { useTranslations } from 'next-intl'; import { useLocale } from 'next-intl'; export default function Game() { const t = useTranslations('Game'); const locale = useLocale(); return ; } ``` ### Navigation Verwende die lokalisierte Navigation aus `lib/navigation.ts`: ```typescript import { Link } from '@/lib/navigation'; // Automatisch lokalisiert Admin Rock ``` ## Admin-Interface Das Admin-Dashboard unterstützt mehrsprachige Eingaben: 1. **Sprach-Tabs:** Wechsle zwischen `DE` und `EN` Tabs 2. **Genre/Special/News:** Alle Felder können in beiden Sprachen bearbeitet werden 3. **Vorschau:** Sieh dir die lokalisierte Version direkt an ### Beispiel: Genre erstellen 1. Öffne `/de/admin` 2. Wähle den `DE` Tab 3. Gib Name und Subtitle ein 4. Wechsle zum `EN` Tab 5. Gib die englischen Übersetzungen ein 6. Speichere ## Migration bestehender Daten Bestehende Daten werden automatisch migriert: 1. **Migration `20251128131405_add_i18n_columns`:** Fügt neue JSON-Spalten hinzu 2. **Migration `20251128132806_switch_to_json_columns`:** Konvertiert String-Spalten zu JSON **Wichtig:** Alte String-Werte werden automatisch in beide Sprachen kopiert: - `"Rock"` → `{ "de": "Rock", "en": "Rock" }` ## Middleware Die Middleware (`middleware.ts`) leitet Anfragen automatisch um: - `/` → `/de` (Standard) - Ungültige Locales → 404 - Validiert Locale-Parameter ## Sprachumschalter Die `LanguageSwitcher`-Komponente ermöglicht Nutzern, zwischen Sprachen zu wechseln: ```typescript import LanguageSwitcher from '@/components/LanguageSwitcher'; ``` Die aktuelle Route bleibt erhalten, nur die Locale ändert sich: - `/de/admin` → `/en/admin` - `/de/Rock` → `/en/Rock` ## API-Endpunkte API-Routen unterstützen einen optionalen `locale`-Parameter: ```typescript GET /api/genres?locale=de GET /api/specials?locale=en GET /api/news?locale=de ``` Falls kein `locale` angegeben wird, wird `de` als Standard verwendet. ## Best Practices ### 1. Immer `getLocalizedValue` verwenden ❌ **Falsch:** ```typescript {genre.name} // Rendert { de: "...", en: "..." } ``` ✅ **Richtig:** ```typescript {getLocalizedValue(genre.name, locale)} ``` ### 2. Übersetzungsschlüssel konsistent benennen Verwende Namespaces für bessere Organisation: - `Common.*` - Allgemeine UI-Elemente - `Game.*` - Spiel-spezifische Texte - `Home.*` - Homepage-Texte - `Navigation.*` - Navigations-Elemente ### 3. Fallbacks definieren Immer einen Fallback-Wert angeben: ```typescript const name = getLocalizedValue(genre.name, locale, 'Unbekannt'); ``` ### 4. Neue Übersetzungen hinzufügen 1. Füge den Schlüssel zu `messages/de.json` hinzu 2. Füge den Schlüssel zu `messages/en.json` hinzu 3. Verwende `useTranslations('Namespace')` oder `getTranslations('Namespace')` ## Troubleshooting ### 404-Fehler auf `/de` oder `/en` **Problem:** Route wird nicht gefunden. **Lösung:** 1. Überprüfe, ob `middleware.ts` korrekt konfiguriert ist 2. Stelle sicher, dass `app/[locale]/layout.tsx` existiert 3. Prüfe die `i18n/request.ts` Konfiguration ### "Objects are not valid as a React child" **Problem:** Ein JSON-Objekt wird direkt gerendert statt des lokalisierten Werts. **Lösung:** Verwende `getLocalizedValue()`: ```typescript // ❌ Falsch {genre.name} // ✅ Richtig {getLocalizedValue(genre.name, locale)} ``` ### Übersetzungen werden nicht angezeigt **Problem:** Texte erscheinen als Schlüssel (z.B. `"Game.play"`). **Lösung:** 1. Überprüfe, ob der Übersetzungsschlüssel in `messages/de.json` und `messages/en.json` existiert 2. Stelle sicher, dass der Namespace korrekt ist: `useTranslations('Game')` für `Game.play` 3. Prüfe die JSON-Syntax auf Fehler ### Admin-Interface zeigt Objekte statt Text **Problem:** In Dropdowns oder Listen werden `{ de: "...", en: "..." }` angezeigt. **Lösung:** Verwende `getLocalizedValue()` in allen Render-Funktionen: ```typescript // ❌ Falsch // ✅ Richtig ``` ## Erweiterung um weitere Sprachen Um eine neue Sprache hinzuzufügen (z.B. Französisch): 1. **Übersetzungsdatei erstellen:** ```bash cp messages/de.json messages/fr.json ``` 2. **Übersetzungen hinzufügen:** Bearbeite `messages/fr.json` mit französischen Übersetzungen 3. **Locale zur Konfiguration hinzufügen:** - `i18n/request.ts`: `const locales = ['en', 'de', 'fr'];` - `middleware.ts`: `locales: ['en', 'de', 'fr']` - `lib/navigation.ts`: `export const locales = ['de', 'en', 'fr'] as const;` 4. **Layout aktualisieren:** ```typescript // app/[locale]/layout.tsx if (!['en', 'de', 'fr'].includes(locale)) { notFound(); } ``` 5. **LanguageSwitcher erweitern:** Füge einen Button für `fr` hinzu 6. **Datenbank-Migration:** Bestehende Daten behalten ihre Struktur, neue Einträge können optional `fr` enthalten ## Weitere Ressourcen - [next-intl Dokumentation](https://next-intl-docs.vercel.app/) - [Next.js App Router i18n](https://nextjs.org/docs/app/building-your-application/routing/internationalization)