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>
217 lines
7.6 KiB
TypeScript
217 lines
7.6 KiB
TypeScript
import type { TranslationNode } from './de.ts';
|
|
|
|
export const en: TranslationNode = {
|
|
app: {
|
|
name: 'Cards',
|
|
title_suffix: 'Cards',
|
|
},
|
|
nav: {
|
|
decks: 'Decks',
|
|
study: 'Study',
|
|
import: 'Import',
|
|
stats: 'Stats',
|
|
login_dev: 'Login (dev)',
|
|
account: 'Account',
|
|
},
|
|
landing: {
|
|
welcome: 'Spaced-repetition flashcards.',
|
|
intro:
|
|
'Cardecky is the federated flashcard app of mana e.V. — FSRS scheduler, cloze cards, Anki import.',
|
|
cta_login: 'Login (dev)',
|
|
dev_user_prompt: 'User ID (dev):',
|
|
},
|
|
decks: {
|
|
title: 'Decks',
|
|
new: 'New deck',
|
|
empty: 'No decks yet.',
|
|
empty_cta: 'Create first deck',
|
|
loading: 'Loading…',
|
|
error: 'Error: {msg}',
|
|
card_count: '{n} cards',
|
|
card_count_one: '1 card',
|
|
due_count: '{n} due',
|
|
delete_confirm:
|
|
'Really delete deck "{name}"? All cards + review data will be lost.',
|
|
deleted: 'Deck "{name}" deleted',
|
|
delete_failed: 'Delete failed: {msg}',
|
|
},
|
|
deck_detail: {
|
|
back: '← Back to decks',
|
|
study_button: 'Study',
|
|
new_card: 'New card',
|
|
empty: 'No cards in this deck.',
|
|
empty_cta: 'Create first card →',
|
|
card_summary_due: '{cards} · {due} due',
|
|
card_delete_aria: 'Delete card',
|
|
card_delete_label: 'Delete',
|
|
card_delete_confirm: 'Really delete card? Reviews will be deleted with it.',
|
|
},
|
|
deck_new: {
|
|
title: 'New deck',
|
|
name_label: 'Name',
|
|
description_label: 'Description (optional)',
|
|
color_label: 'Color (optional)',
|
|
create: 'Create deck',
|
|
creating: 'Creating…',
|
|
cancel: 'Cancel',
|
|
create_failed: 'Create failed: {msg}',
|
|
},
|
|
card_new: {
|
|
title: 'New card',
|
|
back: '← Back',
|
|
deck_label: 'Deck',
|
|
type_label: 'Type',
|
|
type_basic: 'Basic (front → back)',
|
|
type_basic_reverse: 'Basic + Reverse (front ↔ back, 2 reviews)',
|
|
type_cloze: 'Cloze (fill-in-the-blank, 1 review per cluster)',
|
|
front_label: 'Front (Markdown)',
|
|
back_label: 'Back (Markdown)',
|
|
back_placeholder: 'Answer',
|
|
front_placeholder: '# Markdown is allowed\n**bold**, _italic_, `code`',
|
|
preview_label: 'Preview',
|
|
cloze_text_label: 'Text with blanks (Markdown)',
|
|
cloze_text_placeholder: 'The capital of {{c1::France}} is {{c2::Paris}}.',
|
|
cloze_help: '{{c1::Answer}} defines a blank. Each cluster ID (c1, c2, …) becomes its own review.',
|
|
cloze_no_clusters: 'At least one {{cN::…}} cluster is required.',
|
|
cloze_clusters_detected: '{n} clusters detected: c{ids} → {n} reviews.',
|
|
cloze_preview_label: 'Preview (c{first} masked)',
|
|
cloze_extra_label: 'Extra (optional)',
|
|
cloze_extra_placeholder: 'Additional context, shown below the answer.',
|
|
create: 'Create card',
|
|
creating: 'Saving…',
|
|
cancel: 'Cancel',
|
|
create_failed: 'Create failed: {msg}',
|
|
toast_basic: 'Card created',
|
|
toast_basic_reverse: '2 reviews initialized (front→back, back→front)',
|
|
toast_cloze: '{n} reviews initialized (1 per cluster)',
|
|
decks_load_failed: 'Could not load decks: {msg}',
|
|
},
|
|
card_edit: {
|
|
title: 'Edit card',
|
|
back: '← Back to deck',
|
|
type_locked_help: 'The card type cannot be changed — the reviews table depends on it.',
|
|
save: 'Save',
|
|
saving: 'Saving…',
|
|
cancel: 'Cancel',
|
|
delete: 'Delete',
|
|
deleting: 'Deleting…',
|
|
delete_confirm: 'Really delete card? Reviews will be deleted with it.',
|
|
updated: 'Card updated',
|
|
save_failed: 'Save failed: {msg}',
|
|
delete_failed: 'Delete failed: {msg}',
|
|
deleted: 'Card deleted',
|
|
},
|
|
study: {
|
|
title: 'Study',
|
|
empty: 'No decks.',
|
|
none_due: 'Nothing due right now.',
|
|
study_now: 'Study now',
|
|
due_count: '{n} due',
|
|
},
|
|
study_session: {
|
|
back: '← Overview',
|
|
all_done: 'Done! All due cards reviewed.',
|
|
stats: 'Reviews: {reviewed} · Again: {again}',
|
|
reveal: 'Show answer',
|
|
reveal_hint: 'Space / Enter to reveal',
|
|
grade_again: 'Again',
|
|
grade_hard: 'Hard',
|
|
grade_good: 'Good',
|
|
grade_easy: 'Easy',
|
|
grade_hint: '1=Again · 2=Hard · 3=Good · 4=Easy',
|
|
loading: 'Loading…',
|
|
error: 'Error: {msg}',
|
|
},
|
|
import: {
|
|
title: 'Import',
|
|
intro: 'Import decks and cards from an Anki file (.apkg or .colpkg). FSRS history is not carried over — all cards start as "new".',
|
|
what_works_title: 'What is imported',
|
|
what_works_decks: 'Decks (Anki hierarchy Foo::Bar becomes Foo / Bar).',
|
|
what_works_basic: 'Basic + Basic-Reverse: front/back directly.',
|
|
what_works_cloze: 'Cloze: {{c1::…}} is created with sub-index per cluster.',
|
|
what_skipped_title: 'What is not imported',
|
|
what_skipped_media: 'Images + audio (will arrive with the platform integration in a later phase).',
|
|
what_skipped_history: 'FSRS learning history (Anki reviews are deliberately reset).',
|
|
what_skipped_addons: 'Add-on specific card types (image-occlusion etc.).',
|
|
anki_label: 'Import from Anki',
|
|
dropzone: '📦 Drop .apkg file here or click',
|
|
dropzone_hint: 'Basic, Basic + Reverse, Cloze · Images + audio are not imported in this phase.',
|
|
parsing: 'Reading {file}…',
|
|
preview_found: 'Found in',
|
|
preview_decks_one: '1 deck',
|
|
preview_decks: '{n} decks',
|
|
preview_cards_one: '1 card',
|
|
preview_cards: '{n} cards',
|
|
preview_breakdown: '({basic} basic, {basic_reverse} basic-reverse, {cloze} cloze)',
|
|
preview_media: '{n} media files (will NOT be imported in this phase)',
|
|
preview_skipped: '{n} skipped (unknown type)',
|
|
preview_warnings: 'Notes ({n})',
|
|
cancel: 'Cancel',
|
|
import_now: 'Import',
|
|
stage_decks: 'Creating decks · {current} / {total}',
|
|
stage_cards: 'Importing cards · {current} / {total}',
|
|
stage_done: 'Done.',
|
|
done_summary_one: '✓ {cards} cards in 1 deck.',
|
|
done_summary: '✓ {cards} cards in {decks} decks.',
|
|
done_failures: '{n} errors',
|
|
done_more: 'Another file',
|
|
error_label: 'Error: {msg}',
|
|
retry: 'Try again',
|
|
},
|
|
inbox_banner: {
|
|
label: '📥 Inbox',
|
|
count_one: '1 incoming card from other apps',
|
|
count: '{n} incoming cards from other apps',
|
|
cta: '— sort →',
|
|
},
|
|
account: {
|
|
title: 'Account',
|
|
user_id_label: 'User ID',
|
|
logout: 'Sign out',
|
|
phase2_hint:
|
|
'Phase 2 note: identity is currently a dev stub (sessionStorage). With auth federation, this switches to a mana-auth login against auth.mana.how.',
|
|
export_title: 'Data export',
|
|
export_intro:
|
|
'Download all your Cards data as JSON — decks, cards, reviews, study sessions, tags, media refs, import jobs. GDPR Art. 15/20.',
|
|
export_button: 'Export data',
|
|
export_loading: 'Loading…',
|
|
export_done: 'Export downloaded: {decks} decks, {cards} cards, {reviews} reviews.',
|
|
export_failed: 'Export failed: {msg}',
|
|
delete_title: 'Delete account',
|
|
delete_intro:
|
|
'Irreversibly deletes all your Cards data. GDPR Art. 17. Other mana apps (Memoro, Who, …) keep their data independently — if you want to delete there too, do it there or use the Verein-wide GDPR collective request via mana-admin.',
|
|
delete_button: 'Delete all Cards data',
|
|
delete_loading: 'Deleting…',
|
|
delete_confirm:
|
|
'ALL your Cards data will be deleted irreversibly. Type DELETE to confirm.',
|
|
delete_confirm_word: 'DELETE',
|
|
delete_done: 'Deleted: {decks} decks, {imports} import jobs.',
|
|
delete_failed: 'Delete failed: {msg}',
|
|
},
|
|
stats: {
|
|
title: 'Stats',
|
|
generated_at: 'As of {date}',
|
|
decks: 'Decks',
|
|
cards: 'Cards',
|
|
reviews: 'Reviews',
|
|
due_now: 'Due now',
|
|
days_title: 'Active days',
|
|
streak: 'Streak: {n} days · last 7 days: {total} reviews',
|
|
streak_one: 'Streak: 1 day · last 7 days: {total} reviews',
|
|
fsrs_title: 'FSRS status',
|
|
fsrs_intro: 'Distribution of your card reviews across FSRS states.',
|
|
fsrs_new: 'New',
|
|
fsrs_learning: 'Learning',
|
|
fsrs_review: 'Review',
|
|
fsrs_relearning: 'Relearning',
|
|
loading: 'Loading…',
|
|
error: 'Error: {msg}',
|
|
},
|
|
common: {
|
|
empty: '(empty)',
|
|
skip_to_content: 'Skip to content',
|
|
main_nav: 'Main navigation',
|
|
notifications: 'Notifications',
|
|
language_switcher: 'Switch language',
|
|
},
|
|
};
|