const translations = { en: { "lang.label": "Language", "lang.en": "English", "lang.de": "Deutsch", "auth.loginTab": "Log in", "auth.registerTab": "Register", "auth.username": "Username", "auth.password": "Password", "auth.displayName": "Display name (shows to group)", "auth.adminKey": "Admin key (optional)", "auth.loginSubmit": "Log in", "auth.registerSubmit": "Create account", "auth.logout": "Logout", "auth.welcome": "Welcome, {name}!", "auth.defaultName": "Player", "auth.loading": "Loading…", "auth.needCredentials": "Username and password required", "auth.invalidCredentials": "Invalid username or password", "phase.suggest": "Suggest", "phase.reveal": "Reveal", "phase.vote": "Vote", "phase.results": "Results", "phase.loading": "Loading…", "counts.format": "Players: {players} • Suggestions: {suggestions} • Votes: {votes}", "suggest.title": "Suggest (up to 5)", "suggest.hint": "Only you can see your suggestions until Reveal.", "form.gameName": "Game name *", "form.genre": "Genre", "form.description": "Description", "form.players": "Players", "form.min": "Min", "form.max": "Max", "form.screenshot": "Screenshot URL", "form.youtube": "YouTube URL", "form.gameUrl": "Game website URL", "form.submit": "Submit", "form.placeholder.description": "Short description", "form.placeholder.gameName": "Game name *", "form.placeholder.genre": "Genre", "form.placeholder.screenshot": "Screenshot URL", "form.placeholder.youtube": "YouTube URL", "form.placeholder.gameUrl": "Game website URL", "section.mySuggestions": "Your suggestions", "section.allSuggestions": "All Suggestions", "section.allSuggestions.count": "All {count} suggestions", "section.vote": "Vote 0-10", "section.vote.count": "Vote for all {count} games", "section.results": "Results", "card.edit": "Edit", "card.delete": "Delete", "card.players": "Players: {min}–{max}", "card.site": "Site ↗", "card.youtube": "YouTube ↗", "card.openScreenshot": "Open screenshot", "vote.saved": "Saved vote", "results.rank": "Rank", "results.game": "Game", "results.author": "Author", "results.votes": "Votes", "results.avg": "Avg", "results.total": "Total", "results.links": "Links", "results.link.site": "Site ↗", "results.link.youtube": "YouTube ↗", "admin.title": "Admin", "admin.tools": "Admin tools", "admin.setPhase": "Set phase", "admin.reset": "Reset (keep players)", "admin.factoryReset": "Factory reset", "admin.phaseUpdated": "Phase updated", "admin.resetDone": "Reset complete", "admin.factoryResetDone": "Factory reset complete", "toast.unexpected": "Unexpected error", "toast.registered": "Registered", "toast.loggedIn": "Logged in", "toast.suggestionAdded": "Suggestion added", "toast.suggestionDeleted": "Suggestion deleted", "toast.savedChanges": "Saved changes", "toast.nameRequired": "Name required", "toast.invalidImageUrl": "Screenshot URL must be http(s) and end with an image file.", "modal.editTitle": "Edit game", "modal.save": "Save changes", "modal.cancel": "Cancel", "modal.close": "Close", "lightbox.close": "Close", }, de: { "lang.label": "Sprache", "lang.en": "Englisch", "lang.de": "Deutsch", "auth.loginTab": "Anmelden", "auth.registerTab": "Registrieren", "auth.username": "Benutzername", "auth.password": "Passwort", "auth.displayName": "Anzeigename (für die Gruppe sichtbar)", "auth.adminKey": "Admin-Schlüssel (optional)", "auth.loginSubmit": "Anmelden", "auth.registerSubmit": "Konto erstellen", "auth.logout": "Abmelden", "auth.welcome": "Willkommen, {name}!", "auth.defaultName": "Spieler", "auth.loading": "Lädt…", "auth.needCredentials": "Benutzername und Passwort erforderlich", "auth.invalidCredentials": "Ungültiger Benutzername oder Passwort", "phase.suggest": "Vorschlagen", "phase.reveal": "Enthüllen", "phase.vote": "Bewerten", "phase.results": "Ergebnisse", "phase.loading": "Lädt…", "counts.format": "Spieler: {players} • Vorschläge: {suggestions} • Stimmen: {votes}", "suggest.title": "Vorschlagen (bis zu 5)", "suggest.hint": "Nur du siehst deine Vorschläge bis zur Enthüllung.", "form.gameName": "Spielname *", "form.genre": "Genre", "form.description": "Beschreibung", "form.players": "Spieler", "form.min": "Min", "form.max": "Max", "form.screenshot": "Screenshot-URL", "form.youtube": "YouTube-URL", "form.gameUrl": "Spiel-Webseite", "form.submit": "Absenden", "form.placeholder.description": "Kurze Beschreibung", "form.placeholder.gameName": "Spielname *", "form.placeholder.genre": "Genre", "form.placeholder.screenshot": "Screenshot-URL", "form.placeholder.youtube": "YouTube-URL", "form.placeholder.gameUrl": "Spiel-Webseite", "section.mySuggestions": "Deine Vorschläge", "section.allSuggestions": "Alle Vorschläge", "section.allSuggestions.count": "Alle {count} Vorschläge", "section.vote": "Bewerten 0-10", "section.vote.count": "Bewerte alle {count} Spiele", "section.results": "Ergebnisse", "card.edit": "Bearbeiten", "card.delete": "Löschen", "card.players": "Spieler: {min}–{max}", "card.site": "Website ↗", "card.youtube": "YouTube ↗", "card.openScreenshot": "Screenshot öffnen", "vote.saved": "Stimme gespeichert", "results.rank": "Rang", "results.game": "Spiel", "results.author": "Autor", "results.votes": "Stimmen", "results.avg": "Durchschn.", "results.total": "Gesamt", "results.links": "Links", "results.link.site": "Website ↗", "results.link.youtube": "YouTube ↗", "admin.title": "Admin", "admin.tools": "Admin-Werkzeuge", "admin.setPhase": "Phase setzen", "admin.reset": "Zurücksetzen (Spieler behalten)", "admin.factoryReset": "Werkseinstellung", "admin.phaseUpdated": "Phase aktualisiert", "admin.resetDone": "Zurücksetzen abgeschlossen", "admin.factoryResetDone": "Werkseinstellung abgeschlossen", "toast.unexpected": "Unerwarteter Fehler", "toast.registered": "Registriert", "toast.loggedIn": "Angemeldet", "toast.suggestionAdded": "Vorschlag hinzugefügt", "toast.suggestionDeleted": "Vorschlag gelöscht", "toast.savedChanges": "Änderungen gespeichert", "toast.nameRequired": "Name erforderlich", "toast.invalidImageUrl": "Screenshot-URL muss mit http(s) beginnen und auf eine Bilddatei enden.", "modal.editTitle": "Spiel bearbeiten", "modal.save": "Änderungen speichern", "modal.cancel": "Abbrechen", "modal.close": "Schließen", "lightbox.close": "Schließen", } }; const storageKey = "app_lang"; const defaultLang = "en"; let currentLang = defaultLang; const listeners = []; function interpolate(template, params = {}) { return template.replace(/\{(\w+)\}/g, (_, key) => (params[key] ?? `{${key}}`)); } function t(key, params) { const fallback = translations[defaultLang][key] ?? key; const phrase = translations[currentLang]?.[key] ?? fallback; return interpolate(phrase, params); } function detectLanguage() { const stored = localStorage.getItem(storageKey); if (stored && translations[stored]) return stored; const nav = navigator.language?.slice(0, 2); if (nav && translations[nav]) return nav; return defaultLang; } function applyTranslations(root = document) { root.querySelectorAll("[data-i18n]").forEach((el) => { const key = el.dataset.i18n; const attrs = (el.dataset.i18nAttr || "") .split(",") .map((a) => a.trim()) .filter(Boolean); const text = t(key); if (attrs.length === 0) { el.textContent = text; } else { attrs.forEach((attr) => el.setAttribute(attr, text)); } }); } function notify() { listeners.forEach((fn) => fn(currentLang)); } function setLanguage(lang) { if (!translations[lang]) lang = defaultLang; currentLang = lang; localStorage.setItem(storageKey, lang); document.documentElement.lang = lang; applyTranslations(); notify(); } function getLanguage() { return currentLang; } function initI18n() { currentLang = detectLanguage(); document.documentElement.lang = currentLang; applyTranslations(); notify(); return currentLang; } function onLanguageChange(fn) { listeners.push(fn); } export { t, setLanguage, getLanguage, initI18n, applyTranslations, onLanguageChange, translations };