diff --git a/I18N.md b/I18N.md
new file mode 100644
index 0000000..fdeb21b
--- /dev/null
+++ b/I18N.md
@@ -0,0 +1,349 @@
+# 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)
+
diff --git a/README.md b/README.md
index 6193f73..31f3c76 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@ Eine Web-App inspiriert von Heardle, bei der Nutzer täglich einen Song anhand k
## Features
+- **🌍 Mehrsprachigkeit (i18n):** Vollständige Unterstützung für Deutsch und Englisch mit automatischer Sprachumleitung und lokalisierten Inhalten.
- **Tägliches Rätsel:** Jeden Tag ein neuer Song für alle Nutzer.
- **Inkrementelle Hinweise:** Startet mit 2 Sekunden, dann 4s, 7s, 11s, 16s, 30s, bis 60s (7 Versuche).
- **Admin Dashboard:**
@@ -51,6 +52,17 @@ Eine Web-App inspiriert von Heardle, bei der Nutzer täglich einen Song anhand k
- Special-Verknüpfung: Direkte Links zu Specials in News-Beiträgen.
- Verwaltung über das Admin-Dashboard.
+## Internationalisierung (i18n)
+
+Hördle unterstützt vollständige Mehrsprachigkeit für Deutsch und Englisch.
+
+👉 **[Vollständige i18n-Dokumentation](I18N.md)**
+
+**Schnellstart:**
+- Deutsche Version: `http://localhost:3000/de`
+- Englische Version: `http://localhost:3000/en`
+- Root (`/`) leitet automatisch zur Standardsprache (Deutsch) um
+
## White Labeling
Hördle ist "White Label Ready". Das bedeutet, du kannst das Branding (Name, Farben, Logos) komplett anpassen, ohne den Code zu ändern.
@@ -103,7 +115,7 @@ Das Ziel ist es, den Song mit so wenigen Hinweisen wie möglich zu erraten und d
```bash
npm run dev
```
- Die App läuft unter `http://localhost:3000`.
+ Die App läuft unter `http://localhost:3000` (leitet automatisch zu `/de` um).
## Deployment mit Docker
@@ -139,7 +151,7 @@ Das Projekt ist für den Betrieb mit Docker optimiert.
- Beim Start des Containers wird automatisch ein Migrations-Skript ausgeführt, das fehlende Cover-Bilder aus den MP3s extrahiert.
4. **Admin-Zugang:**
- - URL: `/admin`
+ - URL: `/de/admin` oder `/en/admin`
- Standard-Passwort: `admin123` (Bitte in `docker-compose.yml` ändern! Muss als Hash hinterlegt werden.)
5. **Special Curation & Scheduling verwenden:**
@@ -210,12 +222,12 @@ Hördle kann problemlos als iFrame in andere Webseiten eingebettet werden. Die A
### Genre-spezifische Einbindung
-Einzelne Genres können direkt eingebunden werden:
+Einzelne Genres können direkt eingebunden werden (mit Locale-Präfix):
```html
-
+
-
+