freezmo/lib/i18n-dict.ts
Martin Dvorak 22af888ec0 Redesign the quick-action CTAs: two ways in (study deck vs. homework)
Library empty state ("Your icebox")
- Replace the single "Make your first deck" button with two quick-action
  cards: "Create interactive test" (primary, ice-gradient icon + subtle
  accent border) and "Help me with my homework" (glass card, sparkles
  icon). Stacked on phones, side by side from 640px up; both >=44px tall.

/create
- Replace the "Got homework instead of notes?" banner with a two-card mode
  switcher above "1. Add your notes": "Study Deck & Quizzes" (the current
  view — aria-current, accent tint, ice icon) and "Homework Solver" (a
  shortcut to /homework). Icon-on-top tiles on phones, inline rows from
  640px up; glass tokens + backdrop blur in both themes.

Strings
- New keys in both dictionaries: emptyStateCreateDeck, emptyStateHomework,
  actionStudyDeck, actionHomework. Drop the now-unused homework.entry and
  library.makeFirst keys.

Drive-by AA fix (light mode, /create only)
- The "New folder" dashed button and the difficulty blurbs move from
  frost-500 to frost-600 (4.0:1 -> ~6:1). Dark mode is unchanged.

Service worker: freezmo-v8.

Verified: redesign suite 32/32 (stacked/side-by-side layouts, 360/390/1280,
dark+light, en+cs, >=44px targets, both cards navigate, no overflow, axe
contrast), mobile 49/49, i18n 27/27, desktop regression 73/73. Lint + build
clean.
2026-10-08 19:19:20 +00:00

