- Dropdown um 3 neue Types erweitert - typing/multiple-choice: front + answer Felder (korrekte field-Namen) - multiple-choice: optionaler distractor_pool (Fallback für kleine Decks) - audio-front: audio_ref Text-Input + back Antworttext - canSave + onSubmit korrekt pro Type - i18n de + en vollständig Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
256 lines
9.4 KiB
TypeScript
256 lines
9.4 KiB
TypeScript
import type { TranslationNode } from './de.ts';
|
|
|
|
export const en: TranslationNode = {
|
|
app: {
|
|
name: 'Cards',
|
|
title_suffix: 'Cards',
|
|
},
|
|
nav: {
|
|
decks: 'Decks',
|
|
study: 'Study',
|
|
explore: 'Library',
|
|
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',
|
|
card_count_more: '{n} more cards in the stack',
|
|
card_count_more_one: '1 more card in the stack',
|
|
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.',
|
|
fan_aria: 'Fanned cards from stack "{name}"',
|
|
card_open: 'Open card — {type}',
|
|
},
|
|
deck_stack: {
|
|
aria_label: 'Stack "{name}" — {cards} cards, {due} due',
|
|
},
|
|
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. Optional hint: {{c1::Answer::Hint}} — the hint replaces „…" in the prompt.',
|
|
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)',
|
|
toast_image_occlusion: '{n} reviews initialized (1 per mask)',
|
|
type_image_occlusion: 'Image-Occlusion (image + N masks)',
|
|
type_typing: 'Typing (text input, fuzzy match)',
|
|
type_multiple_choice: 'Multiple-Choice (4 options, AI distractors)',
|
|
type_audio_front: 'Audio-Front (listen + answer)',
|
|
answer_label: 'Answer (Markdown)',
|
|
answer_placeholder: 'Correct answer',
|
|
distractor_pool_label: 'Distractor pool (optional)',
|
|
distractor_pool_placeholder: 'One entry per line — fallback when the deck is too small for AI distractors',
|
|
audio_ref_label: 'Audio reference (media_ref)',
|
|
audio_ref_placeholder: 'e.g. abc123.mp3',
|
|
back_audio_label: 'Answer text (Markdown)',
|
|
toast_typing: 'Typing card created',
|
|
toast_multiple_choice: 'Multiple-choice card created',
|
|
toast_audio_front: 'Audio-front card created',
|
|
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}',
|
|
manage_link: 'Manage cards →',
|
|
},
|
|
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_works_media: 'Images + audio (embedded as Markdown / <audio> tag).',
|
|
what_skipped_title: 'What is not imported',
|
|
what_skipped_media: '— (Images + audio are imported since Sprint 9k, see above)',
|
|
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 imported too (limit 25 MB per file).',
|
|
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 be uploaded',
|
|
preview_skipped: '{n} skipped (unknown type)',
|
|
preview_warnings: 'Notes ({n})',
|
|
cancel: 'Cancel',
|
|
import_now: 'Import',
|
|
stage_media: 'Uploading media · {current} / {total}',
|
|
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_dupes: '{n} duplicates skipped (same content already exists).',
|
|
done_media: '{uploaded} media uploaded, {failed} failed.',
|
|
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',
|
|
},
|
|
image_occlusion: {
|
|
image_label: 'Choose image',
|
|
uploading: 'Uploading image…',
|
|
not_an_image: 'File is not an image.',
|
|
canvas_aria: 'Image canvas — drag to create masks',
|
|
draw_hint: 'Drag a rectangle on the image to create a mask.',
|
|
label_placeholder: 'Label (optional)',
|
|
delete_mask: 'Delete mask',
|
|
no_image_selected: 'Choose an image first.',
|
|
no_masks: 'Create at least one mask.',
|
|
},
|
|
};
|