Globaler :focus-visible-Outline (var(--color-primary), 2px) — Tailwind 4 strippt die Browser-Defaults, ohne Fokus-Ring sind Tastatur-Nutzer blind. .sr-only-Utility (Standard-Rezept) und .skip-link in app.css. prefers- reduced-motion: schaltet alle Transitions/Animationen auf 0.01ms. Layout: Skip-Link "Zum Inhalt springen" → #main, main bekommt tabindex="-1" und id, html-lang wird via $effect reaktiv mit i18n.current synchronisiert (Initial-SSR rendert "de", Browser zieht nach). Header: nav-aria-label aus i18n (common.main_nav), Locale-Switcher-Label aus common.language_switcher. ToastStack: role=region + aria-live=polite, einzelne Toasts role=alert (error) bzw. status (success/warning), Schließen- Button-Label i18n-konform. Hover-only Delete-Buttons (Decks-Liste, Deck-Detail-Karten) bekommen focus-visible:opacity-100 — bisher waren sie für Tastatur-Nutzer unsichtbar. opacity-0 statt hidden, damit Tab-Order intakt bleibt. svelte-check 379 files 0 errors, prod-Build sauber. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
220 lines
8.3 KiB
TypeScript
220 lines
8.3 KiB
TypeScript
// Default-Locale. Schlüssel sind Punkt-getrennt nach Bereich.
|
|
// Interpolation: {n}, {name}, {count} → simple replace.
|
|
|
|
export type TranslationNode = { [key: string]: string | TranslationNode };
|
|
|
|
export const de: TranslationNode = {
|
|
app: {
|
|
name: 'Cards',
|
|
title_suffix: 'Cards',
|
|
},
|
|
nav: {
|
|
decks: 'Decks',
|
|
study: 'Lernen',
|
|
import: 'Import',
|
|
stats: 'Statistik',
|
|
login_dev: 'Login (dev)',
|
|
account: 'Account',
|
|
},
|
|
landing: {
|
|
welcome: 'Lernkarten mit Spaced-Repetition.',
|
|
intro:
|
|
'Cardecky ist die föderierte Karteikarten-App des Vereins mana e.V. — FSRS-Scheduler, Cloze-Karten, Anki-Import.',
|
|
cta_login: 'Login (dev)',
|
|
dev_user_prompt: 'User-ID (dev):',
|
|
},
|
|
decks: {
|
|
title: 'Decks',
|
|
new: 'Neues Deck',
|
|
empty: 'Noch keine Decks.',
|
|
empty_cta: 'Erstes Deck anlegen',
|
|
loading: 'Lade…',
|
|
error: 'Fehler: {msg}',
|
|
card_count: '{n} Karten',
|
|
card_count_one: '1 Karte',
|
|
due_count: '{n} fällig',
|
|
delete_confirm:
|
|
'Deck "{name}" wirklich löschen? Alle Karten + Review-Daten gehen verloren.',
|
|
deleted: 'Deck "{name}" gelöscht',
|
|
delete_failed: 'Löschen fehlgeschlagen: {msg}',
|
|
},
|
|
deck_detail: {
|
|
back: '← Zurück zu Decks',
|
|
study_button: 'Lernen',
|
|
new_card: 'Neue Karte',
|
|
empty: 'Keine Karten in diesem Deck.',
|
|
empty_cta: 'Erste Karte anlegen →',
|
|
card_summary_due: '{cards} · {due} fällig',
|
|
card_delete_aria: 'Karte löschen',
|
|
card_delete_label: 'Löschen',
|
|
card_delete_confirm: 'Karte wirklich löschen? Reviews werden mit gelöscht.',
|
|
},
|
|
deck_new: {
|
|
title: 'Neues Deck',
|
|
name_label: 'Name',
|
|
description_label: 'Beschreibung (optional)',
|
|
color_label: 'Farbe (optional)',
|
|
create: 'Deck anlegen',
|
|
creating: 'Lege an…',
|
|
cancel: 'Abbrechen',
|
|
create_failed: 'Anlegen fehlgeschlagen: {msg}',
|
|
},
|
|
card_new: {
|
|
title: 'Neue Karte',
|
|
back: '← Zurück',
|
|
deck_label: 'Deck',
|
|
type_label: 'Typ',
|
|
type_basic: 'Basic (front → back)',
|
|
type_basic_reverse: 'Basic + Reverse (front ↔ back, 2 Reviews)',
|
|
type_cloze: 'Cloze (Lückentext, 1 Review pro Cluster)',
|
|
front_label: 'Vorderseite (Markdown)',
|
|
back_label: 'Rückseite (Markdown)',
|
|
back_placeholder: 'Antwort',
|
|
front_placeholder: '# Markdown ist erlaubt\n**fett**, _kursiv_, `code`',
|
|
preview_label: 'Vorschau',
|
|
cloze_text_label: 'Text mit Lücken (Markdown)',
|
|
cloze_text_placeholder: 'Die Hauptstadt von {{c1::Frankreich}} ist {{c2::Paris}}.',
|
|
cloze_help: '{{c1::Antwort}} definiert eine Lücke. Pro Cluster-ID (c1, c2, …) entsteht ein eigenes Review.',
|
|
cloze_no_clusters: 'Mindestens ein {{cN::…}}-Cluster wird gebraucht.',
|
|
cloze_clusters_detected: '{n} Cluster erkannt: c{ids} → {n} Reviews.',
|
|
cloze_preview_label: 'Vorschau (c{first} maskiert)',
|
|
cloze_extra_label: 'Extra (optional)',
|
|
cloze_extra_placeholder: 'Zusätzlicher Kontext, wird unter der Antwort gezeigt.',
|
|
create: 'Karte anlegen',
|
|
creating: 'Speichere…',
|
|
cancel: 'Abbrechen',
|
|
create_failed: 'Anlegen fehlgeschlagen: {msg}',
|
|
toast_basic: 'Karte angelegt',
|
|
toast_basic_reverse: '2 Reviews initialisiert (front→back, back→front)',
|
|
toast_cloze: '{n} Reviews initialisiert (1 pro Cluster)',
|
|
decks_load_failed: 'Decks konnten nicht geladen werden: {msg}',
|
|
},
|
|
card_edit: {
|
|
title: 'Karte bearbeiten',
|
|
back: '← Zurück zum Deck',
|
|
type_locked_help: 'Der Card-Type kann nicht geändert werden — die Reviews-Tabelle hängt am Type.',
|
|
save: 'Speichern',
|
|
saving: 'Speichere…',
|
|
cancel: 'Abbrechen',
|
|
delete: 'Löschen',
|
|
deleting: 'Lösche…',
|
|
delete_confirm: 'Karte wirklich löschen? Reviews werden mit gelöscht.',
|
|
updated: 'Karte aktualisiert',
|
|
save_failed: 'Speichern fehlgeschlagen: {msg}',
|
|
delete_failed: 'Löschen fehlgeschlagen: {msg}',
|
|
deleted: 'Karte gelöscht',
|
|
},
|
|
study: {
|
|
title: 'Lernen',
|
|
empty: 'Keine Decks.',
|
|
none_due: 'Aktuell nichts fällig.',
|
|
study_now: 'Jetzt lernen',
|
|
due_count: '{n} fällig',
|
|
},
|
|
study_session: {
|
|
back: '← Übersicht',
|
|
all_done: 'Geschafft! Alle fälligen Karten gemacht.',
|
|
stats: 'Reviews: {reviewed} · Wieder: {again}',
|
|
reveal: 'Lösung zeigen',
|
|
reveal_hint: 'Leertaste / Enter zum Aufdecken',
|
|
grade_again: 'Wieder',
|
|
grade_hard: 'Schwer',
|
|
grade_good: 'Gut',
|
|
grade_easy: 'Leicht',
|
|
grade_hint: '1=Wieder · 2=Schwer · 3=Gut · 4=Leicht',
|
|
loading: 'Lade…',
|
|
error: 'Fehler: {msg}',
|
|
},
|
|
import: {
|
|
title: 'Importieren',
|
|
intro: 'Übernimm Decks und Karten aus einer Anki-Datei (.apkg oder .colpkg). FSRS-Verlauf wird nicht übernommen — alle Karten starten als „neu".',
|
|
what_works_title: 'Was wird übernommen',
|
|
what_works_decks: 'Decks (Anki-Hierarchie Foo::Bar wird zu Foo / Bar).',
|
|
what_works_basic: 'Basic + Basic-Reverse: Front/Back direkt.',
|
|
what_works_cloze: 'Cloze: {{c1::…}} wird mit Sub-Index pro Cluster angelegt.',
|
|
what_skipped_title: 'Was nicht übernommen wird',
|
|
what_skipped_media: 'Bilder + Audio (kommen mit der Plattform-Anbindung in einer späteren Phase).',
|
|
what_skipped_history: 'FSRS-Lernverlauf (Anki-Reviews werden bewusst neu aufgesetzt).',
|
|
what_skipped_addons: 'Add-on-spezifische Card-Types (image-occlusion etc.).',
|
|
anki_label: 'Aus Anki importieren',
|
|
dropzone: '📦 .apkg-Datei hier ablegen oder klicken',
|
|
dropzone_hint: 'Basic, Basic + Reverse, Cloze · Bilder + Audio werden in dieser Phase nicht übernommen.',
|
|
parsing: 'Lese {file}…',
|
|
preview_found: 'Gefunden in',
|
|
preview_decks_one: '1 Deck',
|
|
preview_decks: '{n} Decks',
|
|
preview_cards_one: '1 Karte',
|
|
preview_cards: '{n} Karten',
|
|
preview_breakdown: '({basic} basic, {basic_reverse} basic-reverse, {cloze} cloze)',
|
|
preview_media: '{n} Medien (werden in dieser Phase NICHT übernommen)',
|
|
preview_skipped: '{n} übersprungen (unbekannter Typ)',
|
|
preview_warnings: 'Hinweise ({n})',
|
|
cancel: 'Abbrechen',
|
|
import_now: 'Importieren',
|
|
stage_decks: 'Lege Decks an · {current} / {total}',
|
|
stage_cards: 'Importiere Karten · {current} / {total}',
|
|
stage_done: 'Fertig.',
|
|
done_summary_one: '✓ {cards} Karten in 1 Deck angelegt.',
|
|
done_summary: '✓ {cards} Karten in {decks} Decks angelegt.',
|
|
done_failures: '{n} Fehler',
|
|
done_more: 'Weitere Datei',
|
|
error_label: 'Fehler: {msg}',
|
|
retry: 'Erneut versuchen',
|
|
},
|
|
inbox_banner: {
|
|
label: '📥 Inbox',
|
|
count_one: '1 eingegangene Karte aus anderen Apps',
|
|
count: '{n} eingegangene Karten aus anderen Apps',
|
|
cta: '— sortieren →',
|
|
},
|
|
account: {
|
|
title: 'Account',
|
|
user_id_label: 'User-ID',
|
|
logout: 'Abmelden',
|
|
phase2_hint:
|
|
'Phase-2-Hinweis: aktuell ist die Identität ein Dev-Stub (sessionStorage). Mit Auth-Föderation wechselt das auf einen mana-auth-Login gegen auth.mana.how.',
|
|
export_title: 'Daten-Export',
|
|
export_intro:
|
|
'Lade alle deine Cards-Daten als JSON herunter — Decks, Karten, Reviews, Study-Sessions, Tags, Media-Refs, Import-Jobs. DSGVO Art. 15/20.',
|
|
export_button: 'Daten exportieren',
|
|
export_loading: 'Lade…',
|
|
export_done: 'Export geladen: {decks} Decks, {cards} Karten, {reviews} Reviews.',
|
|
export_failed: 'Export fehlgeschlagen: {msg}',
|
|
delete_title: 'Konto löschen',
|
|
delete_intro:
|
|
'Löscht unwiderruflich alle deine Cards-Daten. DSGVO Art. 17. Andere mana-Apps (Memoro, Who, …) behalten ihre Daten unabhängig — wenn du dort auch löschen willst, mach das jeweils dort oder nutze die Verein-DSGVO-Sammelanfrage über mana-admin.',
|
|
delete_button: 'Alle Cards-Daten löschen',
|
|
delete_loading: 'Lösche…',
|
|
delete_confirm:
|
|
'ALLE deine Cards-Daten werden unwiderruflich gelöscht. Tippe LÖSCHEN zur Bestätigung.',
|
|
delete_confirm_word: 'LÖSCHEN',
|
|
delete_done: 'Gelöscht: {decks} Decks, {imports} Import-Jobs.',
|
|
delete_failed: 'Löschen fehlgeschlagen: {msg}',
|
|
},
|
|
stats: {
|
|
title: 'Statistik',
|
|
generated_at: 'Stand {date}',
|
|
decks: 'Decks',
|
|
cards: 'Karten',
|
|
reviews: 'Reviews',
|
|
due_now: 'Fällig jetzt',
|
|
days_title: 'Lerntage',
|
|
streak: 'Streak: {n} Tage · letzte 7 Tage: {total} Reviews',
|
|
streak_one: 'Streak: 1 Tag · letzte 7 Tage: {total} Reviews',
|
|
fsrs_title: 'FSRS-Status',
|
|
fsrs_intro: 'Verteilung deiner Karten-Reviews über die FSRS-Zustände.',
|
|
fsrs_new: 'Neu',
|
|
fsrs_learning: 'Lernend',
|
|
fsrs_review: 'Review',
|
|
fsrs_relearning: 'Relearning',
|
|
loading: 'Lade…',
|
|
error: 'Fehler: {msg}',
|
|
},
|
|
common: {
|
|
empty: '(leer)',
|
|
skip_to_content: 'Zum Inhalt springen',
|
|
main_nav: 'Hauptnavigation',
|
|
notifications: 'Benachrichtigungen',
|
|
language_switcher: 'Sprache wechseln',
|
|
},
|
|
};
|