1233 lines
61 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Pure dictionary + lookup helpers, shared by the browser and the server.
*
* No React and no DOM here on purpose: API routes import this module to answer
* in the same language the UI is currently set to (the browser sends
* `x-freezmo-locale` with every request).
*/
export type Locale = "en" | "cs";
export const LOCALES: Array<{ id: Locale; label: string; short: string }> = [
{ id: "en", label: "English", short: "EN" },
{ id: "cs", label: "Čeština", short: "CS" },
];
export const DEFAULT_LOCALE: Locale = "en";
/** Header the browser uses to ask for a language in API responses. */
export const LOCALE_HEADER = "x-freezmo-locale";
export type Vars = Record<string, string | number>;
function interpolate(template: string, vars?: Vars): string {
if (!vars) return template;
return template.replace(/\{(\w+)\}/g, (match, key: string) =>
Object.prototype.hasOwnProperty.call(vars, key) ? String(vars[key]) : match,
);
}
/** Translate a key for an explicit locale (falls back to English, then the key). */
export function translate(locale: Locale, key: string, vars?: Vars): string {
const table = locale === "cs" ? cs : en;
const template = table[key] ?? en[key] ?? key;
return interpolate(template, vars);
}
/**
* Plural-aware lookup. Keys live as `<base>.one` / `<base>.few` / `<base>.many`;
* Czech uses the 1 / 2-4 / 5+ rule, English only 1 / rest. `{n}` is provided.
*/
export function translatePlural(locale: Locale, n: number, base: string, vars?: Vars): string {
const form =
locale === "cs"
? n === 1
? "one"
: n >= 2 && n <= 4
? "few"
: "many"
: n === 1
? "one"
: "many";
const table = locale === "cs" ? cs : en;
const template = table[`${base}.${form}`] ?? table[`${base}.many`] ?? en[`${base}.many`] ?? base;
return interpolate(template, { n, ...vars });
}
/** Normalise anything a request sends ("cs-CZ", "CS", "en") to a supported locale. */
export function normaliseLocale(value: string | null | undefined): Locale {
if (!value) return DEFAULT_LOCALE;
const key = value.trim().toLowerCase().split(/[-_,;]/)[0];
return key === "cs" || key === "cz" ? "cs" : "en";
}
const en: Record<string, string> = {
/* common ---------------------------------------------------------- */
"common.cancel": "Cancel",
"common.save": "Save",
"common.add": "Add",
"common.delete": "Delete",
"common.rename": "Rename",
"common.close": "Close",
"common.retry": "Retry",
"common.settings": "Settings",
"common.library": "Library",
"common.newDeck": "New deck",
"common.folder": "Folder",
"common.folders": "Folders",
"common.newFolder": "New folder",
"common.folderName": "Folder name",
"common.newFolderName": "New folder name",
"common.unorganized": "Unorganized",
"common.allDecks": "All decks",
"common.lightMode": "Light mode",
"common.darkMode": "Dark mode",
"common.switchToLight": "Switch to light mode",
"common.switchToDark": "Switch to dark mode",
"common.menu": "Menu",
/* header ---------------------------------------------------------- */
"header.openMenu": "Open menu",
"header.home": "Freezmo home",
"header.tagline": "study feed",
"header.openSettings": "Open settings and Ollama status",
/* tab bar --------------------------------------------------------- */
"tabbar.main": "Main",
/* sidebar --------------------------------------------------------- */
"sidebar.collapse": "Collapse sidebar",
"sidebar.expand": "Expand sidebar",
"sidebar.library": "Library",
"sidebar.label": "Sidebar",
"sidebar.closeMenu": "Close menu",
/* language -------------------------------------------------------- */
"language.label": "Language",
"language.note": "Interface language. Your notes and the generated decks keep their own language.",
/* settings -------------------------------------------------------- */
"settings.title": "Settings",
"settings.subtitle.before": "Models come from",
"settings.subtitle.after": "on the server; your API key stays in this browser.",
"settings.close": "Close settings",
"settings.apiKey": "Ollama API key",
"settings.savedInBrowser": "saved in this browser",
"settings.required": "required",
"settings.notNeeded": "not needed",
"settings.pasteKey": "paste your key from ollama.com/settings/keys",
"settings.hide": "Hide",
"settings.show": "Show",
"settings.keyNote":
"Stored only in this browser's local storage and sent with each request so the server can talk to Ollama for you. It is never written to the server, never logged and never put in a cookie.",
"settings.localTipTitle": "Using the local daemon?",
"settings.localTipSet": "Set",
"settings.keyNoteAfter": "in",
"settings.keyNoteEnd": "and use",
"settings.keyNoteEnd2": " instead.",
"settings.removeKeyConfirm":
"Remove the key? The full-screen key setup comes back until you add one.",
"settings.removeKeyYes": "Yes, remove it",
"settings.testConnection": "Test connection",
"settings.removeKey": "Remove key from this browser",
"settings.savedTesting": "Saved — testing…",
"settings.installApp": "Install app",
"settings.addToHome": "Add Freezmo to your home screen",
"settings.checking": "Checking Ollama…",
/* health ---------------------------------------------------------- */
"health.ok": "Ollama is reachable and both models are available.",
"health.needsKey": "Waiting for your API key.",
"health.rejected": "Ollama rejected your API key.",
"health.modelMissing": "Ollama is reachable, but a configured model is missing.",
"health.unreachable": "Ollama isn't reachable.",
"health.auth": "Auth",
"health.authUserKey": "Your own key",
"health.authServer": "Server key (.env)",
"health.authLocal": "Local daemon sign-in",
"health.endpoint": "Endpoint",
"health.mode": "Mode",
"health.modeCloud": "Direct cloud API",
"health.modeLocal": "Local daemon",
"health.key": "Key",
"health.keyMissing": "missing",
"health.keyAccepted": "accepted",
"health.keyRejected": "rejected",
"health.keyNotNeeded": "not needed",
"health.keyUnknown": "unknown",
"health.visionModel": "Vision model (reads photos)",
"health.textModel": "Text model (builds & grades)",
"health.available": "available",
"health.notFound": "not found",
"health.unknown": "unknown",
"health.configuredAs": "configured as {model}",
"health.checkedNow": "Checked just now",
"health.checkedAt": "Checked at {time}",
"health.createKey.before": "Create a key at",
"health.createKey.after": "and paste it above. Tip: with the local daemon you can run",
"health.createKey.end": "instead and set",
/* make my homework ------------------------------------------------- */
"homework.nav": "Make my homework",
"homework.title": "Make my homework",
"homework.subtitle":
"Paste the assignment, snap a photo or add a document — then choose whether I should just do it, or guide you through it.",
"homework.source.title": "1. The assignment",
"homework.source.help": "Paste its text, drop photos, or add a document.",
"homework.source.textLabel": "Assignment text",
"homework.source.placeholder": "Paste the assignment here…",
"homework.source.add": "Add photos or files",
"homework.source.drop": "Drop the assignment here",
"homework.source.formats": "Text, photos, PDF, DOCX, PPTX, ODT/ODP, TXT, MD",
"homework.source.ready": "Ready when you are.",
"homework.source.need": "Add the assignment first.",
"homework.source.tooManyPhotos": "Up to {n} photos per assignment.",
"homework.source.tooManyDocs": "Up to {n} documents per assignment.",
"homework.mode.title": "2. What should I do?",
"homework.mode.make.title": "Just make it",
"homework.mode.make.subtitle": "The finished homework, ready to copy.",
"homework.mode.guide.title": "Explain how to make this homework",
"homework.mode.guide.subtitle": "A step-by-step guide with hints and quick checks.",
"homework.start.make": "Make my homework",
"homework.start.guide": "Guide me through it",
"homework.loading.title": "Working on the assignment…",
"homework.loading.reading": "Reading the assignment…",
"homework.loading.vision2": "Transcribing the questions…",
"homework.loading.writing": "Writing your homework…",
"homework.loading.planning": "Planning the steps…",
"homework.loading.gen2": "Checking every part…",
"homework.loading.gen3": "Almost done…",
"homework.error.title": "That didn't work",
"homework.error.body": "Something went wrong while working on the assignment.",
"homework.result.title": "Your homework",
"homework.result.copy": "Copy",
"homework.result.copied": "Copied!",
"homework.result.copyFailed": "Copy didn't work — select the text and copy it manually.",
"homework.result.again": "Make it again",
"homework.result.new": "New assignment",
"homework.guide.stepOf": "Step {n} of {total}",
"homework.guide.close": "Close the guide",
"homework.guide.tryIt": "Try it yourself",
"homework.guide.hint": "Show a hint",
"homework.guide.hintMore": "Another hint",
"homework.guide.hintAnswer": "Show the answer",
"homework.guide.hintN": "Hint {n}",
"homework.guide.next": "Next step",
"homework.guide.showSolution": "Show the solution",
"homework.guide.solution": "The full solution",
"homework.guide.solutionNote":
"Assembled from the steps above — copy it and hand it in.",
"homework.history.title": "Recent homework",
"homework.history.made": "Made",
"homework.history.guide": "Guide",
"homework.history.openTitle": "Open “{title}”",
"homework.history.deleteConfirm": "Delete this result?",
/* quick actions (library empty state + /create switcher) ----------- */
"emptyStateCreateDeck": "Create interactive test",
"emptyStateHomework": "Help me with my homework",
"actionStudyDeck": "Study Deck & Quizzes",
"actionHomework": "Homework Solver",
/* library --------------------------------------------------------- */
"library.title": "Your icebox",
"library.subtitle": "Decks frozen from your notes, saved in this browser.",
"library.import": "Import JSON",
"library.folderCreated": "Folder created — decks you freeze while it's selected land here.",
"library.imported": "Imported “{title}”.",
"library.couldNotRead": "Could not read that file.",
"library.folderDeleted": "Folder deleted — its decks moved to Unorganized.",
"library.deckMoved": "Deck moved.",
"library.deckMovedUnorganized": "Deck moved to Unorganized.",
"library.noDecks": "No decks yet",
"library.emptyBody": "Freeze a page of notes into a feed of flashcards and quizzes.",
"library.emptyHint": "Using your own Ollama models —",
"library.emptySettings": "check the connection in Settings",
"library.renameFolderAria": "Folder name",
"library.nothingUnfiled": "Nothing unfiled right now.",
"library.emptyFolder": "Empty folder — freeze a deck into it or move one over from Unorganized.",
"library.stats.best": "best {n}%",
"library.deleteWarning.one": "The {n} deck inside will move to Unorganized — nothing is deleted.",
"library.deleteWarning.few": "The {n} decks inside will move to Unorganized — nothing is deleted.",
"library.deleteWarning.many": "The {n} decks inside will move to Unorganized — nothing is deleted.",
"stats.decks.one": "{n} deck",
"stats.decks.few": "{n} decks",
"stats.decks.many": "{n} decks",
"stats.folders.one": "{n} folder",
"stats.folders.few": "{n} folders",
"stats.folders.many": "{n} folders",
"stats.activities.one": "{n} activity",
"stats.activities.few": "{n} activities",
"stats.activities.many": "{n} activities",
/* deck card ------------------------------------------------------- */
"deck.activities.one": "{n} activity",
"deck.activities.few": "{n} activities",
"deck.activities.many": "{n} activities",
"deck.runs.one": "{n} run",
"deck.runs.few": "{n} runs",
"deck.runs.many": "{n} runs",
"deck.notPlayed": "Not played yet",
"deck.best": "Best {n}%",
"deck.bestLabel": "Best",
"deck.study": "Study",
"deck.actions": "Deck actions",
"deck.titleLabel": "Deck title",
"deck.rename": "Rename",
"deck.export": "Export JSON",
"deck.regenerate": "Regenerate deck",
"deck.reallyDelete": "Really delete",
"deck.delete": "Delete deck",
"deck.newFolder": "+ New folder",
"deck.addMore": "Add more activities",
"deck.generate": "Generate",
"deck.noSource": "No stored source text for this deck (imported decks don't carry it).",
"deck.noSourceExtend": "This deck has no stored source notes, so it can't be extended.",
"deck.noSourceRegenerate": "This deck has no stored source notes, so it can't be regenerated.",
"deck.couldNotAdd": "Could not add more activities.",
"deck.couldNotRegenerate": "Could not regenerate the deck.",
"deck.regenerated": "Deck regenerated",
"deck.added.one": "Added {n} activity",
"deck.added.few": "Added {n} activities",
"deck.added.many": "Added {n} activities",
"deck.addingActivities": "Adding activities",
"deck.regeneratingDeck": "Regenerating deck",
"deck.addingNew": "Adding new activities…",
"deck.regeneratingWhole": "Regenerating the whole deck…",
/* upload ---------------------------------------------------------- */
"upload.title": "Turn your notes into a study feed",
"upload.intro":
"Snap or drop photos of your notes. Freezmo reads them with your own Ollama vision model and builds a swipeable deck of mini study games.",
"upload.errNeedsKey": "API key required",
"upload.errRate": "Slow down a moment",
"upload.errAuth": "Ollama rejected your key",
"upload.errFailed": "Generation failed",
"upload.openSettings": "Open settings",
"upload.addPhotosAria": "Add photos of your notes",
"upload.step1": "1. Add your notes",
"upload.step1Help":
"Drag & drop, paste a screenshot (Ctrl/⌘+V), or use the camera. Photos, PDF, Word, PowerPoint, OpenDocument and text files. Up to {images} photos and {documents} documents.",
"upload.chooseFiles": "Choose files",
"upload.camera": "Camera",
"upload.dropHere": "Drop your notes here",
"upload.formats": "Photos, PDF, DOCX, PPTX, ODT/ODP, TXT, MD · images resized to 1600px before sending",
"upload.remove": "Remove {name}",
"upload.addMorePhotos": "Add more photos",
"upload.reading": "Reading…",
"upload.noReadable": "No readable text — is this a scan without a text layer?",
"upload.words.one": "{n} word",
"upload.words.few": "{n} words",
"upload.words.many": "{n} words",
"upload.pages.one": "{n} page",
"upload.pages.few": "{n} pages",
"upload.pages.many": "{n} pages",
"upload.pageImages.one": "{n} page image",
"upload.pageImages.few": "{n} page images",
"upload.pageImages.many": "{n} page images",
"upload.truncated": "truncated",
"upload.photoSummary.one": "{n} photo · {size} after compression",
"upload.photoSummary.few": "{n} photos · {size} after compression",
"upload.photoSummary.many": "{n} photos · {size} after compression",
"upload.docSummary.one": "{n} document",
"upload.docSummary.few": "{n} documents",
"upload.docSummary.many": "{n} documents",
"upload.docImages.one": "{n} page image",
"upload.docImages.few": "{n} page images",
"upload.docImages.many": "{n} page images",
"upload.skipImages.one": "{n} image will be skipped (max {max} per deck)",
"upload.skipImages.few": "{n} images will be skipped (max {max} per deck)",
"upload.skipImages.many": "{n} images will be skipped (max {max} per deck)",
"upload.compressing": "Compressing photos…",
"upload.step2": "2. Optional extras",
"upload.step2Help": "Paste extra notes or give the AI a hint about what to focus on.",
"upload.pastedNotes": "Pasted notes or text",
"upload.pastedPlaceholder": "Paste lecture text, a summary, or any extra facts here…",
"upload.topicHint": "Topic hint (optional)",
"upload.topicPlaceholder": "e.g. focus on the formulas, or on dates",
"upload.step3": "3. Difficulty & size",
"upload.freezeInto": "Freeze into folder",
"upload.folderPlaceholder": "e.g. History",
"upload.difficultyAria": "Difficulty",
"upload.easy": "Easy",
"upload.easyBlurb": "Recall & recognition",
"upload.medium": "Medium",
"upload.mediumBlurb": "Understanding & application",
"upload.hard": "Hard",
"upload.hardBlurb": "Analysis & tricky edges",
"upload.activities": "Activities",
"upload.readingDocs": "Reading your documents…",
"upload.ready": "Ready when you are — generation can take a minute.",
"upload.addToStart": "Add a photo or a document (or some text) to start.",
"upload.freezeDeck": "Freeze my deck",
"upload.generationProgress": "Generation progress",
"upload.cancel": "Cancel",
"upload.unsupported":
"“{name}” is not a supported format. Add photos or a PDF, DOCX, PPTX, ODT/ODP, TXT or MD file.",
"upload.tooManyDocs": "Up to {n} documents per deck.",
"upload.tooManyPhotos": "Up to {n} photos per deck.",
"upload.couldNotReadFile": "Could not read “{name}”.",
"upload.couldNotReadDoc": "Could not read this file.",
"upload.genericError":
"Something went wrong while generating your deck. Check your Ollama settings and try again.",
"upload.warming": "Warming up…",
"upload.vision1": "Reading your notes…",
"upload.vision2": "Transcribing handwriting…",
"upload.vision3": "Spotting definitions…",
"upload.gen1": "Finding the key ideas…",
"upload.gen2": "Writing good questions…",
"upload.gen3": "Building your activities…",
"upload.tip1": "Tip: swipe up and down through your deck like a feed.",
"upload.tip2": "Tip: matching questions only give full XP for a perfect match.",
"upload.tip3": "Tip: your decks are saved in this browser — no account needed.",
"upload.tip4": "Tip: export a deck as JSON to share it with a friend.",
/* study feed ------------------------------------------------------ */
"feed.back": "Back to your library",
"feed.activityOf": "Activity {n} of {total}",
"feed.streak": "Answer streak: {n}",
"feed.xp": "XP earned: {n}",
"feed.retry": "Retry · ",
"feed.swipeUp": "Swipe up for the next activity ↑",
"feed.prev": "Previous activity",
"feed.next": "Next activity",
"feed.results": "Results",
/* results --------------------------------------------------------- */
"results.perfect": "Perfect freeze! 🧊",
"results.nice": "Nicely done!",
"results.start": "Good start — keep going!",
"results.acedAll": "You aced all {n} activities in “{title}”.",
"results.gotOf": "You got {correct} of {total} right{retry}.",
"results.retrySuffix": " on the retry run",
"results.xpEarned": "{n} XP earned",
"results.bestStreak": "Best streak {n}",
"results.difficulty": "{level} difficulty",
"results.best": "🏆 Best {n}%",
"results.missed": "Missed items ({n})",
"results.yourAnswer": "Your answer: {answer}",
"results.answer": "Answer:",
"results.retryMissed": "Retry missed ones ({n})",
"results.studyAgain": "Study this deck again",
"results.studyWhole": "Study the whole deck again",
"results.regenerate": "Regenerate deck",
"results.regenerating": "Regenerating…",
"results.export": "Export JSON",
"results.score": "score",
"results.true": "True",
"results.false": "False",
/* gate ------------------------------------------------------------ */
"gate.title": "Connect your Ollama key",
"gate.subtitle": "One step to freeze your first deck.",
"gate.apiKeyLabel": "Ollama API key",
"gate.placeholder": "Paste your Ollama API key",
"gate.hide": "Hide API key",
"gate.show": "Show API key",
"gate.checking": "Checking…",
"gate.continue": "Continue",
"gate.howGet": "How do I get a key?",
"gate.howGetAria": "How do I get an Ollama API key?",
"gate.stays": "Your key stays in this browser. Our server never stores it.",
"gate.helpTitle": "How to get your Ollama API key",
"gate.freeTier.before": "You only need the",
"gate.freeTier.strong": "free cloud tier",
"gate.freeTier.after": ". No payment and no credit card required.",
"gate.step1.before": "Go to",
"gate.step1.after": "and sign in, or create a free account.",
"gate.step2.before": "Open Settings → API keys at",
"gate.step2.after": ".",
"gate.step3.before": "Click",
"gate.step3.strong": "“Create new key”",
"gate.step3.after": ", give it any name, and copy it.",
"gate.step4": "Paste it into Freezmo and press Continue.",
"gate.footnote":
"The free tier is enough for generating decks. You can swap or remove your key anytime in Settings.",
"gate.gotIt": "Got it",
/* install sheet --------------------------------------------------- */
"install.titleIos": "Install Freezmo on iPhone",
"install.titleAndroid": "Install Freezmo on Android",
"install.install": "Install",
"install.installing": "Installing…",
"install.orManually": "Or install manually:",
"install.maybeLater": "Maybe later",
"install.openIn": "Open this page in",
"install.tap": "Tap",
"install.tapConfirm": "Tap",
"install.inBrowserBar": "in the browser bar",
"install.then": ", then",
"install.choose": "Choose",
"install.toConfirm": "to confirm",
"install.or": "or",
"install.inTopRight": "in the top right of the browser",
"install.moreOptions": "More options",
"install.share": "Share",
"install.more": "More",
"install.addToHome": "Add to Home Screen",
"install.add": "Add",
"install.installApp": "Install app",
"install.addToHomeScreen": "Add to Home screen",
/* deck page ------------------------------------------------------- */
"deckPage.loading": "Loading your deck…",
"deckPage.notFound": "Deck not found",
"deckPage.notFoundBody":
"This deck isn't in this browser's library. It may have been deleted, or it was created in a different browser profile.",
"deckPage.back": "Back to library",
"deckPage.create": "Create a deck",
/* offline --------------------------------------------------------- */
"offline.title": "You're offline",
"offline.body":
"Freezmo needs a connection for generating decks and grading answers. Your saved decks are still in this browser — reconnect and reload.",
/* activity types -------------------------------------------------- */
"type.flashcard": "Flashcard",
"type.multipleChoice": "Multiple choice",
"type.trueFalse": "True or false",
"type.fillBlank": "Fill the blank",
"type.matching": "Match up",
"type.ordering": "Put in order",
"type.shortAnswer": "Short answer",
/* activities shared ----------------------------------------------- */
"activity.answerOptions": "Answer options",
"activity.wordBank": "Word bank",
"activity.niceOne": "Nice one!",
"activity.notQuite": "Not quite",
"activity.why": "Why:",
"activity.seeResults": "See results",
"activity.next": "Next",
"activity.correctAnswer": "Correct answer:",
"activity.unsupported": "Unsupported activity: {json}",
/* flashcard ------------------------------------------------------- */
"flashcard.tapToFlip": "Tap the card to flip it.",
"flashcard.didYouKnow": "Did you know it before flipping?",
"flashcard.question": "Question",
"flashcard.answer": "Answer",
"flashcard.tapFlip": "tap to flip ↻",
"flashcard.flipBack": "Flip back to the question",
"flashcard.reveal": "Reveal the answer",
"flashcard.notYet": "😅 Not yet",
"flashcard.knewIt": "🧊 I knew it",
"flashcard.frozenSolid": "Frozen solid!",
"flashcard.keepPile": "Keep it on the pile",
/* multiple choice ------------------------------------------------ */
"mc.tipKeys": "Tip: press 1–4 on a keyboard.",
/* true/false ------------------------------------------------------ */
"tf.hint": "Swipe the card right for true, left for false — or tap a button.",
"tf.niceSwipe": "Nice swipe!",
"tf.trueLabel": "✓ true",
"tf.falseLabel": "false ✕",
"tf.falseBtn": "✕ False",
"tf.trueBtn": "✓ True",
"tf.statementIs.before": "This statement is",
"tf.true": "true",
"tf.false": "false",
/* fill blank ------------------------------------------------------ */
"fill.typeMissing": "Type the missing word",
"fill.placeholder": "type it",
"fill.check": "Check answer",
"fill.hint": "Press Enter to check. Small typos are forgiven.",
"fill.expected": "Expected:",
"fill.youWrote": "— you wrote",
/* matching -------------------------------------------------------- */
"matching.matched": "{n}/{total} matched",
"matching.allMatched": "All matched!",
"matching.tapTerm": "Tap a term on the left, then its match on the right.",
"matching.tapDef": "Now tap the matching definition →",
"matching.perfect": "Perfect match!",
"matching.slips": "Matched — with a few slips",
"matching.firstTry": "You matched everything first try.",
"matching.wrongAttempts.one": "You made {n} wrong attempt.",
"matching.wrongAttempts.few": "You made {n} wrong attempts.",
"matching.wrongAttempts.many": "You made {n} wrong attempts.",
/* ordering -------------------------------------------------------- */
"ordering.hint": "Drag the cards, or use ▲▼, then check your order.",
"ordering.correctOrder": "Here is the correct order:",
"ordering.check": "Check order",
"ordering.moveUp": "Move “{item}” up",
"ordering.moveDown": "Move “{item}” down",
/* short answer ---------------------------------------------------- */
"short.placeholder": "Type your answer in your own words…",
"short.ctrlEnter": "Ctrl/⌘ + Enter to submit.",
"short.submit": "Submit answer",
"short.grading": "Grading your answer…",
"short.couldNotGrade": "Couldn't grade that answer",
"short.showAnswer": "Show answer & self-check",
"short.modelAnswer": "Model answer",
"short.keyPoints": "Key points:",
"short.missedIt": "😅 I missed it",
"short.hadRight": "🧊 I had it right",
"short.accepted": "Accepted",
"short.needsWork": "Needs work",
"short.selfChecked": "Self-checked — you judged this one yourself.",
"short.yourAnswer": "Your answer",
"short.gradeFailed": "Grading failed. Check that Ollama is reachable and try again.",
/* client API errors ----------------------------------------------- */
"api.cancelled": "Generation was cancelled.",
"api.unreachable": "Could not reach the Freezmo server. Is it still running?",
"api.unreachableShort": "Could not reach the Freezmo server.",
"api.httpError": "The server returned HTTP {status}.",
"api.empty": "The server sent an empty response.",
"api.almost": "Almost there…",
"api.noDeck": "No deck came back from the server. Check your Ollama settings and try again.",
"api.incomplete": "The server finished without sending a deck.",
"api.noActivities": "The generated deck had no usable activities. Try again.",
"api.gradingCancelled": "Grading was cancelled.",
"api.gradingFailed": "Grading failed with HTTP {status}.",
"api.healthFailed": "The health check failed with HTTP {status}.",
"api.healthUnexpected": "The Freezmo server sent an unexpected health response.",
"api.serverUnexpected": "The Freezmo server answered unexpectedly. Try again in a moment.",
"api.keyRejected": "Ollama rejected this key. Check for typos, or create a new one at ollama.com/settings/keys.",
"api.ollamaUnreachable": "Couldn't reach Ollama just now. Check your connection and try again.",
/* server-side messages (API routes) -------------------------------- */
"server.bodyNotJson": "Request body was not valid JSON.",
"server.invalidRequest": "Invalid request: {detail}",
"server.needContent": "Add at least one photo of your notes or paste some text to build a deck.",
"server.needAssignment":
"Add the assignment first — paste its text, photograph it, or add a document.",
"server.invalidGrade": "Invalid grading request.",
"server.bodyTooLarge": "Request body is too large ({size}). Limit is {limit}.",
"server.invalidOrigin": "Invalid Origin header.",
"server.crossOrigin": "Cross-origin requests are not allowed.",
"server.rateLimited": "Too many requests — this app allows {limit} per window. Try again in {retry}s.",
"server.authMissingKey":
"Add your Ollama API key first — open the settings dialog (⚙️) and paste a key from ollama.com/settings/keys.",
"server.authServerKeyMissing":
"This server is configured to use its own Ollama API key (OLLAMA_AUTH=server), but OLLAMA_API_KEY is empty in .env.local.",
"server.authLocalCloud":
"This server is set to OLLAMA_AUTH=local but points at https://ollama.com. A signed-in local daemon can't be used against the cloud URL — either set OLLAMA_AUTH=user-key, or point OLLAMA_BASE_URL at http://localhost:11434.",
"server.ollamaUnreachable":
"Ollama isn't reachable at {url}. Check your settings and make sure the daemon is running.",
"server.ollamaTimeout": "Ollama took too long to answer. Try fewer/smaller images, a faster model, or retry.",
"server.ollamaAuth":
"Ollama rejected the request. If you are using https://ollama.com, check OLLAMA_API_KEY (create one at ollama.com/settings/keys).",
"server.ollamaUnexpected": "Ollama returned an unexpected response.",
"server.ollamaGeneric": "Something went wrong while talking to Ollama.",
"server.ollamaTimeoutList": "Timed out while listing Ollama models.",
"server.ollamaUnauthorised": "Ollama refused the request (unauthorised).",
"server.ollamaNotJson": "Ollama sent a response that wasn't JSON.",
"server.ollamaNoContent": "Ollama sent a response with no message content.",
"server.healthNoKey":
"No Ollama API key yet. Add your key in the settings dialog — it is stored only in this browser.",
"server.healthKeyRejected": "Ollama rejected this API key. Create a new one at ollama.com/settings/keys and paste it again.",
"server.healthNoCredits":
"The key works, but {model} is not included in this account's plan. Add usage credits or switch to a free-tier model in .env.local.",
"server.healthModelNoteLocal": "Not in the daemon's model list. Run `ollama pull {model}` or check your sign-in.",
"server.healthModelNoteCloud": "Not returned by {url}/api/tags. Check the model name and your key's plan.",
"server.healthModelsMissing": "Ollama is reachable, but at least one configured model is not available.",
/* folders / files -------------------------------------------------- */
"folder.nameRequired": "Give the folder a name.",
"folder.nameTooLong": "That name is a bit long (60 characters max).",
"folder.nameTaken": "Another folder already has that name.",
"file.notImage": "“{name}” is not an image.",
"file.imageTooLarge": "“{name}” is larger than 20 MB.",
"file.notDocument": "“{name}” is not a supported document (use PDF, DOCX, PPTX, ODT/ODP or text).",
"file.documentTooLarge": "“{name}” is larger than 40 MB.",
"import.notJson": "That file is not valid JSON.",
"import.notDeck": "That file doesn't look like a Freezmo deck.",
"doc.readingPage": "Reading page {n} of {total}…",
"doc.readingWord": "Reading the Word document…",
"doc.readingSlides": "Reading the slides…",
"doc.readingOpen": "Reading the OpenDocument file…",
"doc.readingText": "Reading the text file…",
};
const cs: Record<string, string> = {
/* common ---------------------------------------------------------- */
"common.cancel": "Zrušit",
"common.save": "Uložit",
"common.add": "Přidat",
"common.delete": "Smazat",
"common.rename": "Přejmenovat",
"common.close": "Zavřít",
"common.retry": "Zkusit znovu",
"common.settings": "Nastavení",
"common.library": "Knihovna",
"common.newDeck": "Nová sada",
"common.folder": "Složka",
"common.folders": "Složky",
"common.newFolder": "Nová složka",
"common.folderName": "Název složky",
"common.newFolderName": "Název nové složky",
"common.unorganized": "Nezařazené",
"common.allDecks": "Všechny sady",
"common.lightMode": "Světlý režim",
"common.darkMode": "Tmavý režim",
"common.switchToLight": "Přepnout na světlý režim",
"common.switchToDark": "Přepnout na tmavý režim",
"common.menu": "Menu",
/* header ---------------------------------------------------------- */
"header.openMenu": "Otevřít menu",
"header.home": "Domů — Freezmo",
"header.tagline": "studijní feed",
"header.openSettings": "Otevřít nastavení a stav Ollamy",
/* tab bar --------------------------------------------------------- */
"tabbar.main": "Hlavní",
/* sidebar --------------------------------------------------------- */
"sidebar.collapse": "Sbalit panel",
"sidebar.expand": "Rozbalit panel",
"sidebar.library": "Knihovna",
"sidebar.label": "Postranní panel",
"sidebar.closeMenu": "Zavřít menu",
/* language -------------------------------------------------------- */
"language.label": "Jazyk",
"language.note": "Jazyk rozhraní. Tvoje poznámky a vytvořené sady si zachovávají vlastní jazyk.",
/* settings -------------------------------------------------------- */
"settings.title": "Nastavení",
"settings.subtitle.before": "Modely pocházejí z",
"settings.subtitle.after": "na serveru; tvůj API klíč zůstává v tomto prohlížeči.",
"settings.close": "Zavřít nastavení",
"settings.apiKey": "API klíč Ollama",
"settings.savedInBrowser": "uložen v tomto prohlížeči",
"settings.required": "vyžadován",
"settings.notNeeded": "není potřeba",
"settings.pasteKey": "vlož klíč z ollama.com/settings/keys",
"settings.hide": "Skrýt",
"settings.show": "Zobrazit",
"settings.keyNote":
"Ukládá se jen do místního úložiště tohoto prohlížeče a posílá se s každým požadavkem, aby server mohl komunikovat s Ollamou. Nikdy se neukládá na server, nezapisuje do logů ani do cookies.",
"settings.localTipTitle": "Používáš místní daemon?",
"settings.localTipSet": "Nastav",
"settings.keyNoteAfter": "v",
"settings.keyNoteEnd": "a použij",
"settings.keyNoteEnd2": ".",
"settings.removeKeyConfirm":
"Odstranit klíč? Dokud klíč znovu nepřidáš, vrátí se celoobrazovková obrazovka pro nastavení klíče.",
"settings.removeKeyYes": "Ano, odstranit",
"settings.testConnection": "Otestovat připojení",
"settings.removeKey": "Odstranit klíč z prohlížeče",
"settings.savedTesting": "Uloženo — testuji…",
"settings.installApp": "Nainstalovat aplikaci",
"settings.addToHome": "Přidat Freezmo na domovskou obrazovku",
"settings.checking": "Kontroluji Ollamu…",
/* health ---------------------------------------------------------- */
"health.ok": "Ollama je dostupná a oba modely jsou k dispozici.",
"health.needsKey": "Čeká se na tvůj API klíč.",
"health.rejected": "Ollama odmítla tvůj API klíč.",
"health.modelMissing": "Ollama je dostupná, ale nakonfigurovaný model chybí.",
"health.unreachable": "Ollama není dostupná.",
"health.auth": "Autorizace",
"health.authUserKey": "Vlastní klíč",
"health.authServer": "Klíč serveru (.env)",
"health.authLocal": "Přihlášení místního daemonu",
"health.endpoint": "Endpoint",
"health.mode": "Režim",
"health.modeCloud": "Přímé cloudové API",
"health.modeLocal": "Místní daemon",
"health.key": "Klíč",
"health.keyMissing": "chybí",
"health.keyAccepted": "přijat",
"health.keyRejected": "odmítnut",
"health.keyNotNeeded": "není potřeba",
"health.keyUnknown": "neznámý",
"health.visionModel": "Vizuální model (čte fotky)",
"health.textModel": "Textový model (tvoří a známkuje)",
"health.available": "dostupný",
"health.notFound": "nenalezen",
"health.unknown": "neznámý",
"health.configuredAs": "nakonfigurováno jako {model}",
"health.checkedNow": "Zkontrolováno právě teď",
"health.checkedAt": "Zkontrolováno v {time}",
"health.createKey.before": "Vytvoř klíč na",
"health.createKey.after": "a vlož ho výše. Tip: s místním daemonem můžeš spustit",
"health.createKey.end": "a nastavit",
/* make my homework ------------------------------------------------- */
"homework.nav": "Udělej mi úkol",
"homework.title": "Udělej mi úkol",
"homework.subtitle":
"Vlož zadání, vyfoť ho nebo přidej dokument — a pak si vyber, jestli to mám rovnou udělat, nebo tě tím provést.",
"homework.source.title": "1. Zadání",
"homework.source.help": "Vlož jeho text, přetáhni fotky nebo přidej dokument.",
"homework.source.textLabel": "Text zadání",
"homework.source.placeholder": "Sem vlož zadání…",
"homework.source.add": "Přidat fotky nebo soubory",
"homework.source.drop": "Přetáhni zadání sem",
"homework.source.formats": "Text, fotky, PDF, DOCX, PPTX, ODT/ODP, TXT, MD",
"homework.source.ready": "Můžeme začít.",
"homework.source.need": "Nejdřív přidej zadání.",
"homework.source.tooManyPhotos": "Až {n} fotek na zadání.",
"homework.source.tooManyDocs": "Až {n} dokumentů na zadání.",
"homework.mode.title": "2. Co mám udělat?",
"homework.mode.make.title": "Jen to udělej",
"homework.mode.make.subtitle": "Hotový úkol, připravený ke zkopírování.",
"homework.mode.guide.title": "Vysvětli, jak to udělat",
"homework.mode.guide.subtitle": "Návod krok za krokem s nápovědami a rychlými kontrolami.",
"homework.start.make": "Udělej mi úkol",
"homework.start.guide": "Proveď mě tím",
"homework.loading.title": "Pracuji na zadání…",
"homework.loading.reading": "Čtu zadání…",
"homework.loading.vision2": "Přepisuji otázky…",
"homework.loading.writing": "Píšu tvůj úkol…",
"homework.loading.planning": "Plánuji postup…",
"homework.loading.gen2": "Kontroluji každou část…",
"homework.loading.gen3": "Už to bude…",
"homework.error.title": "To se nepovedlo",
"homework.error.body": "Při zpracování zadání se něco pokazilo.",
"homework.result.title": "Tvůj úkol",
"homework.result.copy": "Kopírovat",
"homework.result.copied": "Zkopírováno!",
"homework.result.copyFailed": "Kopírování se nepovedlo — vyber text a zkopíruj ho ručně.",
"homework.result.again": "Udělej to znovu",
"homework.result.new": "Nové zadání",
"homework.guide.stepOf": "Krok {n}/{total}",
"homework.guide.close": "Zavřít návod",
"homework.guide.tryIt": "Zkus si to",
"homework.guide.hint": "Ukaž nápovědu",
"homework.guide.hintMore": "Další nápověda",
"homework.guide.hintAnswer": "Ukaž odpověď",
"homework.guide.hintN": "Nápověda {n}",
"homework.guide.next": "Další krok",
"homework.guide.showSolution": "Ukaž řešení",
"homework.guide.solution": "Celé řešení",
"homework.guide.solutionNote":
"Složeno ze všech kroků — zkopíruj a odevzdej.",
"homework.history.title": "Nedávné úkoly",
"homework.history.made": "Hotové",
"homework.history.guide": "Návod",
"homework.history.openTitle": "Otevřít „{title}“",
"homework.history.deleteConfirm": "Smazat tento výsledek?",
/* quick actions (library empty state + /create switcher) ----------- */
"emptyStateCreateDeck": "Vytvořit interaktivní test",
"emptyStateHomework": "Pomoct s úkolem",
"actionStudyDeck": "Studijní balíček a testy",
"actionHomework": "Řešitel úkolů",
/* library --------------------------------------------------------- */
"library.title": "Tvoje mraznička",
"library.subtitle": "Sady zmrazené z tvých poznámek, uložené v tomto prohlížeči.",
"library.import": "Importovat JSON",
"library.folderCreated": "Složka vytvořena — sady, které zmrazíš, když je vybraná, přistanou tady.",
"library.imported": "Importováno „{title}“.",
"library.couldNotRead": "Tento soubor se nepodařilo přečíst.",
"library.folderDeleted": "Složka smazána — její sady se přesunuly do Nezařazených.",
"library.deckMoved": "Sada přesunuta.",
"library.deckMovedUnorganized": "Sada přesunuta do Nezařazených.",
"library.noDecks": "Zatím žádné sady",
"library.emptyBody": "Zmraz stránku poznámek do feedu kartiček a kvízů.",
"library.emptyHint": "S tvými vlastními modely Ollama —",
"library.emptySettings": "zkontroluj připojení v Nastavení",
"library.renameFolderAria": "Název složky",
"library.nothingUnfiled": "Teď tu nic nezařazeného není.",
"library.emptyFolder": "Prázdná složka — zmraz do ní sadu nebo ji sem přesuň z Nezařazených.",
"library.stats.best": "nejlepší {n} %",
"library.deleteWarning.one": "Sada uvnitř ({n}) se přesune do Nezařazených — nic se nesmaže.",
"library.deleteWarning.few": "Sady uvnitř ({n}) se přesunou do Nezařazených — nic se nesmaže.",
"library.deleteWarning.many": "Sad uvnitř ({n}) se přesune do Nezařazených — nic se nesmaže.",
"stats.decks.one": "{n} sada",
"stats.decks.few": "{n} sady",
"stats.decks.many": "{n} sad",
"stats.folders.one": "{n} složka",
"stats.folders.few": "{n} složky",
"stats.folders.many": "{n} složek",
"stats.activities.one": "{n} aktivita",
"stats.activities.few": "{n} aktivity",
"stats.activities.many": "{n} aktivit",
/* deck card ------------------------------------------------------- */
"deck.activities.one": "{n} aktivita",
"deck.activities.few": "{n} aktivity",
"deck.activities.many": "{n} aktivit",
"deck.runs.one": "{n} běh",
"deck.runs.few": "{n} běhy",
"deck.runs.many": "{n} běhů",
"deck.notPlayed": "Zatím nehráno",
"deck.best": "Nejlepší {n} %",
"deck.bestLabel": "Nejlepší",
"deck.study": "Studovat",
"deck.actions": "Akce sady",
"deck.titleLabel": "Název sady",
"deck.rename": "Přejmenovat",
"deck.export": "Exportovat JSON",
"deck.regenerate": "Znovu vytvořit sadu",
"deck.reallyDelete": "Opravdu smazat",
"deck.delete": "Smazat sadu",
"deck.newFolder": "+ Nová složka",
"deck.addMore": "Přidat další aktivity",
"deck.generate": "Vygenerovat",
"deck.noSource": "Tato sada nemá uložený zdrojový text (importované sady ho nenesou).",
"deck.noSourceExtend": "Tato sada nemá uložené zdrojové poznámky, takže ji nelze rozšířit.",
"deck.noSourceRegenerate": "Tato sada nemá uložené zdrojové poznámky, takže ji nelze znovu vytvořit.",
"deck.couldNotAdd": "Další aktivity se nepodařilo přidat.",
"deck.couldNotRegenerate": "Sadu se nepodařilo znovu vytvořit.",
"deck.regenerated": "Sada znovu vytvořena",
"deck.added.one": "Přidána {n} aktivita",
"deck.added.few": "Přidány {n} aktivity",
"deck.added.many": "Přidáno {n} aktivit",
"deck.addingActivities": "Přidávám aktivity",
"deck.regeneratingDeck": "Znovu vytvářím sadu",
"deck.addingNew": "Přidávám nové aktivity…",
"deck.regeneratingWhole": "Znovu vytvářím celou sadu…",
/* upload ---------------------------------------------------------- */
"upload.title": "Proměň poznámky ve studijní feed",
"upload.intro":
"Vyfoť nebo přetáhni fotky svých poznámek. Freezmo je přečte tvým vlastním vizuálním modelem Ollama a postaví prolistovatelnou sadu mini her.",
"upload.errNeedsKey": "Je vyžadován API klíč",
"upload.errRate": "Zpomal na chvíli",
"upload.errAuth": "Ollama odmítla tvůj klíč",
"upload.errFailed": "Vytváření selhalo",
"upload.openSettings": "Otevřít nastavení",
"upload.addPhotosAria": "Přidat fotky poznámek",
"upload.step1": "1. Přidej poznámky",
"upload.step1Help":
"Přetáhni soubory, vlož screenshot (Ctrl/⌘+V) nebo použij foťák. Fotky, PDF, Word, PowerPoint, OpenDocument a textové soubory. Až {images} fotek a {documents} dokumentů.",
"upload.chooseFiles": "Vybrat soubory",
"upload.camera": "Foťák",
"upload.dropHere": "Přetáhni poznámky sem",
"upload.formats": "Fotky, PDF, DOCX, PPTX, ODT/ODP, TXT, MD · obrázky se před odesláním zmenší na 1600 px",
"upload.remove": "Odebrat {name}",
"upload.addMorePhotos": "Přidat další fotky",
"upload.reading": "Čtu…",
"upload.noReadable": "Žádný čitelný text — není to sken bez textové vrstvy?",
"upload.words.one": "{n} slovo",
"upload.words.few": "{n} slova",
"upload.words.many": "{n} slov",
"upload.pages.one": "{n} strana",
"upload.pages.few": "{n} strany",
"upload.pages.many": "{n} stran",
"upload.pageImages.one": "{n} obrázek stránky",
"upload.pageImages.few": "{n} obrázky stránek",
"upload.pageImages.many": "{n} obrázků stránek",
"upload.truncated": "zkráceno",
"upload.photoSummary.one": "{n} fotka · {size} po komprimaci",
"upload.photoSummary.few": "{n} fotky · {size} po komprimaci",
"upload.photoSummary.many": "{n} fotek · {size} po komprimaci",
"upload.docSummary.one": "{n} dokument",
"upload.docSummary.few": "{n} dokumenty",
"upload.docSummary.many": "{n} dokumentů",
"upload.docImages.one": "{n} obrázek stránky",
"upload.docImages.few": "{n} obrázky stránek",
"upload.docImages.many": "{n} obrázků stránek",
"upload.skipImages.one": "{n} obrázek bude vynechán (max {max} na sadu)",
"upload.skipImages.few": "{n} obrázky budou vynechány (max {max} na sadu)",
"upload.skipImages.many": "{n} obrázků bude vynecháno (max {max} na sadu)",
"upload.compressing": "Komprimuji fotky…",
"upload.step2": "2. Nepovinné doplňky",
"upload.step2Help": "Vlož další poznámky nebo AI napověz, na co se zaměřit.",
"upload.pastedNotes": "Vložené poznámky nebo text",
"upload.pastedPlaceholder": "Vlož sem text z přednášky, shrnutí nebo další fakta…",
"upload.topicHint": "Nápověda k tématu (nepovinné)",
"upload.topicPlaceholder": "např. zaměř se na vzorce nebo na data",
"upload.step3": "3. Obtížnost a velikost",
"upload.freezeInto": "Zmrazit do složky",
"upload.folderPlaceholder": "např. Dějepis",
"upload.difficultyAria": "Obtížnost",
"upload.easy": "Snadná",
"upload.easyBlurb": "Vybavování a rozpoznávání",
"upload.medium": "Střední",
"upload.mediumBlurb": "Porozumění a použití",
"upload.hard": "Těžká",
"upload.hardBlurb": "Analýza a záludnosti",
"upload.activities": "Aktivity",
"upload.readingDocs": "Čtu dokumenty…",
"upload.ready": "Můžeme začít — vytváření může chvíli trvat.",
"upload.addToStart": "Začni přidáním fotky, dokumentu nebo textu.",
"upload.freezeDeck": "Zmrazit sadu",
"upload.generationProgress": "Průběh vytváření",
"upload.cancel": "Zrušit",
"upload.unsupported":
"„{name}“ není podporovaný formát. Přidej fotky nebo soubor PDF, DOCX, PPTX, ODT/ODP, TXT či MD.",
"upload.tooManyDocs": "Až {n} dokumentů na sadu.",
"upload.tooManyPhotos": "Až {n} fotek na sadu.",
"upload.couldNotReadFile": "Soubor „{name}“ se nepodařilo přečíst.",
"upload.couldNotReadDoc": "Tento soubor se nepodařilo přečíst.",
"upload.genericError":
"Při vytváření sady se něco pokazilo. Zkontroluj nastavení Ollamy a zkus to znovu.",
"upload.warming": "Zahřívám…",
"upload.vision1": "Čtu poznámky…",
"upload.vision2": "Přepisuji rukopis…",
"upload.vision3": "Hledám definice…",
"upload.gen1": "Hledám klíčové myšlenky…",
"upload.gen2": "Píšu dobré otázky…",
"upload.gen3": "Stavím aktivity…",
"upload.tip1": "Tip: prolistuj sadu nahoru a dolů jako feed.",
"upload.tip2": "Tip: přiřazovací otázky dají plné XP jen za perfektní shodu.",
"upload.tip3": "Tip: sady se ukládají v tomto prohlížeči — účet není potřeba.",
"upload.tip4": "Tip: exportuj sadu jako JSON a pošli ji kamarádovi.",
/* study feed ------------------------------------------------------ */
"feed.back": "Zpět do knihovny",
"feed.activityOf": "Aktivita {n} z {total}",
"feed.streak": "Série odpovědí: {n}",
"feed.xp": "Získané XP: {n}",
"feed.retry": "Opakování · ",
"feed.swipeUp": "Přejeď nahoru na další aktivitu ↑",
"feed.prev": "Předchozí aktivita",
"feed.next": "Další aktivita",
"feed.results": "Výsledky",
/* results --------------------------------------------------------- */
"results.perfect": "Perfektní zmrazení! 🧊",
"results.nice": "Pěkně!",
"results.start": "Dobrý začátek — jen dál!",
"results.acedAll": "Zvládl jsi všech {n} aktivit v sadě „{title}“.",
"results.gotOf": "Máš {correct} z {total} správně{retry}.",
"results.retrySuffix": " v opakování",
"results.xpEarned": "Získáno {n} XP",
"results.bestStreak": "Nejlepší série {n}",
"results.difficulty": "Obtížnost: {level}",
"results.best": "🏆 Nejlepší {n} %",
"results.missed": "Chybné položky ({n})",
"results.yourAnswer": "Tvoje odpověď: {answer}",
"results.answer": "Odpověď:",
"results.retryMissed": "Zopakovat chybné ({n})",
"results.studyAgain": "Studovat sadu znovu",
"results.studyWhole": "Studovat celou sadu znovu",
"results.regenerate": "Znovu vytvořit sadu",
"results.regenerating": "Znovu vytvářím…",
"results.export": "Exportovat JSON",
"results.score": "skóre",
"results.true": "Pravda",
"results.false": "Nepravda",
/* gate ------------------------------------------------------------ */
"gate.title": "Připoj svůj klíč Ollama",
"gate.subtitle": "Jeden krok k tvé první zmrazené sadě.",
"gate.apiKeyLabel": "API klíč Ollama",
"gate.placeholder": "Vlož svůj API klíč Ollama",
"gate.hide": "Skrýt API klíč",
"gate.show": "Zobrazit API klíč",
"gate.checking": "Kontroluji…",
"gate.continue": "Pokračovat",
"gate.howGet": "Jak získám klíč?",
"gate.howGetAria": "Jak získám API klíč Ollama?",
"gate.stays": "Tvůj klíč zůstává v tomto prohlížeči. Náš server ho nikdy neukládá.",
"gate.helpTitle": "Jak získat API klíč Ollama",
"gate.freeTier.before": "Potřebuješ jen",
"gate.freeTier.strong": "bezplatnou cloudovou úroveň",
"gate.freeTier.after": ". Žádná platba ani kreditní karta.",
"gate.step1.before": "Přejdi na",
"gate.step1.after": "a přihlas se, nebo si vytvoř zdarma účet.",
"gate.step2.before": "Otevři Settings → API keys na",
"gate.step2.after": ".",
"gate.step3.before": "Klikni na",
"gate.step3.strong": "„Create new key“",
"gate.step3.after": ", dej mu libovolný název a zkopíruj ho.",
"gate.step4": "Vlož ho do Freezma a stiskni Pokračovat.",
"gate.footnote":
"Bezplatná úroveň na vytváření sad stačí. Klíč můžeš kdykoli změnit nebo odebrat v Nastavení.",
"gate.gotIt": "Rozumím",
/* install sheet --------------------------------------------------- */
"install.titleIos": "Nainstalovat Freezmo na iPhone",
"install.titleAndroid": "Nainstalovat Freezmo na Android",
"install.install": "Nainstalovat",
"install.installing": "Instaluji…",
"install.orManually": "Nebo nainstaluj ručně:",
"install.maybeLater": "Možná později",
"install.openIn": "Otevři tuto stránku v",
"install.tap": "Klepni na",
"install.tapConfirm": "Klepnutím na",
"install.inBrowserBar": "v liště prohlížeče",
"install.then": "a pak na",
"install.choose": "Zvol",
"install.toConfirm": "potvrď",
"install.or": "nebo",
"install.inTopRight": "vpravo nahoře v prohlížeči",
"install.moreOptions": "Další možnosti",
"install.share": "Sdílet",
"install.more": "Další",
"install.addToHome": "Přidat na domovskou obrazovku",
"install.add": "Přidat",
"install.installApp": "Nainstalovat aplikaci",
"install.addToHomeScreen": "Přidat na domovskou obrazovku",
/* deck page ------------------------------------------------------- */
"deckPage.loading": "Načítám sadu…",
"deckPage.notFound": "Sada nenalezena",
"deckPage.notFoundBody":
"Tato sada není v knihovně tohoto prohlížeče. Možná byla smazána, nebo vznikla v jiném profilu prohlížeče.",
"deckPage.back": "Zpět do knihovny",
"deckPage.create": "Vytvořit sadu",
/* offline --------------------------------------------------------- */
"offline.title": "Jsi offline",
"offline.body":
"Freezmo potřebuje připojení k vytváření sad a známkování odpovědí. Uložené sady máš pořád v tomto prohlížeči — připoj se a načti stránku znovu.",
/* activity types -------------------------------------------------- */
"type.flashcard": "Kartička",
"type.multipleChoice": "Výběr z možností",
"type.trueFalse": "Pravda nebo nepravda",
"type.fillBlank": "Doplň slovo",
"type.matching": "Přiřaď k sobě",
"type.ordering": "Seřaď",
"type.shortAnswer": "Krátká odpověď",
/* activities shared ----------------------------------------------- */
"activity.answerOptions": "Možnosti odpovědi",
"activity.wordBank": "Nabídka slov",
"activity.niceOne": "Pěkně!",
"activity.notQuite": "Skoro",
"activity.why": "Proč:",
"activity.seeResults": "Zobrazit výsledky",
"activity.next": "Další",
"activity.correctAnswer": "Správná odpověď:",
"activity.unsupported": "Nepodporovaná aktivita: {json}",
/* flashcard ------------------------------------------------------- */
"flashcard.tapToFlip": "Klepnutím na kartu ji otočíš.",
"flashcard.didYouKnow": "Věděl jsi to ještě před otočením?",
"flashcard.question": "Otázka",
"flashcard.answer": "Odpověď",
"flashcard.tapFlip": "klepni pro otočení ↻",
"flashcard.flipBack": "Otočit zpět na otázku",
"flashcard.reveal": "Odhalit odpověď",
"flashcard.notYet": "😅 Ještě ne",
"flashcard.knewIt": "🧊 Věděl jsem to",
"flashcard.frozenSolid": "Zmrazeno na pevno!",
"flashcard.keepPile": "Nech to na hromádce",
/* multiple choice ------------------------------------------------ */
"mc.tipKeys": "Tip: stiskni 1–4 na klávesnici.",
/* true/false ------------------------------------------------------ */
"tf.hint": "Přejeď kartu doprava pro pravdu, doleva pro nepravdu — nebo klepni na tlačítko.",
"tf.niceSwipe": "Pěkný přejezd!",
"tf.trueLabel": "✓ pravda",
"tf.falseLabel": "nepravda ✕",
"tf.falseBtn": "✕ Nepravda",
"tf.trueBtn": "✓ Pravda",
"tf.statementIs.before": "Toto tvrzení je",
"tf.true": "pravda",
"tf.false": "nepravda",
/* fill blank ------------------------------------------------------ */
"fill.typeMissing": "Napiš chybějící slovo",
"fill.placeholder": "napiš to",
"fill.check": "Zkontrolovat odpověď",
"fill.hint": "Stiskni Enter pro kontrolu. Malé překlepy se prominou.",
"fill.expected": "Očekáváno:",
"fill.youWrote": "— napsal jsi",
/* matching -------------------------------------------------------- */
"matching.matched": "{n}/{total} přiřazeno",
"matching.allMatched": "Vše přiřazeno!",
"matching.tapTerm": "Klepni vlevo na pojem a pak vpravo na jeho definici.",
"matching.tapDef": "Teď klepni na odpovídající definici →",
"matching.perfect": "Perfektní přiřazení!",
"matching.slips": "Přiřazeno — s pár chybami",
"matching.firstTry": "Vše jsi přiřadil na první pokus.",
"matching.wrongAttempts.one": "Udělal jsi {n} chybný pokus.",
"matching.wrongAttempts.few": "Udělal jsi {n} chybné pokusy.",
"matching.wrongAttempts.many": "Udělal jsi {n} chybných pokusů.",
/* ordering -------------------------------------------------------- */
"ordering.hint": "Přetahuj karty nebo použij ▲▼ a pak zkontroluj pořadí.",
"ordering.correctOrder": "Toto je správné pořadí:",
"ordering.check": "Zkontrolovat pořadí",
"ordering.moveUp": "Posunout „{item}“ nahoru",
"ordering.moveDown": "Posunout „{item}“ dolů",
/* short answer ---------------------------------------------------- */
"short.placeholder": "Napiš odpověď vlastními slovy…",
"short.ctrlEnter": "Ctrl/⌘ + Enter pro odeslání.",
"short.submit": "Odeslat odpověď",
"short.grading": "Známkuji tvoji odpověď…",
"short.couldNotGrade": "Tuto odpověď nešlo oznámkovat",
"short.showAnswer": "Zobrazit odpověď a sebehodnocení",
"short.modelAnswer": "Vzorová odpověď",
"short.keyPoints": "Klíčové body:",
"short.missedIt": "😅 Netrefil jsem se",
"short.hadRight": "🧊 Měl jsem to správně",
"short.accepted": "Přijato",
"short.needsWork": "Ještě zapracovat",
"short.selfChecked": "Sebehodnocení — tuhle odpověď jsi posoudil sám.",
"short.yourAnswer": "Tvoje odpověď",
"short.gradeFailed": "Známkování selhalo. Zkontroluj, že je Ollama dostupná, a zkus to znovu.",
/* client API errors ----------------------------------------------- */
"api.cancelled": "Vytváření bylo zrušeno.",
"api.unreachable": "Nepodařilo se připojit k serveru Freezmo. Běží ještě?",
"api.unreachableShort": "Nepodařilo se připojit k serveru Freezmo.",
"api.httpError": "Server vrátil HTTP {status}.",
"api.empty": "Server poslal prázdnou odpověď.",
"api.almost": "Už to bude…",
"api.noDeck": "Ze serveru nepřišla žádná sada. Zkontroluj nastavení Ollamy a zkus to znovu.",
"api.incomplete": "Server skončil, aniž by poslal sadu.",
"api.noActivities": "Vygenerovaná sada neměla žádné použitelné aktivity. Zkus to znovu.",
"api.gradingCancelled": "Známkování bylo zrušeno.",
"api.gradingFailed": "Známkování selhalo s HTTP {status}.",
"api.healthFailed": "Kontrola stavu selhala s HTTP {status}.",
"api.healthUnexpected": "Server Freezmo poslal neočekávanou odpověď o stavu.",
"api.serverUnexpected": "Server Freezmo odpověděl neočekávaně. Zkus to za chvíli znovu.",
"api.keyRejected": "Ollama tento klíč odmítla. Zkontroluj překlepy, nebo si vytvoř nový na ollama.com/settings/keys.",
"api.ollamaUnreachable": "Teď se nepodařilo připojit k Ollamě. Zkontroluj připojení a zkus to znovu.",
/* server-side messages (API routes) -------------------------------- */
"server.bodyNotJson": "Tělo požadavku nebylo platné JSON.",
"server.invalidRequest": "Neplatný požadavek: {detail}",
"server.needContent": "Přidej aspoň jednu fotku poznámek nebo vlož text, ze kterého sada vznikne.",
"server.needAssignment":
"Nejdřív přidej zadání — vlož jeho text, vyfoť ho nebo přidej dokument.",
"server.invalidGrade": "Neplatný požadavek na známkování.",
"server.bodyTooLarge": "Tělo požadavku je příliš velké ({size}). Limit je {limit}.",
"server.invalidOrigin": "Neplatná hlavička Origin.",
"server.crossOrigin": "Požadavky z jiné domény nejsou povoleny.",
"server.rateLimited": "Příliš mnoho požadavků — aplikace povoluje {limit} za okno. Zkus to znovu za {retry} s.",
"server.authMissingKey":
"Nejdřív přidej svůj API klíč Ollama — otevři nastavení (⚙️) a vlož klíč z ollama.com/settings/keys.",
"server.authServerKeyMissing":
"Tento server má používat vlastní API klíč Ollama (OLLAMA_AUTH=server), ale OLLAMA_API_KEY je v .env.local prázdný.",
"server.authLocalCloud":
"Tento server má OLLAMA_AUTH=local, ale míří na https://ollama.com. Přihlášený místní daemon se s cloudovou adresou použít nedá — nastav OLLAMA_AUTH=user-key, nebo přepiš OLLAMA_BASE_URL na http://localhost:11434.",
"server.ollamaUnreachable":
"Ollama není dostupná na {url}. Zkontroluj nastavení a jestli daemon běží.",
"server.ollamaTimeout": "Ollamě to trvalo příliš dlouho. Zkus méně nebo menší obrázky, rychlejší model, případně to zopakuj.",
"server.ollamaAuth":
"Ollama požadavek odmítla. Pokud používáš https://ollama.com, zkontroluj OLLAMA_API_KEY (vytvoř si ho na ollama.com/settings/keys).",
"server.ollamaUnexpected": "Ollama vrátila neočekávanou odpověď.",
"server.ollamaGeneric": "Při komunikaci s Ollamou se něco pokazilo.",
"server.ollamaTimeoutList": "Vypršel čas při načítání modelů Ollama.",
"server.ollamaUnauthorised": "Ollama požadavek odmítla (neautorizováno).",
"server.ollamaNotJson": "Ollama poslala odpověď, která nebyla JSON.",
"server.ollamaNoContent": "Ollama poslala odpověď bez obsahu zprávy.",
"server.healthNoKey":
"Zatím žádný API klíč Ollama. Přidej klíč v nastavení — ukládá se jen v tomto prohlížeči.",
"server.healthKeyRejected": "Ollama tento API klíč odmítla. Vytvoř si nový na ollama.com/settings/keys a vlož ho znovu.",
"server.healthNoCredits":
"Klíč funguje, ale {model} není součástí tarifu tohoto účtu. Přidej kredity, nebo v .env.local přepni na model zdarma.",
"server.healthModelNoteLocal": "Není v seznamu modelů daemonu. Spusť `ollama pull {model}` nebo zkontroluj přihlášení.",
"server.healthModelNoteCloud": "Není v odpovědi {url}/api/tags. Zkontroluj název modelu a tarif klíče.",
"server.healthModelsMissing": "Ollama je dostupná, ale aspoň jeden nakonfigurovaný model není k dispozici.",
/* folders / files -------------------------------------------------- */
"folder.nameRequired": "Dej složce název.",
"folder.nameTooLong": "Ten název je trochu dlouhý (max 60 znaků).",
"folder.nameTaken": "Jiná složka už tenhle název má.",
"file.notImage": "„{name}“ není obrázek.",
"file.imageTooLarge": "„{name}“ je větší než 20 MB.",
"file.notDocument": "„{name}“ není podporovaný dokument (použij PDF, DOCX, PPTX, ODT/ODP nebo text).",
"file.documentTooLarge": "„{name}“ je větší než 40 MB.",
"import.notJson": "Tento soubor není platný JSON.",
"import.notDeck": "Tento soubor nevypadá jako sada Freezmo.",
"doc.readingPage": "Čtu stranu {n} z {total}…",
"doc.readingWord": "Čtu dokument Word…",
"doc.readingSlides": "Čtu snímky…",
"doc.readingOpen": "Čtu soubor OpenDocument…",
"doc.readingText": "Čtu textový soubor…",
};
/** Used by tests/tools that want the raw tables. */
export const dictionaries = { en, cs };