// @ts-nocheck export const SUPPORTED_LOCALES = [ { code: 'de', label: 'Deutsch', flag: '🇩🇪' }, { code: 'en', label: 'English', flag: '🇬🇧' }, ] as const; export type LocaleCode = typeof SUPPORTED_LOCALES[number]['code']; type NestedMessages = { [key: string]: string | NestedMessages; }; export const DEFAULT_LOCALE: LocaleCode = 'de'; export const messages: Record = { de: { common: { hi: 'Hi', refresh: 'Aktualisieren', pullToRefresh: 'Zum Aktualisieren ziehen', releaseToRefresh: 'Zum Aktualisieren loslassen', refreshing: 'Aktualisiere...', updateAvailable: 'Neue Version verfügbar.', updateAction: 'Aktualisieren', offlineReady: 'Offline bereit.', actions: { close: 'Schließen', back: 'Zurück', loading: 'Lädt...', cancel: 'Abbrechen', }, }, consent: { analytics: { title: 'Hilf uns, die App zu verbessern', body: 'Erlaube anonyme Statistik (Matomo). Du kannst das jederzeit in den Einstellungen ändern.', allow: 'Erlauben', later: 'Nicht jetzt', }, }, navigation: { home: 'Start', tasks: 'Aufgaben', achievements: 'Erfolge', gallery: 'Galerie', share: 'Teilen', }, header: { loading: 'Lade Event...', stats: { online: 'online', tasksSolved: 'Aufgaben gelöst', }, helpGallery: 'Hilfe zu Galerie & Teilen', notifications: { title: 'Updates', unread: '{count} neu', allRead: 'Alles gelesen', tabUnread: 'Nachrichten', tabUploads: 'Uploads', tabAll: 'Alle Updates', emptyStatus: 'Keine Upload-Hinweise oder Wartungen aktiv.', queueLabel: 'Upload-Warteschlange (offline)', pendingLabel: 'Uploads in Prüfung', }, }, appShell: { fab: { title: 'Moment erstellen', }, actions: { upload: { label: 'Foto hochladen / aufnehmen', description: 'Füge einen Moment von deinem Gerät oder der Kamera hinzu.', }, compass: { label: 'Kompass', description: 'Schneller Zugriff auf wichtige Bereiche.', }, task: { label: 'Aufgabe starten', description: 'Wähle eine Mission und halte sie fest.', }, live: { label: 'Live Show', description: 'Sieh den Highlight-Stream in Echtzeit.', }, slideshow: { label: 'Diashow', description: 'Lehn dich zurück und schau dir die Galerie an.', }, share: { label: 'Einladung teilen', description: 'Teile den Event-Link oder QR-Code.', }, achievements: { label: 'Erfolge', description: 'Behalte deine Foto-Serien im Blick.', }, }, compass: { capture: 'Aufnehmen', }, }, liveShowPlayer: { title: 'Live Show', loading: 'Live Show wird geladen...', connection: { live: 'Live', sync: 'Sync', }, controls: { play: 'Weiter', pause: 'Pause', fullscreen: 'Vollbild', exitFullscreen: 'Vollbild verlassen', offline: 'Offline', paused: 'Pausiert', }, empty: { title: 'Noch keine Live-Fotos', description: 'Warte auf die ersten Uploads...', }, error: { title: 'Live Show nicht erreichbar', description: 'Bitte überprüfe den Live-Link.', }, }, eventAccess: { loading: { title: 'Wir prüfen deinen Zugang...', subtitle: 'Einen Moment bitte.', }, error: { invalid_token: { title: 'Zugriffscode ungültig', description: 'Der eingegebene Code konnte nicht verifiziert werden.', ctaLabel: 'Neuen Code anfordern', }, token_revoked: { title: 'Zugriffscode deaktiviert', description: 'Dieser Code wurde zurückgezogen. Bitte fordere einen neuen Code an.', ctaLabel: 'Neuen Code anfordern', }, token_expired: { title: 'Zugriffscode abgelaufen', description: 'Der Code ist nicht mehr gültig. Aktualisiere deinen Code, um fortzufahren.', ctaLabel: 'Code aktualisieren', }, token_rate_limited: { title: 'Zu viele Versuche', description: 'Es gab zu viele Eingaben in kurzer Zeit. Warte kurz und versuche es erneut.', hint: 'Tipp: Eine erneute Eingabe ist in wenigen Minuten möglich.', }, access_rate_limited: { title: 'Zu viele Aufrufe', description: 'Es gab sehr viele Aufrufe in kurzer Zeit. Warte kurz und versuche es erneut.', hint: 'Tipp: Du kannst es gleich noch einmal versuchen.', }, guest_limit_exceeded: { title: 'Gäste-Limit erreicht', description: 'Dieses Event hat sein Gäste-Limit erreicht. Bitte wende dich an die Veranstalter:innen.', }, gallery_expired: { title: 'Galerie nicht mehr verfügbar', description: 'Die Galerie zu diesem Event ist nicht mehr zugänglich.', ctaLabel: 'Neuen Code anfordern', }, event_not_public: { title: 'Event nicht öffentlich', description: 'Dieses Event ist aktuell nicht öffentlich zugänglich.', hint: 'Nimm Kontakt mit den Veranstalter:innen auf, um Zugang zu erhalten.', }, network_error: { title: 'Verbindungsproblem', description: 'Wir konnten keine Verbindung zum Server herstellen. Prüfe deine Internetverbindung und versuche es erneut.', }, server_error: { title: 'Server nicht erreichbar', description: 'Der Server reagiert derzeit nicht. Versuche es später erneut.', }, default: { title: 'Event nicht erreichbar', description: 'Wir konnten dein Event nicht laden. Bitte versuche es erneut.', ctaLabel: 'Zur Code-Eingabe', }, }, }, profileSetup: { loading: 'Lade Event...', error: { default: 'Event nicht gefunden.', backToStart: 'Zurück zur Startseite', }, card: { description: 'Fange den schönsten Moment ein!', }, form: { label: 'Dein Name (z.B. Anna)', placeholder: 'Dein Name', submit: 'Los gehts!', submitting: 'Speichere...', }, }, landing: { pageTitle: 'Willkommen bei der Fotospiel App!', headline: 'Elegante Erinnerungen, live erzählt.', subheadline: 'Hier beginnt euer Fotoabenteuer – gemeinsam, intuitiv und live.', join: { title: 'Event beitreten', description: 'Scanne den QR-Code oder gib den Code manuell ein.', button: 'Event beitreten', buttonLoading: 'Prüfe...', }, scan: { start: 'QR-Code scannen', stop: 'Scanner stoppen', manualDivider: 'Oder manuell eingeben', }, input: { placeholder: 'Event-Code eingeben', }, errors: { eventClosed: 'Event nicht gefunden oder geschlossen.', network: 'Netzwerkfehler. Bitte später erneut versuchen.', camera: 'Kamera-Zugriff fehlgeschlagen. Bitte erlaube den Zugriff und versuche es erneut.', }, }, home: { fallbackGuestName: 'Gast', welcomeLine: 'Willkommen {name}!', swipeHint: 'Wische für mehr Aufgaben', introRotating: { 0: 'Hilf uns, diesen besonderen Tag mit deinen schönsten Momenten festzuhalten.', 1: 'Fang die Stimmung des Events ein und teile sie mit allen Gästen.', 2: 'Deine Sicht zählt: Halte Augenblicke fest, die sonst niemand bemerkt.', 3: 'Erfülle kleine Fotoaufgaben und fülle die gemeinsame Galerie mit Leben.', 4: 'Zeig uns, was du siehst – deine Fotos erzählen die Geschichte dieses Tages.', 5: 'Mach aus Schnappschüssen gemeinsame Erinnerungen in einer großen Event-Galerie.', 6: 'Diese App ist eure Fotozentrale – fotografiere, lade hoch und begeistere die anderen.', 7: 'Sorge dafür, dass kein wichtiger Moment verloren geht – mit deinen Bildern.', 8: 'Lass dich von Fotoaufgaben inspirieren und halte die besonderen Szenen fest.', 9: 'Mach mit beim Fotospiel: Deine Fotos machen dieses Event unvergesslich.', }, hero: { subtitle: 'Willkommen zur Party', title: 'Hey {name}!', description: 'Du bist bereit für "{eventName}". Fang die Highlights des Events ein und teile sie mit allen Gästen.', progress: { some: 'Schon {count} Aufgaben erledigt – weiter so!', none: 'Starte mit deiner ersten Aufgabe – wir zählen auf dich!', }, defaultEventName: 'Dein Event', }, stats: { online: 'Gleichzeitig online', tasksSolved: 'Aufgaben gelöst', lastUpload: 'Letzter Upload', completedTasks: 'Deine erledigten Aufgaben', }, actions: { title: 'Deine Aktionen', subtitle: 'Wähle aus, womit du starten willst', queueButton: 'Uploads in Warteschlange ansehen', items: { tasks: { label: 'Aufgabe ziehen', description: 'Hol dir deine nächste Challenge', }, upload: { label: 'Direkt hochladen', description: 'Teile deine neuesten Fotos', }, gallery: { label: 'Galerie ansehen', description: 'Lass dich von anderen inspirieren', }, }, }, checklist: { title: 'Dein Fortschritt', description: 'Halte dich an diese drei kurzen Schritte für die besten Ergebnisse.', steps: { first: 'Aufgabe auswählen oder starten', second: 'Emotion festhalten und Foto schießen', third: 'Bild hochladen und den Moment teilen', }, }, latestUpload: { none: 'Noch kein Upload', invalid: 'Noch kein Upload', justNow: 'Gerade eben', minutes: 'vor {count} Min', hours: 'vor {count} Std', days: 'vor {count} Tagen', }, }, homeV2: { welcome: { label: 'Willkommen', message: 'Willkommen, {name}!', }, rings: { newUploads: 'Neue Uploads', topMoments: 'Top-Momente', }, promptQuest: { label: 'Prompt-Quest', title: 'Fang das herzlichste Lachen ein', subtitle: 'Sammle Punkte und halte die Galerie lebendig.', ctaStart: 'Prompt starten', ctaBrowse: 'Aufgaben entdecken', }, captureReady: { label: 'Bereit zum Aufnehmen', title: 'Füge ein Foto zur gemeinsamen Galerie hinzu', subtitle: 'Schneller Upload aus Kamera oder Galerie.', cta: 'Foto hochladen', }, stats: { uploadsQueued: 'Uploads in Warteschlange', guestCount: 'Gäste', likesCount: 'Likes', }, galleryPreview: { title: 'Galerie-Vorschau', cta: 'Alle ansehen', }, }, achievements: { page: { title: 'Erfolge', subtitle: 'Behalte deine Highlights, Badges und die aktivsten Gäste im Blick.', loadError: 'Erfolge konnten nicht geladen werden.', retry: 'Erneut versuchen', buttons: { personal: 'Meine Erfolge', event: 'Event Highlights', feed: 'Live Feed', }, }, personal: { greeting: 'Hi {name}!', stats: '{photos} Fotos | {tasks} Aufgaben | {likes} Likes', actions: { upload: 'Neues Foto hochladen', tasks: 'Aufgabe ziehen', }, }, badges: { title: 'Badges', description: 'Dein Fortschritt bei den verfügbaren Erfolgen.', empty: 'Noch keine Badges verfügbar.', }, leaderboard: { description: 'Top 5 Teilnehmer dieses Events', uploadsTitle: 'Top Uploads', uploadsEmpty: 'Noch keine Uploads – sobald Fotos vorhanden sind, erscheinen sie hier.', likesTitle: 'Beliebteste Gäste', likesEmpty: 'Likes fehlen noch – motiviere die Gäste, Fotos zu liken.', guestFallback: 'Gast', item: { photos: '{count} Fotos', likes: '{count} Likes', }, }, timeline: { title: 'Timeline', description: 'Wie das Event im Laufe der Zeit Fahrt aufgenommen hat.', row: '{photos} Fotos | {guests} Gäste', }, feed: { title: 'Live Feed', description: 'Die neuesten Momente aus deinem Event.', empty: 'Noch keine Uploads – starte die Kamera und lege los!', taskLabel: 'Aufgabe: {task}', likesLabel: '{count} Likes', thumbnailAlt: 'Vorschau', }, highlights: { topTitle: 'Publikumsliebling', topDescription: 'Das Foto mit den meisten Likes.', noPreview: 'Kein Vorschau-Bild', likesAmount: '{count} Likes', taskLabel: 'Aufgabe: {task}', trendingTitle: 'Trend-Emotion', trendingDescription: 'Diese Stimmung taucht gerade besonders oft auf.', trendingCount: '{count} Fotos mit dieser Stimmung', }, summary: { topContributor: 'Top-Gast', placeholder: 'Teile weiter, um Highlights freizuschalten.', tasksCompleted: 'Aufgaben erledigt', photosShared: 'Fotos geteilt', likesCollected: 'Likes gesammelt', uniqueGuests: 'Gäste beteiligt', }, }, tasks: { page: { eyebrow: 'Aufgaben-Zentrale', title: 'Deine nächste Aufgabe', subtitle: 'Wähle eine Stimmung aus oder lass dich überraschen.', swipeHint: 'Tipp: Links wischen = neue Aufgabe · Rechts wischen = Inspiration', completedLabel: 'Schon erledigt', ctaStart: "Los geht's!", shuffleCta: 'Was Neues!', shuffleButton: 'Shuffle', inspirationTitle: 'Foto-Inspiration', inspirationLoading: 'lädt…', inspirationEmptyTitle: 'Noch kein Foto zu dieser Aufgabe', inspirationEmptyDescription: 'Sei die/der Erste und lade eins hoch', inspirationMore: 'Mehr', inspirationError: 'Fotos konnten nicht geladen werden', checklist: 'Checkliste', tips: 'Tipps', inspirationTitleSecondary: 'Inspiration', suggestionsEyebrow: 'Mehr Inspiration', suggestionsTitle: 'Spring direkt zur nächsten Aufgabe', noTasksAlert: 'Für dieses Event sind derzeit keine Aufgaben hinterlegt.', emptyTitle: 'Keine passende Aufgabe gefunden', emptyDescriptionWithTasks: 'Für deine aktuelle Stimmung gibt es gerade keine Aufgabe. Wähle eine andere Stimmung oder lade neue Aufgaben.', emptyDescriptionNoTasks: 'Hier sind noch keine Aufgaben hinterlegt. Bitte versuche es später erneut.', reloadButton: 'Aufgaben neu laden', filters: { none: 'Kein Filter', recentFallback: 'Stimmung wählen', showAll: 'Alle zeigen', dialogTitle: 'Alle verfügbaren Stimmungen', empty: 'Für dieses Event stehen noch keine Stimmungen bereit.', countOne: '{count} Aufgabe', countMany: '{count} Aufgaben', }, progressLabel: 'Quest-Fortschritt', progressValue: '{count}/{total} Aufgaben erledigt', }, disabled: { title: 'Aufgaben sind deaktiviert', subtitle: 'Dieses Event ist im Foto-Modus.', }, }, notFound: { title: 'Nicht gefunden', description: 'Die Seite konnte nicht gefunden werden.', }, galleryCountdown: { expiresIn: 'Noch {days} Tage online', expiresToday: 'Letzter Tag!', expired: 'Galerie abgelaufen', description: 'Sichere jetzt deine Lieblingsfotos – die Galerie verschwindet bald.', expiredDescription: 'Nur die Veranstalter:innen können die Galerie jetzt noch verlängern.', ctaUpload: 'Letzte Fotos hochladen', }, galleryPublic: { title: 'Galerie', loading: 'Galerie wird geladen ...', loadingMore: 'Weitere Fotos werden geladen', loadError: 'Die Galerie konnte nicht geladen werden.', loadMore: 'Mehr anzeigen', download: 'Herunterladen', expiredTitle: 'Galerie nicht verfügbar', expiredDescription: 'Die Galerie für dieses Event ist abgelaufen.', emptyTitle: 'Noch keine Fotos', emptyDescription: 'Sobald Fotos freigegeben sind, erscheinen sie hier.', lightboxGuestFallback: 'Gast', }, galleryPage: { title: 'Galerie', subtitle: 'Live-Impressionen deines Events', loadingEvent: 'Event-Info wird geladen...', eventNotFound: 'Event nicht gefunden.', hero: { label: 'Live-Galerie', stats: '{photoCount} Fotos · {likeCount} ❤️ · {guestCount} Gäste online', newPhotos: '{count} neue Fotos ansehen', upload: 'Neues Foto hochladen', eventFallback: 'Event', }, loading: 'Lade…', photo: { justNow: 'Gerade eben', anonymous: 'Gast', alt: 'Foto {id}{suffix}', altTaskSuffix: ' - {task}', likeAria: 'Foto liken', shareAria: 'Foto teilen', }, filters: { latest: 'Neueste', popular: 'Beliebt', mine: 'Meine', photobooth: 'Fotobox', }, badge: { newPhotos: '{count} neue Fotos', }, feed: { title: 'Live-Feed', description: 'Alle paar Sekunden aktualisiert.', newUploads: '{count} neue Uploads sind da.', }, lightbox: { prev: 'Zurück', next: 'Weiter', likes: '{count} Likes', deleteTitle: 'Foto löschen?', deleteDescription: 'Das Foto wird aus der Galerie entfernt und kann nicht wiederhergestellt werden.', deleteConfirm: 'Foto löschen', deleteAria: 'Foto löschen', deleting: 'Wird gelöscht…', deletedToast: 'Foto gelöscht.', deleteFailed: 'Foto konnte nicht gelöscht werden.', }, }, share: { title: 'Geteiltes Foto', defaultEvent: 'Ein besonderer Moment', button: 'Teilen', copyLink: 'Link kopieren', copySuccess: 'Link kopiert!', manualPrompt: 'Link kopieren', openEvent: 'Event öffnen', loading: 'Moment wird geladen...', expiredTitle: 'Link abgelaufen', expiredDescription: 'Dieser Link ist nicht mehr verfügbar.', shareText: 'Schau dir diesen Moment bei Fotospiel an.', error: 'Teilen fehlgeschlagen', whatsapp: 'WhatsApp', imessage: 'Nachrichten', copyError: 'Link konnte nicht kopiert werden.', invite: { title: 'Gäste einladen', description: 'Teile den Event-Link oder zeige den QR-Code.', qrLabel: 'QR anzeigen', qrAlt: 'Event-QR-Code', qrLoading: 'QR wird erstellt...', qrRetry: 'Erneut versuchen', copyLabel: 'Link kopieren', guestsTitle: 'Gäste', guestsSubtitle: 'Teile das Event mit deinen Gästen.', guestsSubtitleEvent: 'Teile {event} mit deinen Gästen.', send: 'Einladung senden', }, chips: { emotion: 'Emotion', task: 'Aufgabe', date: 'Aufgenommen', }, }, uploadQueue: { title: 'Uploads', description: 'Warteschlange mit Fortschritt und erneuten Versuchen.', summary: '{waiting} wartend · {failed} fehlgeschlagen', emptyTitle: 'Keine Uploads in der Warteschlange', emptyDescription: 'Sobald Fotos in der Warteschlange sind, erscheinen sie hier.', progress: '{progress}%', actions: { retryAll: 'Alle erneut versuchen', clearFinished: 'Erledigte löschen', removeFailed: 'Entfernen', }, status: { uploaded: 'Hochgeladen', uploading: 'Wird hochgeladen', failed: 'Fehlgeschlagen', waiting: 'Wartet', retries: ' · {count} Versuche', }, }, uploadV2: { preview: { title: 'Live-Vorschau', subtitle: 'Tippe irgendwo, um zu fokussieren.', cta: 'Jetzt aufnehmen', }, galleryCta: 'Aus Galerie', tools: { grid: 'Raster', flash: 'Blitz', upload: 'Upload', }, toast: { queued: 'Offline – in der Warteschlange gespeichert.', uploaded: 'Upload abgeschlossen.', }, queue: { summary: '{waiting} wartend, {sending} sendend', uploading: 'Upload {name} · {progress}%', button: 'Warteschlange', }, errors: { invalidFile: 'Bitte wähle eine Fotodatei aus.', eventMissing: 'Event nicht gefunden.', }, }, pendingUploads: { title: 'Uploads in Prüfung', subtitle: 'Deine Fotos warten noch auf die Freigabe.', successTitle: 'Upload gespeichert', successBody: 'Dein Foto ist hochgeladen und wartet auf die Freigabe.', emptyTitle: 'Keine wartenden Uploads', emptyBody: 'Wenn du ein Foto hochlädst, erscheint es hier bis zur Freigabe.', cta: 'Weiteres Foto aufnehmen', refresh: 'Aktualisieren', loading: 'Lade Uploads...', error: 'Laden fehlgeschlagen. Bitte versuche es erneut.', card: { pending: 'Wartet auf Freigabe', uploadedAt: 'Hochgeladen {time}', justNow: 'Gerade eben', }, }, lightbox: { taskLabel: 'Aufgabe', loadingTask: 'Lade Aufgabe...', photoAlt: 'Foto {id}{suffix}', photoAltTaskSuffix: ' - {taskTitle}', fallbackTitle: 'Aufgabe {id}', unknownTitle: 'Unbekannte Aufgabe {id}', errors: { notFound: 'Foto nicht gefunden', loadFailed: 'Fehler beim Laden des Fotos', }, }, upload: { cameraTitle: 'Kamera', preparing: 'Aufgabe und Kamera werden vorbereitet ...', loadError: { title: 'Aufgabe konnte nicht geladen werden. Du kannst trotzdem ein Foto machen.', retry: 'Nochmal versuchen', }, primer: { title: 'Bereit für dein Shooting?', body: { part1: 'Lass uns sicherstellen, dass alles sitzt: prüfe das Licht, wisch die Kamera sauber und richte alle Personen im Bild aus.', part2: 'Du kannst zwischen Front- und Rückkamera wechseln und bei Bedarf ein Raster aktivieren.', }, dismiss: 'Verstanden', }, hud: { title: 'Live-Missionen', subtitle: 'Bleib im Flow – Kamera bereithalten.', moodLabel: 'Stimmung: {mood}', moodFallback: 'Freestyle', ctaLabel: 'Inspiration öffnen', cards: { online: 'Gäste online', completed: 'Aufgaben gelöst', lastUpload: 'Letzter Upload', }, progressLabel: 'Story {count}/{target} aktiv', liveGuests: '{count} Gäste live', relative: { now: 'Gerade eben', minutes: 'vor {count} Min', hours: 'vor {count} Std', days: 'vor {count} Tagen', }, }, limitSummary: { title: 'Uploads & Slots', subtitle: 'Dein Event-Paket im Überblick', badgeLabel: 'Aktuell', cards: { photos: { title: 'Fotos insgesamt', remaining: '{remaining} von {limit} frei', unlimited: 'Unlimitierte Foto-Uploads aktiv', }, guests: { title: 'Geräte im Einsatz', remaining: '{remaining} Slots verfügbar', unlimited: 'Unlimitierte Geräte freigeschaltet', }, }, badges: { ok: 'OK', warning: 'Bald voll', limit_reached: 'Limit erreicht', unlimited: 'Unlimitiert', }, }, cameraUnsupported: { title: 'Kamera nicht verfügbar', message: 'Dein Gerät unterstützt keine Kameravorschau im Browser. Du kannst stattdessen Fotos aus deiner Galerie hochladen.', openGallery: 'Foto aus Galerie wählen', }, cameraBlocked: { title: 'Kamera durch Sicherheitsrichtlinie blockiert', message: 'Die Kamera ist durch die Sicherheitsrichtlinie dieser Seite blockiert. Öffne den Event-Link im Browser oder lade ein Foto aus der Galerie hoch.', hint: 'Tipp: Wenn du in einer In-App-Ansicht bist, öffne den Link in Safari/Chrome und lade die Seite neu.', }, cameraDenied: { title: 'Kamera-Zugriff verweigert', explanation: 'Du musst den Zugriff auf die Kamera erlauben, um Fotos aufnehmen zu können.', reopenPrompt: 'Systemdialog erneut öffnen', chooseFile: 'Foto aus Galerie wählen', prompt: 'Wir brauchen Zugriff auf deine Kamera. Erlaube die Anfrage oder wähle alternativ ein Foto aus deiner Galerie.', hint: 'Tipp: Prüfe in den Browser-Einstellungen, ob Kamera-Zugriff erlaubt ist, und lade die Seite neu.', }, cameraError: { title: 'Kamera konnte nicht gestartet werden', explanation: 'Wir konnten keine Verbindung zur Kamera herstellen. Prüfe die Berechtigungen oder starte dein Gerät neu.', tryAgain: 'Nochmals versuchen', hint: 'Tipp: Schließe andere Apps mit Kamerazugriff und versuche es erneut.', }, readyOverlay: { title: 'Kamera bereit', message: 'Wenn alle im Bild sind, kannst du den Countdown starten oder ein vorhandenes Foto wählen.', start: 'Countdown starten', chooseFile: 'Foto auswählen', }, taskInfo: { countdown: 'Countdown', emotion: 'Stimmung: {value}', instructionsPrefix: 'Hinweis', difficulty: { easy: 'Leicht', medium: 'Medium', hard: 'Herausfordernd', }, timeEstimate: '{count} Min', fallbackTitle: 'Aufgabe {id}', fallbackDescription: 'Halte den Moment fest und teile ihn mit allen Gästen.', fallbackInstructions: 'Positioniere alle im Bild, starte den Countdown und lass die Emotion wirken.', badge: 'Aufgabe #{id}', }, countdown: { ready: 'Bereit machen ...', }, review: { retake: 'Nochmal aufnehmen', retakeGallery: 'Ein anderes Foto auswählen', keep: 'Foto verwenden', readyAnnouncement: 'Foto aufgenommen. Bitte Vorschau prüfen.', }, liveShow: { title: 'Live-Show', description: 'Zeige dieses Foto direkt auf der Live-Show.', toggle: 'Live-Show aktivieren', immediate: 'Erscheint sofort auf der Leinwand.', reviewed: 'Wird nach Freigabe für die Live-Show angezeigt.', }, status: { saving: 'Speichere Foto...', processing: 'Verarbeite Foto...', uploading: 'Foto wird hochgeladen...', preparing: 'Foto wird vorbereitet...', optimizing: 'Foto wird optimiert...', completed: 'Upload abgeschlossen.', failed: 'Upload fehlgeschlagen. Bitte versuche es erneut.', }, demoReadOnly: 'Uploads sind in der Demo deaktiviert.', optimizedNotice: 'Wir haben dein Foto verkleinert, damit der Upload schneller klappt. Eingespart: {saved}', optimizedFallback: 'Optimierung nicht möglich – wir laden das Original hoch.', retrying: 'Verbindung holperig – neuer Versuch ({attempt}).', controls: { toggleGrid: 'Raster umschalten', toggleCountdown: 'Countdown umschalten', toggleMirror: 'Spiegelung für Frontkamera umschalten', toggleFlash: 'Blitzpräferenz umschalten', capture: 'Foto aufnehmen', switchCamera: 'Kamera wechseln', chooseFile: 'Foto auswählen', }, limitReached: 'Upload-Limit erreicht ({used} / {max} Fotos). Bitte kontaktiere die Veranstalter für ein Upgrade.', limitUnlimited: 'unbegrenzt', limitWarning: 'Nur noch {remaining} von {max} Fotos möglich. Bitte kontaktiere die Veranstalter für ein Upgrade.', galleryWarningDay: 'Galerie läuft in {days} Tag ab. Teile deine Fotos rechtzeitig!', galleryWarningDays: 'Galerie läuft in {days} Tagen ab. Teile deine Fotos rechtzeitig!', packageStatus: { title: 'Dein Paketstatus', subtitle: 'Behalte deine Kontingente im Blick, bevor es eng wird.', badges: { ok: 'Bereit', warning: 'Hinweis', limit_reached: 'Limit erreicht', unlimited: 'Unbegrenzt', }, cards: { photos: { title: 'Fotos', remaining: 'Nur noch {remaining} von {limit} Fotos möglich', unlimited: 'Unbegrenzte Foto-Uploads inklusive', }, guests: { title: 'Gäste', remaining: '{remaining} Gäste frei (max. {limit})', unlimited: 'Beliebig viele Gäste erlaubt', }, }, }, dialogs: { close: 'Verstanden', photoLimit: { title: 'Upload-Limit erreicht', description: 'Es wurden {used} von {limit} Fotos hochgeladen. Bitte kontaktiere das Team für ein Upgrade.', hint: 'Tipp: Größere Pakete schalten sofort wieder Uploads frei.', }, deviceLimit: { title: 'Gerätelimit erreicht', description: 'Dieses Gerät hat sein Upload-Kontingent ausgeschöpft.', hint: 'Nutze ein anderes Gerät oder sprich die Veranstalter:innen an.', }, packageMissing: { title: 'Event pausiert Uploads', description: 'Für dieses Event sind aktuell keine Uploads freigeschaltet.', hint: 'Bitte kontaktiere die Veranstalter:innen für weitere Informationen.', }, galleryExpired: { title: 'Galerie abgelaufen', description: 'Die Galerie ist geschlossen – neue Uploads sind nicht mehr möglich.', hint: 'Nur die Veranstalter:innen können die Galerie verlängern.', }, csrf: { title: 'Sitzung abgelaufen', description: 'Bitte lade die Seite neu und versuche den Upload anschließend erneut.', hint: 'Aktualisiere die Seite, um eine neue Sitzung zu starten.', }, generic: { title: 'Upload fehlgeschlagen', description: 'Der Upload konnte nicht abgeschlossen werden. Bitte versuche es später erneut.', hint: 'Bleibt das Problem bestehen, kontaktiere die Veranstalter:innen.', }, }, errors: { photoLimit: 'Upload-Limit erreicht. Bitte kontaktiere die Veranstalter für ein Upgrade.', deviceLimit: 'Dieses Gerät hat das Upload-Limit erreicht. Bitte wende dich an die Veranstalter.', packageMissing: 'Dieses Event akzeptiert derzeit keine Uploads.', galleryExpired: 'Die Galerie ist abgelaufen. Uploads sind nicht mehr möglich.', generic: 'Upload fehlgeschlagen. Bitte versuche es erneut.', tooLargeHint: 'Das Foto war zu groß. Bitte erneut versuchen – wir verkleinern es automatisch.', }, cameraInactive: 'Kamera ist nicht aktiv. {hint}', cameraInactiveHint: 'Tippe auf "{label}", um loszulegen.', captureError: 'Foto konnte nicht erstellt werden.', feedError: 'Kamera liefert kein Bild. Bitte starte die Kamera neu.', canvasError: 'Canvas konnte nicht initialisiert werden.', limitCheckError: 'Fehler beim Prüfen des Upload-Limits. Upload deaktiviert.', galleryPickError: 'Auswahl fehlgeschlagen. Bitte versuche es erneut.', captureButton: 'Foto aufnehmen', galleryButton: 'Foto aus Galerie wählen', switchCamera: 'Kamera wechseln', countdownLabel: 'Countdown: {seconds}s', countdownReady: 'Bereit machen ...', buttons: { startCamera: 'Kamera starten', tryAgain: 'Erneut versuchen', recheckCamera: 'Zugriff erneut prüfen', }, }, settings: { title: 'Einstellungen', subtitle: 'Verwalte deinen Gastzugang, rechtliche Dokumente und lokale Daten.', language: { title: 'Sprache', option: { de: 'Deutsch', en: 'English', }, }, name: { title: 'Dein Name', description: 'Passe an, wie wir dich im Event begrüßen. Der Name wird nur lokal gespeichert.', label: 'Anzeigename', placeholder: 'z.B. Anna', save: 'Name speichern', saving: 'Speichere...', reset: 'Zurücksetzen', saved: 'Gespeichert (ok)', loading: 'Lade gespeicherten Namen...', }, haptics: { title: 'Haptisches Feedback', description: 'Kurze Vibrationen bei Likes, Uploads und Aktualisierungen.', label: 'Vibrationen aktivieren', unsupported: 'Auf diesem Gerät nicht verfügbar.', }, analytics: { title: 'Anonyme Statistik', description: 'Hilft uns, die Guest-App zu verbessern (Matomo, ohne Tracking-Cookies).', label: 'Statistik zulassen', note: 'Du kannst deine Entscheidung jederzeit hier ändern.', }, legal: { title: 'Rechtliches', description: 'Die rechtlich verbindlichen Texte sind jederzeit hier abrufbar.', loading: 'Dokument wird geladen...', error: 'Das Dokument konnte nicht geladen werden. Bitte versuche es später erneut.', fallbackTitle: 'Rechtlicher Hinweis', section: { impressum: 'Impressum', privacy: 'Datenschutz', terms: 'AGB', }, }, cache: { title: 'Offline-Cache', description: 'Lösche lokale Daten, falls Inhalte veraltet erscheinen oder Uploads hängen bleiben.', clear: 'Cache leeren', clearing: 'Leere Cache...', cleared: 'Cache gelöscht.', note: 'Dies betrifft nur diesen Browser. Wartende Uploads können verloren gehen.', }, help: { title: 'Hilfecenter', description: 'Öffne das Hilfecenter mit Schritt-für-Schritt-Anleitungen.', cta: 'Hilfecenter öffnen', }, footer: { notice: 'Gastbereich - Daten werden lokal im Browser gespeichert.', }, sheet: { openLabel: 'Einstellungen öffnen', backLabel: 'Zurück', legalDescription: 'Rechtlicher Hinweis', }, }, help: { center: { title: 'Hilfecenter', subtitle: 'Antworten für Gäste – nach dem ersten Laden auch offline verfügbar.', searchPlaceholder: 'Suche nach Thema oder Stichwort', offlineBadge: 'Offline-Version', offlineDescription: 'Du siehst eine zwischengespeicherte Version. Geh online für aktuelle Inhalte.', empty: 'Keine Artikel gefunden.', error: 'Hilfe konnte nicht geladen werden.', retry: 'Erneut versuchen', listTitle: 'Alle Artikel', }, article: { back: 'Zurück zur Übersicht', updated: 'Aktualisiert am {date}', relatedTitle: 'Verwandte Artikel', loadingTitle: 'Artikel wird geladen', loadingDescription: 'Wir holen die neuesten Infos für dich.', unavailable: 'Dieser Artikel ist nicht verfügbar.', reload: 'Neu laden', }, }, }, en: { common: { hi: 'Hi', refresh: 'Refresh', pullToRefresh: 'Pull to refresh', releaseToRefresh: 'Release to refresh', refreshing: 'Refreshing...', updateAvailable: 'A new version is available.', updateAction: 'Update', offlineReady: 'Offline ready.', actions: { close: 'Close', back: 'Back', loading: 'Loading...', cancel: 'Cancel', }, }, consent: { analytics: { title: 'Help us improve the app', body: 'Allow anonymous analytics (Matomo). You can change this anytime in settings.', allow: 'Allow', later: 'Not now', }, }, navigation: { home: 'Home', tasks: 'Tasks', achievements: 'Achievements', gallery: 'Gallery', share: 'Share', }, header: { loading: 'Loading event...', stats: { online: 'online', tasksSolved: 'tasks solved', }, helpGallery: 'Help: Gallery & sharing', notifications: { title: 'Updates', unread: '{count} new', allRead: 'All read', tabUnread: 'Messages', tabUploads: 'Uploads', tabAll: 'All updates', emptyStatus: 'No upload status or maintenance active.', queueLabel: 'Upload queue (offline)', pendingLabel: 'Pending uploads', }, }, appShell: { fab: { title: 'Create a moment', }, actions: { upload: { label: 'Upload / Take photo', description: 'Add a moment from your device or camera.', }, compass: { label: 'Compass hub', description: 'Quick jump to key areas.', }, task: { label: 'Start a task', description: 'Pick a challenge and capture it now.', }, live: { label: 'Live show', description: 'See the real-time highlight stream.', }, slideshow: { label: 'Slideshow', description: 'Lean back and watch the gallery roll.', }, share: { label: 'Share invite', description: 'Send the event link or QR code.', }, achievements: { label: 'Achievements', description: 'Track your photo streaks.', }, }, compass: { capture: 'Capture', }, }, liveShowPlayer: { title: 'Live Show', loading: 'Loading Live Show...', connection: { live: 'Live', sync: 'Sync', }, controls: { play: 'Play', pause: 'Pause', fullscreen: 'Fullscreen', exitFullscreen: 'Exit fullscreen', offline: 'Offline', paused: 'Paused', }, empty: { title: 'No live photos yet', description: 'Waiting for the first uploads...', }, error: { title: 'Live Show unavailable', description: 'Please check the live link.', }, }, eventAccess: { loading: { title: 'Checking your access...', subtitle: 'Hang tight for a moment.', }, error: { invalid_token: { title: 'Access code invalid', description: 'We could not verify the code you entered.', ctaLabel: 'Request new code', }, token_revoked: { title: 'Access code revoked', description: 'This code was revoked. Please request a new one.', ctaLabel: 'Request new code', }, token_expired: { title: 'Access code expired', description: 'The code is no longer valid. Refresh your code to continue.', ctaLabel: 'Refresh code', }, token_rate_limited: { title: 'Too many attempts', description: 'There were too many attempts in a short time. Wait a bit and try again.', hint: 'Tip: You can retry in a few minutes.', }, access_rate_limited: { title: 'Too many requests', description: 'There were too many requests in a short time. Please wait a moment and try again.', hint: 'Tip: You can retry shortly.', }, guest_limit_exceeded: { title: 'Guest limit reached', description: 'This event has reached its guest allowance. Please contact the organisers.', }, gallery_expired: { title: 'Gallery unavailable', description: 'The gallery for this event is no longer accessible.', ctaLabel: 'Request new code', }, event_not_public: { title: 'Event not public', description: 'This event is not publicly accessible right now.', hint: 'Contact the organizers to get access.', }, network_error: { title: 'Connection issue', description: 'We could not reach the server. Check your connection and try again.', }, server_error: { title: 'Server unavailable', description: 'The server is currently unavailable. Please try again later.', }, default: { title: 'Event unavailable', description: 'We could not load your event. Please try again.', ctaLabel: 'Back to code entry', }, }, }, profileSetup: { loading: 'Loading event...', error: { default: 'Event not found.', backToStart: 'Back to start', }, card: { description: 'Capture the best moment!', }, form: { label: 'Your name (e.g. Anna)', placeholder: 'Your name', submit: "Let's go!", submitting: 'Saving...', }, }, landing: { pageTitle: 'Welcome to the Fotospiel App!', headline: 'An elegant way to tell memories live.', subheadline: 'Start your collaborative photo story—intuitive, fast, live.', join: { title: 'Join the event', description: 'Scan the QR code or enter the code manually.', button: 'Join event', buttonLoading: 'Checking...', }, scan: { start: 'Scan QR code', stop: 'Stop scanner', manualDivider: 'Or enter it manually', }, input: { placeholder: 'Enter event code', }, errors: { eventClosed: 'Event not found or closed.', network: 'Network error. Please try again later.', camera: 'Camera access failed. Allow access and try again.', }, }, home: { fallbackGuestName: 'Guest', welcomeLine: 'Welcome {name}!', swipeHint: 'Swipe for more missions', introRotating: { 0: 'Help us capture this special day with your favourite moments.', 1: 'Capture the mood of the event and share it with everyone.', 2: 'Your view matters: save the moments that others might miss.', 3: 'Complete playful photo missions and fill the shared gallery with life.', 4: 'Show us what you see – your photos tell the story of this day.', 5: 'Turn quick snapshots into shared memories in one big event gallery.', 6: 'This app is your photo hub – shoot, upload, and delight the other guests.', 7: 'Make sure no important moment gets lost – with your pictures.', 8: 'Let photo missions inspire you and capture the scenes that really matter.', 9: 'Join the photo game: your pictures make this event unforgettable.', }, hero: { subtitle: 'Welcome to the party', title: 'Hey {name}!', description: 'You are ready for "{eventName}". Capture the highlights and share them with everyone.', progress: { some: 'Already {count} tasks done - keep going!', none: 'Start with your first task - we are counting on you!', }, defaultEventName: 'Your event', }, stats: { online: 'Guests online', tasksSolved: 'Tasks completed', lastUpload: 'Latest upload', completedTasks: 'Your completed tasks', }, actions: { title: 'Your actions', subtitle: 'Choose how you want to start', queueButton: 'View uploads in queue', items: { tasks: { label: 'Draw a task', description: 'Grab your next challenge', }, upload: { label: 'Upload directly', description: 'Share your latest photos', }, gallery: { label: 'Browse gallery', description: 'Get inspired by others', }, }, }, checklist: { title: 'Your progress', description: 'Follow these three quick steps for the best results.', steps: { first: 'Pick or start a task', second: 'Capture the emotion and take the photo', third: 'Upload the picture and share the moment with everyone', }, }, latestUpload: { none: 'No upload yet', invalid: 'No upload yet', justNow: 'Just now', minutes: '{count} min ago', hours: '{count} h ago', days: '{count} days ago', }, }, homeV2: { welcome: { label: 'Welcome', message: 'Welcome, {name}!', }, rings: { newUploads: 'New uploads', topMoments: 'Top moments', }, promptQuest: { label: 'Prompt quest', title: 'Capture the happiest laugh', subtitle: 'Earn points and keep the gallery lively.', ctaStart: 'Start prompt', ctaBrowse: 'Browse tasks', }, captureReady: { label: 'Capture ready', title: 'Add a photo to the shared gallery', subtitle: 'Quick add from your camera or device.', cta: 'Upload / Take photo', }, stats: { uploadsQueued: 'Uploads queued', guestCount: 'Guests', likesCount: 'Likes', }, galleryPreview: { title: 'Gallery preview', cta: 'View all', }, }, achievements: { page: { title: 'Achievements', subtitle: 'Keep an eye on highlights, badges, and the most active guests.', loadError: 'Achievements could not be loaded.', retry: 'Try again', buttons: { personal: 'My achievements', event: 'Event highlights', feed: 'Live feed', }, }, personal: { greeting: 'Hi {name}!', stats: '{photos} photos | {tasks} tasks | {likes} likes', actions: { upload: 'Upload photo', tasks: 'Draw a task', }, }, badges: { title: 'Badges', description: 'Your progress across available achievements.', empty: 'No badges unlocked yet.', }, leaderboard: { description: 'Top 5 participants of this event', uploadsTitle: 'Top uploads', uploadsEmpty: 'No uploads yet – once photos arrive you will see them here.', likesTitle: 'Most liked guests', likesEmpty: 'No likes yet – motivate guests to like photos.', guestFallback: 'Guest', item: { photos: '{count} photos', likes: '{count} likes', }, }, timeline: { title: 'Timeline', description: 'How the event gained momentum throughout the day.', row: '{photos} photos | {guests} guests', }, feed: { title: 'Live feed', description: 'The freshest moments from your event.', empty: 'No uploads yet – grab your camera and start!', taskLabel: 'Task: {task}', likesLabel: '{count} likes', thumbnailAlt: 'Preview', }, highlights: { topTitle: 'Audience favorite', topDescription: 'The photo with the most likes.', noPreview: 'No preview image', likesAmount: '{count} likes', taskLabel: 'Task: {task}', trendingTitle: 'Trending emotion', trendingDescription: 'This mood appears most often right now.', trendingCount: '{count} photos in this mood', }, summary: { topContributor: 'Top guest', placeholder: 'Keep sharing to unlock highlights.', tasksCompleted: 'Tasks completed', photosShared: 'Photos shared', likesCollected: 'Likes collected', uniqueGuests: 'Guests involved', }, }, tasks: { page: { eyebrow: 'Mission hub', title: 'Your next task', subtitle: 'Pick a mood or stay spontaneous.', swipeHint: 'Tip: Swipe left for a new task · right for inspiration', completedLabel: 'Already done', ctaStart: "Let's go!", shuffleCta: 'Something new!', shuffleButton: 'Shuffle', inspirationTitle: 'Photo inspiration', inspirationLoading: 'loading…', inspirationEmptyTitle: 'No photo for this task yet', inspirationEmptyDescription: 'Be the first one to upload!', inspirationMore: 'More', inspirationError: 'Photos could not be loaded', checklist: 'Checklist', tips: 'Tips', inspirationTitleSecondary: 'Inspiration', suggestionsEyebrow: 'More inspiration', suggestionsTitle: 'Jump straight to the next task', noTasksAlert: 'No tasks available for this event yet.', emptyTitle: 'No matching task found', emptyDescriptionWithTasks: 'No task matches this mood right now. Pick another mood or load new tasks.', emptyDescriptionNoTasks: 'No tasks are available yet. Please try again later.', reloadButton: 'Reload tasks', filters: { none: 'No filter', recentFallback: 'Select mood', showAll: 'Show all', dialogTitle: 'All available moods', empty: 'No moods are available for this event yet.', countOne: '{count} task', countMany: '{count} tasks', }, progressLabel: 'Quest progress', progressValue: '{count}/{total} tasks completed', }, disabled: { title: 'Tasks are disabled', subtitle: 'This event is set to photo-only mode.', }, }, notFound: { title: 'Not found', description: 'We could not find the page you requested.', }, galleryCountdown: { expiresIn: '{days} days remaining', expiresToday: 'Final day!', expired: 'Gallery expired', description: 'Save your favourite photos before the gallery goes offline.', expiredDescription: 'Only the organizers can extend the gallery now.', ctaUpload: 'Share last photos', }, galleryPublic: { title: 'Gallery', loading: 'Loading gallery ...', loadingMore: 'Loading more photos', loadError: 'The gallery could not be loaded.', loadMore: 'Show more', download: 'Download', expiredTitle: 'Gallery unavailable', expiredDescription: 'The gallery for this event has expired.', emptyTitle: 'No photos yet', emptyDescription: 'Once photos are approved they will appear here.', lightboxGuestFallback: 'Guest', }, galleryPage: { title: 'Gallery', subtitle: 'Live impressions from your event', loadingEvent: 'Loading event info…', eventNotFound: 'Event not found.', hero: { label: 'Live gallery', stats: '{photoCount} photos · {likeCount} ❤️ · {guestCount} guests online', newPhotos: 'View {count} new photos', upload: 'Upload new photo', eventFallback: 'Event', }, loading: 'Loading…', photo: { justNow: 'Just now', anonymous: 'Guest', alt: 'Photo {id}{suffix}', altTaskSuffix: ' - {task}', likeAria: 'Like photo', shareAria: 'Share photo', }, filters: { latest: 'Newest', popular: 'Popular', mine: 'My photos', photobooth: 'Photo booth', }, badge: { newPhotos: '{count} new photos', }, feed: { title: 'Live feed', description: 'Updated every few seconds.', newUploads: '{count} new uploads just landed.', }, lightbox: { prev: 'Prev', next: 'Next', likes: '{count} likes', deleteTitle: 'Delete this photo?', deleteDescription: 'This photo will be removed from the gallery and cannot be restored.', deleteConfirm: 'Delete photo', deleteAria: 'Delete photo', deleting: 'Deleting…', deletedToast: 'Photo deleted.', deleteFailed: 'Photo could not be deleted.', }, }, share: { title: 'Shared photo', defaultEvent: 'A special moment', button: 'Share', copyLink: 'Copy link', copyError: 'Link could not be copied.', manualPrompt: 'Copy link', openEvent: 'Open event', loading: 'Loading moment...', expiredTitle: 'Link expired', expiredDescription: 'This link is no longer available.', shareText: 'Check out this moment on Fotospiel.', error: 'Share failed', invite: { title: 'Invite guests', description: 'Share the event link or show the QR code to join.', qrLabel: 'Show QR', qrAlt: 'Event QR code', qrLoading: 'Generating QR...', qrRetry: 'Retry', copyLabel: 'Copy link', guestsTitle: 'Guests', guestsSubtitle: 'Share the event with your guests.', guestsSubtitleEvent: 'Share {event} with your guests.', send: 'Send invite', }, chips: { emotion: 'Emotion', task: 'Task', date: 'Captured', }, }, uploadQueue: { title: 'Uploads', description: 'Queue with progress and retries.', summary: '{waiting} waiting · {failed} failed', emptyTitle: 'No queued uploads', emptyDescription: 'Once photos are queued, they will appear here.', progress: '{progress}%', actions: { retryAll: 'Retry all', clearFinished: 'Clear finished', removeFailed: 'Remove', }, status: { uploaded: 'Uploaded', uploading: 'Uploading', failed: 'Failed', waiting: 'Waiting', retries: ' · {count} retries', }, }, uploadV2: { preview: { title: 'Live preview', subtitle: 'Tap anywhere to focus.', cta: 'Capture now', }, galleryCta: 'From gallery', tools: { grid: 'Grid', flash: 'Flash', upload: 'Upload', }, toast: { queued: 'Offline — added to upload queue.', uploaded: 'Upload complete.', }, queue: { summary: '{waiting} waiting, {sending} sending', uploading: 'Uploading {name} · {progress}%', button: 'Queue', }, errors: { invalidFile: 'Please choose a photo file.', eventMissing: 'Event not found', }, }, pendingUploads: { title: 'Pending uploads', subtitle: 'Your photos are waiting for approval.', successTitle: 'Upload saved', successBody: 'Your photo is uploaded and waiting for approval.', emptyTitle: 'No pending uploads', emptyBody: 'Once you upload a photo, it will appear here until it is approved.', cta: 'Take another photo', refresh: 'Refresh', loading: 'Loading uploads...', error: 'Failed to load uploads. Please try again.', card: { pending: 'Waiting for approval', uploadedAt: 'Uploaded {time}', justNow: 'Just now', }, }, lightbox: { taskLabel: 'Task', loadingTask: 'Loading task...', photoAlt: 'Photo {id}{suffix}', photoAltTaskSuffix: ' - {taskTitle}', fallbackTitle: 'Task {id}', unknownTitle: 'Unknown task {id}', errors: { notFound: 'Photo not found', loadFailed: 'Failed to load photo', }, }, upload: { cameraTitle: 'Camera', preparing: 'Preparing task and camera ...', loadError: { title: 'Task could not be loaded. You can still take a photo.', retry: 'Try again', }, primer: { title: 'Ready for your shoot?', body: { part1: 'Make sure everything is set: check the lighting, clean the lens, and line everyone up in the frame.', part2: 'You can switch between front and back camera and enable the grid if needed.', }, dismiss: 'Got it', }, hud: { title: 'Live missions', subtitle: 'Stay in the flow – keep the camera ready.', moodLabel: 'Mood: {mood}', moodFallback: 'Freestyle', ctaLabel: 'Open inspiration', cards: { online: 'Guests online', completed: 'Tasks completed', lastUpload: 'Latest upload', }, progressLabel: 'Story {count}/{target} active', liveGuests: '{count} guests live', relative: { now: 'Just now', minutes: '{count} min ago', hours: '{count} h ago', days: '{count} days ago', }, }, limitSummary: { title: 'Uploads & slots', subtitle: 'Your event package overview', badgeLabel: 'Current', cards: { photos: { title: 'Photos total', remaining: '{remaining} of {limit} free', unlimited: 'Unlimited photo uploads', }, guests: { title: 'Devices in use', remaining: '{remaining} slots available', unlimited: 'Unlimited devices enabled', }, }, badges: { ok: 'OK', warning: 'Almost full', limit_reached: 'Limit reached', unlimited: 'Unlimited', }, }, cameraUnsupported: { title: 'Camera not available', message: 'Your device does not support live camera preview in this browser. You can upload photos from your gallery instead.', openGallery: 'Choose photo from gallery', }, cameraBlocked: { title: 'Camera blocked by security policy', message: 'Camera access is blocked by the site security policy. Open the event link in your browser or upload a photo from your gallery.', hint: 'Tip: If you are in an in-app browser, open the link in Safari/Chrome and reload the page.', }, cameraDenied: { title: 'Camera access denied', explanation: 'Allow camera access to capture photos.', reopenPrompt: 'Open system dialog again', chooseFile: 'Choose photo from gallery', prompt: 'We need access to your camera. Allow the request or pick a photo from your gallery.', hint: 'Tip: Check your browser settings for camera permissions and reload the page.', }, cameraError: { title: 'Camera could not be started', explanation: 'We could not connect to the camera. Check permissions or restart your device.', tryAgain: 'Try again', hint: 'Tip: Close other apps that might be using the camera and try again.', }, readyOverlay: { title: 'Camera ready', message: 'Once everyone is in frame, start the countdown or pick an existing photo.', start: 'Start countdown', chooseFile: 'Choose photo', }, taskInfo: { countdown: 'Countdown', emotion: 'Emotion: {value}', instructionsPrefix: 'Hint', difficulty: { easy: 'Easy', medium: 'Medium', hard: 'Challenging', }, timeEstimate: '{count} min', fallbackTitle: 'Task {id}', fallbackDescription: 'Capture the moment and share it with everyone.', fallbackInstructions: 'Line everyone up, start the countdown, and let the emotion shine.', badge: 'Task #{id}', }, countdown: { ready: 'Get ready ...', }, review: { retake: 'Retake photo', retakeGallery: 'Choose another photo', keep: 'Use this photo', readyAnnouncement: 'Photo captured. Please review the preview.', }, liveShow: { title: 'Live Show', description: 'Show this photo on the live screen.', toggle: 'Enable Live Show', immediate: 'Shows immediately on the big screen.', reviewed: 'Shown after approval for the Live Show.', }, status: { saving: 'Saving photo...', processing: 'Processing photo...', uploading: 'Uploading photo...', preparing: 'Preparing photo...', optimizing: 'Optimizing photo...', completed: 'Upload complete.', failed: 'Upload failed. Please try again.', }, demoReadOnly: 'Uploads are disabled in demo mode.', optimizedNotice: 'We optimized your photo to speed up the upload. Saved: {saved}', optimizedFallback: 'Could not optimize – uploading the original.', retrying: 'Connection unstable – retrying ({attempt}).', controls: { toggleGrid: 'Toggle grid', toggleCountdown: 'Toggle countdown', toggleMirror: 'Toggle mirroring for front camera', toggleFlash: 'Toggle flash', capture: 'Capture photo', switchCamera: 'Switch camera', chooseFile: 'Choose photo', }, limitReached: 'Upload limit reached ({used} / {max} photos). Contact the organizers for an upgrade.', limitUnlimited: 'unlimited', limitWarning: 'Only {remaining} of {max} photos left. Please contact the organizers for an upgrade.', galleryWarningDay: 'Gallery expires in {days} day. Upload your photos soon!', galleryWarningDays: 'Gallery expires in {days} days. Upload your photos soon!', packageStatus: { title: 'Your package status', subtitle: 'Keep an eye on your remaining allowances.', badges: { ok: 'Ready', warning: 'Heads-up', limit_reached: 'Limit reached', unlimited: 'Unlimited', }, cards: { photos: { title: 'Photos', remaining: '{remaining} of {limit} photo slots left', unlimited: 'Unlimited photo uploads included', }, guests: { title: 'Guests', remaining: '{remaining} guest slots free (max {limit})', unlimited: 'Unlimited guests allowed', }, }, }, dialogs: { close: 'Got it', photoLimit: { title: 'Upload limit reached', description: '{used} of {limit} photos are already uploaded. Please reach out to upgrade your package.', hint: 'Tip: Upgrading the package re-enables uploads instantly.', }, deviceLimit: { title: 'Device limit reached', description: 'This device has used all available upload slots.', hint: 'Try a different device or contact the organizers.', }, packageMissing: { title: 'Uploads paused', description: 'This event is currently not accepting new uploads.', hint: 'Check in with the organizers for the latest status.', }, galleryExpired: { title: 'Gallery closed', description: 'The gallery has expired and no new uploads can be added.', hint: 'Only the organizers can extend the gallery window.', }, csrf: { title: 'Session expired', description: 'Refresh the page and try the upload again.', hint: 'Reload the page to start a new session.', }, generic: { title: 'Upload failed', description: 'We could not complete the upload. Please try again later.', hint: 'If it keeps happening, reach out to the organizers.', }, }, errors: { photoLimit: 'Upload limit reached. Contact the organizers for an upgrade.', deviceLimit: 'This device reached its upload limit. Please contact the organizers.', packageMissing: 'This event is not accepting uploads right now.', galleryExpired: 'The gallery has expired. Uploads are no longer possible.', generic: 'Upload failed. Please try again.', tooLargeHint: 'The photo was too large. Please try again — we compress it automatically.', }, cameraInactive: 'Camera is not active. {hint}', cameraInactiveHint: 'Tap "{label}" to get started.', captureError: 'Photo could not be created.', feedError: 'Camera feed not available. Please restart the camera.', canvasError: 'Canvas could not be initialised.', limitCheckError: 'Failed to check upload limits. Upload disabled.', galleryPickError: 'Selection failed. Please try again.', captureButton: 'Capture photo', galleryButton: 'Choose from gallery', switchCamera: 'Switch camera', countdownLabel: 'Countdown: {seconds}s', countdownReady: 'Get ready ...', buttons: { startCamera: 'Start camera', tryAgain: 'Try again', recheckCamera: 'Recheck access', }, }, settings: { title: 'Settings', subtitle: 'Manage your guest access, legal documents, and local data.', language: { title: 'Language', option: { de: 'German', en: 'English', }, }, name: { title: 'Your name', description: 'Update how we greet you inside the event. The name is stored locally only.', label: 'Display name', placeholder: 'e.g. Anna', save: 'Save name', saving: 'Saving...', reset: 'Reset', saved: 'Saved', loading: 'Loading saved name...', }, haptics: { title: 'Haptic feedback', description: 'Short vibrations for likes, uploads, and refreshes.', label: 'Enable vibrations', unsupported: 'Not available on this device.', }, analytics: { title: 'Anonymous analytics', description: 'Helps us improve the Guest app (Matomo, no tracking cookies).', label: 'Allow analytics', note: 'You can change this anytime here.', }, legal: { title: 'Legal', description: 'The legally binding documents are always available here.', loading: 'Loading document...', error: 'The document could not be loaded. Please try again later.', fallbackTitle: 'Legal notice', section: { impressum: 'Imprint', privacy: 'Privacy', terms: 'Terms', }, }, cache: { title: 'Offline cache', description: 'Clear local data if content looks outdated or uploads get stuck.', clear: 'Clear cache', clearing: 'Clearing cache...', cleared: 'Cache cleared.', note: 'This only affects this browser. Pending uploads may be lost.', }, help: { title: 'Help Center', description: 'Open the help center for guides and quick answers.', cta: 'Open help center', }, footer: { notice: 'Guest area - data is stored locally in the browser.', }, sheet: { openLabel: 'Open settings', backLabel: 'Back', legalDescription: 'Legal notice', }, }, help: { center: { title: 'Help Center', subtitle: 'Guides for guests – available offline after the first sync.', searchPlaceholder: 'Search by topic or keyword', offlineBadge: 'Offline copy', offlineDescription: 'You are viewing cached content. Go online to refresh articles.', empty: 'No articles found.', error: 'Help could not be loaded.', retry: 'Try again', listTitle: 'All articles', }, article: { back: 'Back to overview', updated: 'Updated on {date}', relatedTitle: 'Related articles', loadingTitle: 'Loading article', loadingDescription: 'Fetching the latest details for you.', unavailable: 'This article is unavailable.', reload: 'Reload', }, }, }, }; export function isLocaleCode(value: string | null | undefined): value is LocaleCode { return SUPPORTED_LOCALES.some((item) => item.code === value); } export function translate(locale: LocaleCode, key: string): string | undefined { const segments = key.split('.'); const tryLocale = (loc: LocaleCode): string | undefined => { let current: unknown = messages[loc]; for (const segment of segments) { if (!current || typeof current !== 'object' || !(segment in current)) { return undefined; } current = (current as NestedMessages)[segment]; } return typeof current === 'string' ? current : undefined; }; return tryLocale(locale) ?? tryLocale(DEFAULT_LOCALE); }