A user got stuck on the wizard's loading screen: a hung model meant silence, and silence was indistinguishable from work - the server's own hard deadline is 2x OLLAMA_TIMEOUT_MS (ten minutes by default), and the client waited on a dead stream indefinitely. Both generate routes (deck, homework) now send an NDJSON heartbeat every 15s, and the client guards the two quiet phases: no response at all for 45s, and no bytes for 90s mid-stream, both surfacing the regular error state with a Retry. The caller's abort still maps to the friendly "cancelled" state. Verified with a stalled route and Playwright's fake clock in the wizard suite (93 checks), plus contrast 0 findings, mobile 124/124, chat 101/101, regression 44/44, i18n 13/13, native 33/33. Version 0.4.0, SW -> freezmo-v69.
1977 lines
99 KiB
TypeScript
1977 lines
99 KiB
TypeScript
/**
|
||
* 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.back": "Back",
|
||
"common.next": "Next",
|
||
"common.more": "More",
|
||
"common.less": "Less",
|
||
"common.edit": "Edit",
|
||
"common.stepOf": "{n} / {total}",
|
||
"common.aiDisclaimer": "Freezmo uses AI and can make mistakes. Double-check important info.",
|
||
|
||
/* header ---------------------------------------------------------- */
|
||
"header.home": "Freezmo home",
|
||
"header.tagline": "study feed",
|
||
"header.openSettings": "Open settings and Ollama status",
|
||
|
||
/* tab bar --------------------------------------------------------- */
|
||
"tabbar.main": "Main",
|
||
/* Short tab labels: they must stay on one line at 360px in both languages. */
|
||
"tab.library": "Library",
|
||
"tab.progress": "Progress",
|
||
"tab.new": "New deck",
|
||
"tab.exam": "Exam",
|
||
"tab.settings": "Settings",
|
||
|
||
/* sidebar --------------------------------------------------------- */
|
||
"sidebar.collapse": "Collapse sidebar",
|
||
"sidebar.expand": "Expand sidebar",
|
||
"sidebar.library": "Library",
|
||
"sidebar.label": "Sidebar",
|
||
|
||
/* 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…",
|
||
"settings.connection": "Connection",
|
||
"settings.connected": "Connected",
|
||
"settings.modelsReady.one": "{n} model ready",
|
||
"settings.modelsReady.few": "{n} models ready",
|
||
"settings.modelsReady.many": "{n} models ready",
|
||
"settings.details": "Details",
|
||
"settings.privacyLine": "Your key stays in this browser.",
|
||
"settings.recheck": "Re-check connection",
|
||
"settings.haptics": "Haptics",
|
||
"settings.hapticsNote": "Vibration feedback on supported phones.",
|
||
|
||
/* data, backup & sync --------------------------------------------- */
|
||
"settings.data": "Data & backup",
|
||
"settings.dataNote": "Everything lives in this browser. Export a backup regularly.",
|
||
"settings.exportAll": "Export all data",
|
||
"settings.exportNote": "Decks, homework, progress and settings — never your API key.",
|
||
"settings.importBackup": "Import backup",
|
||
"settings.importPreview": "This backup has {decks} decks, {homework} homework results, {folders} folders and {chats} chats.",
|
||
"settings.importMergeNote": "Merge keeps what's here and adds the backup; matching IDs are replaced.",
|
||
"settings.importReplaceNote": "Replace wipes this browser's data and uses the backup instead.",
|
||
"settings.importMerge": "Merge",
|
||
"settings.importReplace": "Replace",
|
||
"settings.importCancel": "Cancel",
|
||
"settings.importDone": "Imported {decks} decks and {homework} homework results.",
|
||
"settings.importFailed": "That backup could not be imported.",
|
||
"settings.storage": "Storage",
|
||
"settings.storagePersistent": "Persistent — the browser won't evict your data.",
|
||
"settings.storageBestEffort": "Best-effort — the browser may clear data under pressure.",
|
||
"settings.storageRequest": "Ask for persistence",
|
||
"settings.storageGranted": "Granted — your data is now persistent.",
|
||
"settings.storageDenied": "The browser declined. Installing the app usually helps.",
|
||
"settings.backupNudgeTitle": "Back up your decks?",
|
||
"settings.backupNudgeBody": "You've added decks since your last backup. A backup keeps them safe.",
|
||
"settings.backupNow": "Back up now",
|
||
"settings.backupLater": "Not now",
|
||
"settings.sync": "Sync",
|
||
"settings.syncNote":
|
||
"Off by default. Data is encrypted in this browser before upload — the server stores an opaque blob and never sees your data or key.",
|
||
"settings.syncEnable": "Turn on sync",
|
||
"settings.syncPassphrase": "Sync passphrase",
|
||
"settings.syncPassphraseConfirm": "Repeat the passphrase",
|
||
"settings.syncPassphraseMismatch": "The passphrases don't match.",
|
||
"settings.syncPassphraseShort": "Use at least 8 characters.",
|
||
"settings.syncStart": "Start syncing",
|
||
"settings.syncCancel": "Cancel",
|
||
"settings.syncId": "Sync ID",
|
||
"settings.syncCopy": "Copy",
|
||
"settings.syncCopied": "Copied.",
|
||
"settings.syncLast": "Last synced {when}",
|
||
"settings.syncNever": "Not synced yet.",
|
||
"settings.syncNow": "Sync now",
|
||
"settings.syncWorking": "Syncing…",
|
||
"settings.syncDone": "Up to date.",
|
||
"settings.syncPushed": "Uploaded from this device.",
|
||
"settings.syncPulled": "Downloaded from the synced copy.",
|
||
"settings.syncConflict": "This device and the synced copy both changed. Choose which one to keep.",
|
||
"settings.syncKeepLocal": "Keep this device",
|
||
"settings.syncKeepRemote": "Use synced copy",
|
||
"settings.syncJoin": "Join another sync",
|
||
"settings.syncJoinNote":
|
||
"Enter the sync ID and passphrase from your other device. Data in this browser is replaced by the synced copy.",
|
||
"settings.syncJoinId": "Sync ID",
|
||
"settings.syncJoinAction": "Join",
|
||
"settings.syncLocked": "Enter your sync passphrase to unlock syncing on this device.",
|
||
"settings.syncUnlock": "Unlock",
|
||
"settings.syncForget": "Forget passphrase on this device",
|
||
"settings.syncDelete": "Delete synced data",
|
||
"settings.syncDeleteConfirm": "Delete the synced copy from the server? This device keeps its data.",
|
||
"settings.syncDeleteYes": "Delete",
|
||
"settings.syncWrongPass": "That passphrase can't unlock the synced copy.",
|
||
"settings.syncError": "Sync failed. Check your connection and try again.",
|
||
"settings.syncOff": "Sync is off.",
|
||
"settings.syncOn": "Sync is on.",
|
||
"settings.syncNoteKey": "Your API key is never synced.",
|
||
|
||
/* offline / share / toasts ---------------------------------------- */
|
||
"offline.banner": "You're offline — saved decks still work.",
|
||
"offline.generate": "You're offline — connect to generate.",
|
||
"share.title": "Add to Freezmo",
|
||
"share.intro": "What should I do with this?",
|
||
"share.preview": "Shared content",
|
||
"share.loading": "Loading the shared content…",
|
||
"share.expired": "That share expired — try again.",
|
||
"share.tooLarge": "That's too large to share (max 30 MB).",
|
||
"share.nothing": "Nothing to add.",
|
||
"share.copied": "Copied to the clipboard.",
|
||
"share.failed": "Sharing isn't available here.",
|
||
"toast.deckDeleted": "Deck deleted",
|
||
"toast.fileRemoved": "File removed",
|
||
"toast.undo": "Undo",
|
||
"upload.pasteClipboard": "Paste from clipboard",
|
||
"upload.clipboardDenied": "Clipboard access was denied.",
|
||
|
||
/* 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": "New homework",
|
||
"wizard.mode.deck": "Study deck",
|
||
"wizard.mode.homework": "Make my homework",
|
||
"wizard.mode.aria": "Study deck or homework",
|
||
"homeworkHeroTitle": "Make my homework",
|
||
"homework.subtitle": "Paste it, photograph it or add a file — then pick how I should help.",
|
||
"homework.phone.assignment": "The assignment",
|
||
"homework.phone.mode": "How should I help?",
|
||
"homework.phone.modeDesc": "Pick how I should help with this assignment.",
|
||
"homework.phone.preview": "Assignment preview",
|
||
"homework.phone.startHint": "Tap a card to start.",
|
||
"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.reading": "Reading the assignment…",
|
||
"homework.loading.readingSolution": "Reading your solution…",
|
||
"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.report": "Report exercise",
|
||
"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?",
|
||
"homework.emptyTitle": "No homework yet",
|
||
"homework.emptyBody": "Solved assignments and step-by-step guides show up here.",
|
||
"homework.open": "Open",
|
||
"homework.steps.one": "{n} step",
|
||
"homework.steps.few": "{n} steps",
|
||
"homework.steps.many": "{n} steps",
|
||
|
||
/* library tabs ----------------------------------------------------- */
|
||
"tabDecks": "Study Decks",
|
||
"tabHomework": "Homework",
|
||
|
||
/* quick actions (library empty state + /create switcher) ----------- */
|
||
"emptyStateCreateDeck": "Create study deck",
|
||
"emptyStateHomework": "New homework",
|
||
|
||
/* library --------------------------------------------------------- */
|
||
"library.title": "Your icebox",
|
||
"library.subtitle": "Decks frozen from your notes, saved in this browser.",
|
||
"library.import": "Import JSON",
|
||
"library.moreActions": "More actions",
|
||
"library.moreTitle": "Library actions",
|
||
"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 flashcards and quizzes.",
|
||
"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.share": "Share",
|
||
"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.introShort": "Add your notes — I'll build the deck.",
|
||
"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.addPhotosFiles": "Add photos or files",
|
||
"upload.pasteInstead": "Paste text instead",
|
||
"upload.focusExtra": "Add a focus or extra notes (optional)",
|
||
"upload.phone.notes": "Your notes",
|
||
"upload.phone.notesDesc": "Photos, PDF, Word or text — up to {images} photos and {documents} files.",
|
||
"upload.phone.options": "Options",
|
||
"upload.phone.optionsDesc": "Everything has a sensible default — change what you like.",
|
||
"upload.phone.summary": "Ready to freeze",
|
||
"upload.phone.summaryDesc": "Check what goes in, then freeze.",
|
||
"upload.phone.difficultyHelp": "How deep the questions go.",
|
||
"upload.phone.activitiesHelp": "How many activities to build.",
|
||
"upload.phone.folderHelp": "Where the deck lands in your library.",
|
||
"upload.nothingAdded": "Nothing added yet",
|
||
"upload.summary.files": "Files",
|
||
"upload.summary.folder": "Folder",
|
||
"upload.summaryText": "Text",
|
||
"upload.photoCount.one": "{n} photo",
|
||
"upload.photoCount.few": "{n} photos",
|
||
"upload.photoCount.many": "{n} photos",
|
||
"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",
|
||
"feed.report": "Report question",
|
||
"feed.reported": "Question reported — it won't show up again.",
|
||
|
||
/* 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.stalled": "The model stopped responding. Check it and try again.",
|
||
"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.busy": "Freezmo is already working on something — try again in a moment.",
|
||
"server.pushTooMany": "This server already stores {max} reminder devices — remove one first.",
|
||
"server.pushEndpoint": "That is not a push service this server is allowed to send to.",
|
||
"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.",
|
||
"server.usageLimit":
|
||
"Your free monthly Ollama usage has run out. Upgrade your Ollama account, or create a new one and use the new API key.",
|
||
"server.ollamaThrottled": "Ollama is temporarily rate limiting requests. Try again in a moment.",
|
||
"server.syncInvalidId": "That sync ID doesn't look right.",
|
||
"server.syncTooLarge": "This sync upload is too large.",
|
||
"server.syncStorageFull": "The sync storage is full. Delete some synced data and try again.",
|
||
"server.syncNotFound": "No synced data found for that ID.",
|
||
"server.pushDisabled": "Push reminders are not configured on this server (set VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY).",
|
||
"server.pushNotSubscribed": "This browser is not subscribed to reminders.",
|
||
"server.pushGone": "That subscription is no longer valid — reminders were turned off for it.",
|
||
"server.pushSendFailed": "The reminder could not be sent.",
|
||
"server.pushReminderTitle": "Time to review",
|
||
"server.pushReminderBody": "Your decks are waiting — a few due cards keep the streak alive.",
|
||
|
||
/* 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.",
|
||
"import.notBackup": "That file doesn't look like a Freezmo backup.",
|
||
"error.usageTitle": "Free monthly usage used up",
|
||
"error.usageBody":
|
||
"You've run out of your free monthly Ollama usage. Upgrade your Ollama account, or create a new one and use the new API key.",
|
||
"error.openOllama": "Open Ollama",
|
||
"error.throttledTitle": "Ollama is busy",
|
||
"error.throttledBody": "Ollama is temporarily rate limiting requests. Try again in a moment.",
|
||
|
||
/* learning: review, mistakes, stats, exam -------------------------- */
|
||
"review.cardTitle": "Smart review",
|
||
"review.title": "Review due",
|
||
"review.mistakesTitle": "Mistakes",
|
||
"review.mistakesDeck": "Mistakes in this deck",
|
||
"review.nothingDue": "Nothing is due right now. Study a deck and the schedule fills up on its own.",
|
||
"review.noMistakes": "No open mistakes — everything you got wrong has since been answered correctly.",
|
||
"review.done": "Back to the Library",
|
||
"review.seeResults": "See results",
|
||
"review.finishFirst": "Finish the last activity to see your results.",
|
||
"review.results": "Results",
|
||
"review.preparing": "Preparing your review…",
|
||
"review.due.one": "{n} due",
|
||
"review.due.few": "{n} due",
|
||
"review.due.many": "{n} due",
|
||
"review.mistakes.one": "{n} mistake",
|
||
"review.mistakes.few": "{n} mistakes",
|
||
"review.mistakes.many": "{n} mistakes",
|
||
"exam.title": "Exam mode",
|
||
"exam.preparing": "Preparing exam mode…",
|
||
"exam.subtitle": "Pick one or more decks, set a timer, no hints — see how you'd do.",
|
||
"exam.pickDecks": "Decks",
|
||
"exam.noDecks": "Create a deck first.",
|
||
"exam.questions": "Questions",
|
||
"exam.timer": "Timer",
|
||
"exam.timerOff": "Off",
|
||
"exam.start": "Start exam ({n} questions)",
|
||
"exam.results": "Exam results",
|
||
"exam.byDeck": "By deck",
|
||
"exam.byTopic": "By topic",
|
||
"exam.studyMistakes": "Study mistakes",
|
||
"exam.timeLeft": "{time} left",
|
||
"stats.title": "Your progress",
|
||
"stats.preparing": "Preparing your progress…",
|
||
"stats.subtitle": "Streaks, accuracy and the topics that deserve another pass.",
|
||
"stats.streak": "Day streak",
|
||
"stats.today": "Today",
|
||
"stats.todayOf": "{n} / {goal} answers",
|
||
"stats.goalDone": "Daily goal done 🎉",
|
||
"stats.accuracy": "Accuracy",
|
||
"stats.answers.one": "{n} answer",
|
||
"stats.answers.few": "{n} answers",
|
||
"stats.answers.many": "{n} answers",
|
||
"stats.calendar": "Last 5 weeks",
|
||
"stats.bySubject": "By subject",
|
||
"stats.byDeck": "By deck",
|
||
"stats.weakest": "Weakest topics",
|
||
"stats.noSubject": "No subject",
|
||
"stats.correctOf": "{correct} / {total} correct",
|
||
"stats.empty": "No answers yet",
|
||
"stats.emptyBody": "Study a deck and your progress shows up here.",
|
||
"stats.start": "Study a deck",
|
||
"stats.legendLess": "Less",
|
||
"stats.legendMore": "More",
|
||
"nav.stats": "Progress",
|
||
"nav.exam": "Exam mode",
|
||
"settings.learning": "Learning",
|
||
"settings.dailyGoal": "Daily goal",
|
||
"settings.dailyGoalNote": "Answers per day that count as done.",
|
||
"settings.reminders": "Daily reminder",
|
||
"settings.remindersNote":
|
||
"A gentle nudge when your review is due. Needs the installed app — on iPhone that is required.",
|
||
"settings.reminderEnable": "Turn on reminders",
|
||
"settings.reminderDisable": "Turn off",
|
||
"settings.reminderTest": "Send a test",
|
||
"settings.reminderOn": "Reminders are on.",
|
||
"settings.reminderOff": "Reminders are off.",
|
||
"settings.reminderDenied": "Notifications are blocked in your browser settings.",
|
||
"settings.reminderError": "Could not set up reminders.",
|
||
"library.learningToday": "Today: {n} / {goal}",
|
||
|
||
/* homework: check my work + follow-ups ----------------------------- */
|
||
"homework.mode.check": "Check my work",
|
||
"homework.mode.checkDesc": "Mark your own attempt: what's right, what's wrong, and the corrected version.",
|
||
"homework.solution.label": "Your solution",
|
||
"homework.solution.placeholder": "Paste your own attempt here — the assignment is above.",
|
||
"homework.solution.note": "Type or paste your solution, or add photos and documents of your work — handwritten pages are fine.",
|
||
"homework.solution.add": "Add photos or files",
|
||
"homework.solution.tooManyPhotos": "Up to {n} photos per solution.",
|
||
"homework.solution.tooManyDocs": "Up to {n} documents per solution.",
|
||
"homework.solution.required": "Add your solution so there is something to check.",
|
||
"homework.check.title": "Checked",
|
||
"homework.check.verdict": "Verdict",
|
||
"homework.check.correct": "What's right",
|
||
"homework.check.wrong": "What needs fixing",
|
||
"homework.check.fix": "Fix",
|
||
"homework.check.corrected": "Corrected version",
|
||
"homework.history.checked": "Checked",
|
||
"homework.loading.checking": "Checking your attempt…",
|
||
"homework.followup.shorter": "Shorter",
|
||
"homework.followup.more": "More detail",
|
||
"homework.followup.explain": "Explain differently",
|
||
"homework.followup.redo": "Redo",
|
||
"homework.followup.step": "Explain step {n} differently",
|
||
"homework.followup.working": "Rewriting…",
|
||
"homework.followup.failed": "That didn't work. Try again.",
|
||
"homework.followup.restore": "Original",
|
||
"homework.toDeck": "Turn into a study deck",
|
||
"homework.toDeckNote": "Opens New deck with this content and the options pre-filled.",
|
||
"server.needSolution": "Add your own solution so it can be checked.",
|
||
"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…",
|
||
|
||
/* chat ------------------------------------------------------------ */
|
||
"chat.nav": "Chat",
|
||
"chat.title": "Chat",
|
||
"chat.ask": "Ask",
|
||
"chat.askAboutDeck": "Ask about this deck",
|
||
"chat.askQuestion": "Ask a question",
|
||
"chat.back": "Back",
|
||
"chat.new": "New chat",
|
||
"chat.placeholder": "Ask about your notes…",
|
||
"chat.send": "Send",
|
||
"chat.stop": "Stop",
|
||
"chat.copy": "Copy",
|
||
"chat.copied": "Copied",
|
||
"chat.copyFailed": "Copy failed",
|
||
"chat.edit": "Edit and resend",
|
||
"chat.regenerate": "Regenerate",
|
||
"chat.retry": "Retry",
|
||
"chat.thinking": "Thinking…",
|
||
"chat.thought": "Thinking",
|
||
"chat.sources": "Sources",
|
||
"chat.jumpToBottom": "Jump to the newest",
|
||
"chat.offlineNote": "Offline — sending is paused",
|
||
"chat.emptyTitle": "Ask your notes anything",
|
||
"chat.emptyHint": "Answers come from your own decks. Pick what the chat may read with the context chip below.",
|
||
"chat.oldDecksNote": "Older decks only have the generated study content. Re-add the notes for better answers.",
|
||
"chat.fromLabel": "From",
|
||
"chat.notesOffNote": "Chat is set to ignore notes text.",
|
||
"chat.notesOffLink": "Turn it back on in Settings",
|
||
"chat.attach": "Attach photos or documents",
|
||
"chat.attachPhoto": "photo",
|
||
"chat.attachDoc": "document",
|
||
"chat.attachRemove": "Remove attachment",
|
||
"chat.attachFailed": "That attachment could not be read.",
|
||
"chat.attachNoText": "No text found in that file — try a photo instead.",
|
||
"chat.attachEmpty": "Nothing readable was found in that attachment.",
|
||
"chat.attachmentLabel": "Attachment",
|
||
"chat.modelTitle": "Chat model",
|
||
"chat.modelText": "Text",
|
||
"chat.modelVision": "Vision",
|
||
"chat.modelBoth": "Text + vision",
|
||
"chat.modelAvailable": "available",
|
||
"chat.modelUnavailable": "not found",
|
||
"chat.modelNote": "Photos and documents are transcribed first, so any of these works.",
|
||
"chat.scope.title": "What may chat read?",
|
||
"chat.scope.all": "All notes",
|
||
"chat.scope.allNote": "Every deck's notes can ground the answers.",
|
||
"chat.scope.pick": "Selected decks",
|
||
"chat.scope.pickNote": "Choose folders or single decks below.",
|
||
"chat.scope.none": "No notes",
|
||
"chat.scope.noneNote": "General chat — the model answers from general knowledge only.",
|
||
"chat.scope.decks.one": "{n} deck",
|
||
"chat.scope.decks.few": "{n} decks",
|
||
"chat.scope.decks.many": "{n} decks",
|
||
"chat.scope.deckCount.one": "{n} deck",
|
||
"chat.scope.deckCount.few": "{n} decks",
|
||
"chat.scope.deckCount.many": "{n} decks",
|
||
"chat.scope.hasNotes": "notes text kept",
|
||
"chat.scope.generatedOnly": "generated content only",
|
||
"chat.scope.note": "Only what you pick here is sent as context — the API key never leaves this browser.",
|
||
"chat.suggest.explain": "Explain the main idea of {deck}",
|
||
"chat.suggest.quiz": "Quiz me on {deck}",
|
||
"chat.suggest.summarize": "Summarize my notes",
|
||
"chat.suggest.quizAll": "Quiz me on my decks",
|
||
"chat.suggest.studyFirst": "What should I study first?",
|
||
"chat.suggest.general": "Help me understand a topic",
|
||
"chat.suggest.explainTopic": "Explain a topic step by step",
|
||
"chat.seedResults": "Study results",
|
||
"chat.seedAssignment": "Assignment",
|
||
"chat.seedAnswer": "Answer",
|
||
"chat.seedAttempt": "My attempt",
|
||
"chat.seedVerdict": "Verdict",
|
||
"chat.seedToFix": "To fix",
|
||
"chat.seedCorrected": "Corrected version",
|
||
"chat.seedSteps": "Steps",
|
||
"chat.notesDeleted": "Notes text deleted",
|
||
"chat.deleteNotes": "Delete notes text",
|
||
"chat.history": "Chats",
|
||
"chat.searchChats": "Search chats",
|
||
"chat.noResults": "No chats match that search.",
|
||
"chat.noHistory": "No saved chats yet — ask something and it will appear here.",
|
||
"chat.untitled": "Untitled chat",
|
||
"chat.deleteChat": "Delete chat",
|
||
"chat.deletedChat": "Chat deleted",
|
||
"chat.toFlashcards": "Turn into flashcards",
|
||
"chat.saveToNotes": "Save to notes",
|
||
"chat.savedToNotes": "Saved to {deck}'s notes",
|
||
"chat.savedToNotesTrimmed": "Saved to {deck}'s notes (trimmed to fit)",
|
||
"chat.savedFromChat": "Saved from chat",
|
||
"legal.updated": "Last updated {when}",
|
||
"legal.privacy": "Privacy policy",
|
||
"legal.terms": "Terms of service",
|
||
"legal.cookies": "Cookie policy",
|
||
"settings.theme": "Theme",
|
||
"theme.aero": "Frutiger Aero",
|
||
"theme.aeroNote": "Aero has no light/dark switch — pick Light or Dark to get it back.",
|
||
"settings.legal": "Legal",
|
||
"consent.title": "About cookies",
|
||
"consent.body": "Freezmo uses no cookies and no trackers — your decks, homework and settings stay in this browser.",
|
||
"consent.link": "Cookie policy",
|
||
"consent.ok": "Got it",
|
||
"settings.version": "Version",
|
||
"settings.chatNotes": "Keep notes text for chat",
|
||
"settings.chatNotesNote": "Chat answers from the notes you added (stored with each deck). Turn off to use only the generated study content.",
|
||
"server.chatModelNotAllowed": "That chat model is not on the server's allowlist.",
|
||
"server.chatNeedsQuestion": "The conversation must end with a user message.",
|
||
};
|
||
|
||
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.back": "Zpět",
|
||
"common.next": "Další",
|
||
"common.more": "Více",
|
||
"common.less": "Méně",
|
||
"common.edit": "Upravit",
|
||
"common.stepOf": "{n} / {total}",
|
||
"common.aiDisclaimer": "Freezmo používá AI a může se mýlit. Důležité informace si raději ověř.",
|
||
|
||
/* header ---------------------------------------------------------- */
|
||
"header.home": "Domů — Freezmo",
|
||
"header.tagline": "studijní feed",
|
||
"header.openSettings": "Otevřít nastavení a stav Ollamy",
|
||
|
||
/* tab bar --------------------------------------------------------- */
|
||
"tabbar.main": "Hlavní",
|
||
/* Krátké popisky záložek: musí zůstat na jednom řádku při 360px v obou jazycích. */
|
||
"tab.library": "Knihovna",
|
||
"tab.progress": "Pokrok",
|
||
"tab.new": "Nová sada",
|
||
"tab.exam": "Zkouška",
|
||
"tab.settings": "Nastavení",
|
||
|
||
/* sidebar --------------------------------------------------------- */
|
||
"sidebar.collapse": "Sbalit panel",
|
||
"sidebar.expand": "Rozbalit panel",
|
||
"sidebar.library": "Knihovna",
|
||
"sidebar.label": "Postranní panel",
|
||
|
||
/* 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…",
|
||
"settings.connection": "Připojení",
|
||
"settings.connected": "Připojeno",
|
||
"settings.modelsReady.one": "{n} model připraven",
|
||
"settings.modelsReady.few": "{n} modely připraveny",
|
||
"settings.modelsReady.many": "{n} modelů připraveno",
|
||
"settings.details": "Podrobnosti",
|
||
"settings.privacyLine": "Tvůj klíč zůstává v tomto prohlížeči.",
|
||
"settings.recheck": "Zkontrolovat připojení",
|
||
"settings.haptics": "Haptika",
|
||
"settings.hapticsNote": "Vibrace na podporovaných telefonech.",
|
||
|
||
/* data, backup & sync --------------------------------------------- */
|
||
"settings.data": "Data a záloha",
|
||
"settings.dataNote": "Všechno je v tomto prohlížeči. Zálohuj pravidelně.",
|
||
"settings.exportAll": "Exportovat všechna data",
|
||
"settings.exportNote": "Sady, úkoly, pokrok a nastavení — nikdy tvůj API klíč.",
|
||
"settings.importBackup": "Importovat zálohu",
|
||
"settings.importPreview": "Záloha obsahuje {decks} sad, {homework} výsledků úkolů, {folders} složek a {chats} chatů.",
|
||
"settings.importMergeNote": "Sloučení ponechá stávající data a přidá zálohu; stejná ID se nahradí.",
|
||
"settings.importReplaceNote": "Nahrazení smaže data v tomto prohlížeči a použije zálohu.",
|
||
"settings.importMerge": "Sloučit",
|
||
"settings.importReplace": "Nahradit",
|
||
"settings.importCancel": "Zrušit",
|
||
"settings.importDone": "Importováno {decks} sad a {homework} výsledků úkolů.",
|
||
"settings.importFailed": "Zálohu se nepodařilo importovat.",
|
||
"settings.storage": "Úložiště",
|
||
"settings.storagePersistent": "Trvalé — prohlížeč tvá data nesmaže.",
|
||
"settings.storageBestEffort": "Dočasné — prohlížeč může data při nedostatku místa smazat.",
|
||
"settings.storageRequest": "Požádat o trvalé úložiště",
|
||
"settings.storageGranted": "Povoleno — data jsou teď trvalá.",
|
||
"settings.storageDenied": "Prohlížeč odmítl. Pomůže nainstalovat aplikaci.",
|
||
"settings.backupNudgeTitle": "Zálohovat sady?",
|
||
"settings.backupNudgeBody": "Od poslední zálohy jsi přidal sady. Záloha je udrží v bezpečí.",
|
||
"settings.backupNow": "Zálohovat teď",
|
||
"settings.backupLater": "Teď ne",
|
||
"settings.sync": "Synchronizace",
|
||
"settings.syncNote":
|
||
"Ve výchozím stavu vypnuto. Data se šifrují v tomto prohlížeči před odesláním — server ukládá jen neprůhledný blob a nikdy nevidí tvá data ani klíč.",
|
||
"settings.syncEnable": "Zapnout synchronizaci",
|
||
"settings.syncPassphrase": "Přístupová fráze",
|
||
"settings.syncPassphraseConfirm": "Zopakuj přístupovou frázi",
|
||
"settings.syncPassphraseMismatch": "Fráze se neshodují.",
|
||
"settings.syncPassphraseShort": "Použij aspoň 8 znaků.",
|
||
"settings.syncStart": "Začít synchronizovat",
|
||
"settings.syncCancel": "Zrušit",
|
||
"settings.syncId": "Sync ID",
|
||
"settings.syncCopy": "Kopírovat",
|
||
"settings.syncCopied": "Zkopírováno.",
|
||
"settings.syncLast": "Naposledy synchronizováno {when}",
|
||
"settings.syncNever": "Zatím nesynchronizováno.",
|
||
"settings.syncNow": "Synchronizovat teď",
|
||
"settings.syncWorking": "Synchronizuji…",
|
||
"settings.syncDone": "Vše je aktuální.",
|
||
"settings.syncPushed": "Nahráno z tohoto zařízení.",
|
||
"settings.syncPulled": "Staženo ze synchronizované kopie.",
|
||
"settings.syncConflict": "Toto zařízení i synchronizovaná kopie se změnily. Vyber, kterou ponechat.",
|
||
"settings.syncKeepLocal": "Ponechat toto zařízení",
|
||
"settings.syncKeepRemote": "Použít synchronizovanou kopii",
|
||
"settings.syncJoin": "Připojit k jiné synchronizaci",
|
||
"settings.syncJoinNote":
|
||
"Zadej sync ID a přístupovou frázi z druhého zařízení. Data v tomto prohlížeči nahradí synchronizovaná kopie.",
|
||
"settings.syncJoinId": "Sync ID",
|
||
"settings.syncJoinAction": "Připojit",
|
||
"settings.syncLocked": "Zadej přístupovou frázi a odemkni synchronizaci na tomto zařízení.",
|
||
"settings.syncUnlock": "Odemknout",
|
||
"settings.syncForget": "Zapomenout frázi na tomto zařízení",
|
||
"settings.syncDelete": "Smazat synchronizovaná data",
|
||
"settings.syncDeleteConfirm": "Smazat synchronizovanou kopii ze serveru? Data v tomto zařízení zůstanou.",
|
||
"settings.syncDeleteYes": "Smazat",
|
||
"settings.syncWrongPass": "Touhle frází se synchronizovaná kopie odemknout nedá.",
|
||
"settings.syncError": "Synchronizace selhala. Zkontroluj připojení a zkus to znovu.",
|
||
"settings.syncOff": "Synchronizace je vypnutá.",
|
||
"settings.syncOn": "Synchronizace je zapnutá.",
|
||
"settings.syncNoteKey": "Tvůj API klíč se nikdy nesynchronizuje.",
|
||
|
||
/* offline / share / toasts ---------------------------------------- */
|
||
"offline.banner": "Jsi offline — uložené sady fungují dál.",
|
||
"offline.generate": "Jsi offline — pro vytvoření se připoj.",
|
||
"share.title": "Přidat do Freezma",
|
||
"share.intro": "Co s tím mám udělat?",
|
||
"share.preview": "Sdílený obsah",
|
||
"share.loading": "Načítám sdílený obsah…",
|
||
"share.expired": "Sdílení vypršelo — zkus to znovu.",
|
||
"share.tooLarge": "To je příliš velké (max 30 MB).",
|
||
"share.nothing": "Není co přidat.",
|
||
"share.copied": "Zkopírováno do schránky.",
|
||
"share.failed": "Sdílení tu není dostupné.",
|
||
"toast.deckDeleted": "Sada smazána",
|
||
"toast.fileRemoved": "Soubor odebrán",
|
||
"toast.undo": "Vrátit",
|
||
"upload.pasteClipboard": "Vložit ze schránky",
|
||
"upload.clipboardDenied": "Přístup ke schránce byl zamítnut.",
|
||
|
||
/* 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": "Nový úkol",
|
||
"wizard.mode.deck": "Studijní sada",
|
||
"wizard.mode.homework": "Udělej mi úkol",
|
||
"wizard.mode.aria": "Studijní sada nebo úkol",
|
||
"homeworkHeroTitle": "Udělej mi úkol",
|
||
"homework.subtitle": "Vlož ho, vyfoť nebo přidej soubor — a vyber, jak ti mám pomoct.",
|
||
"homework.phone.assignment": "Zadání",
|
||
"homework.phone.mode": "Jak ti mám pomoct?",
|
||
"homework.phone.modeDesc": "Vyber, jak ti s tímto zadáním pomůžu.",
|
||
"homework.phone.preview": "Náhled zadání",
|
||
"homework.phone.startHint": "Klepni na kartu a začnu.",
|
||
"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.reading": "Čtu zadání…",
|
||
"homework.loading.readingSolution": "Čtu tvoje řešení…",
|
||
"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.report": "Nahlásit cvičení",
|
||
"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?",
|
||
"homework.emptyTitle": "Zatím žádné úkoly",
|
||
"homework.emptyBody": "Vyřešená zadání a návody krok za krokem se objeví tady.",
|
||
"homework.open": "Otevřít",
|
||
"homework.steps.one": "{n} krok",
|
||
"homework.steps.few": "{n} kroky",
|
||
"homework.steps.many": "{n} kroků",
|
||
|
||
/* library tabs ----------------------------------------------------- */
|
||
"tabDecks": "Studijní balíčky",
|
||
"tabHomework": "Úkoly",
|
||
|
||
/* quick actions (library empty state + /create switcher) ----------- */
|
||
"emptyStateCreateDeck": "Vytvořit studijní balíček",
|
||
"emptyStateHomework": "Nový úkol",
|
||
|
||
/* library --------------------------------------------------------- */
|
||
"library.title": "Tvůj mrazák",
|
||
"library.subtitle": "Sady zmrazené z tvých poznámek, uložené v tomto prohlížeči.",
|
||
"library.import": "Importovat JSON",
|
||
"library.moreActions": "Další akce",
|
||
"library.moreTitle": "Akce knihovny",
|
||
"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 kartiček a kvízů.",
|
||
"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.share": "Sdílet",
|
||
"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.introShort": "Přidej poznámky — sadu postavím já.",
|
||
"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.addPhotosFiles": "Přidat fotky nebo soubory",
|
||
"upload.pasteInstead": "Místo toho vložit text",
|
||
"upload.focusExtra": "Přidat zaměření nebo poznámky navíc (nepovinné)",
|
||
"upload.phone.notes": "Tvoje poznámky",
|
||
"upload.phone.notesDesc": "Fotky, PDF, Word nebo text — až {images} fotek a {documents} souborů.",
|
||
"upload.phone.options": "Možnosti",
|
||
"upload.phone.optionsDesc": "Vše má rozumné výchozí hodnoty — změň, co chceš.",
|
||
"upload.phone.summary": "Připraveno ke zmrazení",
|
||
"upload.phone.summaryDesc": "Zkontroluj, co se použije, a zmraz.",
|
||
"upload.phone.difficultyHelp": "Jak hluboko otázky jdou.",
|
||
"upload.phone.activitiesHelp": "Kolik aktivit se vytvoří.",
|
||
"upload.phone.folderHelp": "Kam se sada v knihovně zařadí.",
|
||
"upload.nothingAdded": "Zatím nic přidáno",
|
||
"upload.summary.files": "Soubory",
|
||
"upload.summary.folder": "Složka",
|
||
"upload.summaryText": "Text",
|
||
"upload.photoCount.one": "{n} fotka",
|
||
"upload.photoCount.few": "{n} fotky",
|
||
"upload.photoCount.many": "{n} fotek",
|
||
"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",
|
||
"feed.report": "Nahlásit otázku",
|
||
"feed.reported": "Otázka nahlášena — už se neukáže.",
|
||
|
||
/* 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.stalled": "Model přestal odpovídat. Zkontrolujte ho a zkuste to znovu.",
|
||
"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.busy": "Freezmo už na něčem pracuje — zkus to za chvilku znovu.",
|
||
"server.pushTooMany": "Server už má uložených {max} zařízení pro připomínky — nejdřív nějaké odeber.",
|
||
"server.pushEndpoint": "Na tuhle službu server připomínky posílat nesmí.",
|
||
"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.",
|
||
"server.usageLimit":
|
||
"Vyčerpal se tvůj měsíční limit Ollama zdarma. Upgraduj svůj účet Ollama, nebo si vytvoř nový a použij jeho nový API klíč.",
|
||
"server.ollamaThrottled": "Ollama dočasně omezuje požadavky. Zkus to za chvíli znovu.",
|
||
"server.syncInvalidId": "Tenhle sync ID nevypadá správně.",
|
||
"server.syncTooLarge": "Tahle synchronizace je příliš velká.",
|
||
"server.syncStorageFull": "Úložiště synchronizace je plné. Smaž nějaká synchronizovaná data a zkus to znovu.",
|
||
"server.syncNotFound": "Pro tenhle ID nejsou žádná synchronizovaná data.",
|
||
"server.pushDisabled": "Push připomínky nejsou na tomto serveru nastavené (nastav VAPID_PUBLIC_KEY a VAPID_PRIVATE_KEY).",
|
||
"server.pushNotSubscribed": "Tento prohlížeč není k připomínkám přihlášený.",
|
||
"server.pushGone": "Ta přihláška už není platná — připomínky pro ni byly vypnuté.",
|
||
"server.pushSendFailed": "Připomínku se nepodařilo poslat.",
|
||
"server.pushReminderTitle": "Čas na opakování",
|
||
"server.pushReminderBody": "Sady na tebe čekají — pár kartiček udrží sérii.",
|
||
|
||
/* 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.",
|
||
"import.notBackup": "Tento soubor nevypadá jako záloha Freezmo.",
|
||
"error.usageTitle": "Měsíční limit zdarma vyčerpán",
|
||
"error.usageBody":
|
||
"Vyčerpal se tvůj měsíční limit Ollama zdarma. Upgraduj svůj účet Ollama, nebo si vytvoř nový a použij jeho nový API klíč.",
|
||
"error.openOllama": "Otevřít Ollamu",
|
||
"error.throttledTitle": "Ollama je zaneprázdněná",
|
||
"error.throttledBody": "Ollama dočasně omezuje požadavky. Zkus to za chvíli znovu.",
|
||
|
||
/* learning: review, mistakes, stats, exam -------------------------- */
|
||
"review.cardTitle": "Chytré opakování",
|
||
"review.title": "K opakování",
|
||
"review.mistakesTitle": "Chyby",
|
||
"review.mistakesDeck": "Chyby v této sadě",
|
||
"review.nothingDue": "Teď není co opakovat. Zauč se do sady a rozvrh se sám naplní.",
|
||
"review.noMistakes": "Žádné otevřené chyby — co jsi měl špatně, jsi od té doby zodpověděl správně.",
|
||
"review.done": "Zpět do knihovny",
|
||
"review.seeResults": "Zobrazit výsledky",
|
||
"review.finishFirst": "Dokonči poslední aktivitu a uvidíš výsledky.",
|
||
"review.results": "Výsledky",
|
||
"review.preparing": "Připravuji opakování…",
|
||
"review.due.one": "{n} k opakování",
|
||
"review.due.few": "{n} k opakování",
|
||
"review.due.many": "{n} k opakování",
|
||
"review.mistakes.one": "{n} chyba",
|
||
"review.mistakes.few": "{n} chyby",
|
||
"review.mistakes.many": "{n} chyb",
|
||
"exam.title": "Zkouškový režim",
|
||
"exam.preparing": "Připravuji zkouškový režim…",
|
||
"exam.subtitle": "Vyber sady, nastav časovač, žádné nápovědy — uvidíš, jak bys dopadl.",
|
||
"exam.pickDecks": "Sady",
|
||
"exam.noDecks": "Nejdřív si vytvoř sadu.",
|
||
"exam.questions": "Otázky",
|
||
"exam.timer": "Časovač",
|
||
"exam.timerOff": "Vypnuto",
|
||
"exam.start": "Spustit zkoušku ({n} otázek)",
|
||
"exam.results": "Výsledky zkoušky",
|
||
"exam.byDeck": "Podle sady",
|
||
"exam.byTopic": "Podle tématu",
|
||
"exam.studyMistakes": "Procvičit chyby",
|
||
"exam.timeLeft": "zbývá {time}",
|
||
"stats.title": "Tvůj pokrok",
|
||
"stats.preparing": "Připravuji pokrok…",
|
||
"stats.subtitle": "Série, úspěšnost a témata, která si zaslouží další kolo.",
|
||
"stats.streak": "Série dní",
|
||
"stats.today": "Dnes",
|
||
"stats.todayOf": "{n} / {goal} odpovědí",
|
||
"stats.goalDone": "Denní cíl splněn 🎉",
|
||
"stats.accuracy": "Úspěšnost",
|
||
"stats.answers.one": "{n} odpověď",
|
||
"stats.answers.few": "{n} odpovědi",
|
||
"stats.answers.many": "{n} odpovědí",
|
||
"stats.calendar": "Posledních 5 týdnů",
|
||
"stats.bySubject": "Podle předmětu",
|
||
"stats.byDeck": "Podle sady",
|
||
"stats.weakest": "Nejslabší témata",
|
||
"stats.noSubject": "Bez předmětu",
|
||
"stats.correctOf": "{correct} / {total} správně",
|
||
"stats.empty": "Zatím žádné odpovědi",
|
||
"stats.emptyBody": "Zauč se do sady a tvůj pokrok se objeví tady.",
|
||
"stats.start": "Jít studovat",
|
||
"stats.legendLess": "Méně",
|
||
"stats.legendMore": "Více",
|
||
"nav.stats": "Pokrok",
|
||
"nav.exam": "Zkouškový režim",
|
||
"settings.learning": "Učení",
|
||
"settings.dailyGoal": "Denní cíl",
|
||
"settings.dailyGoalNote": "Kolik odpovědí za den se počítá jako splněno.",
|
||
"settings.reminders": "Denní připomínka",
|
||
"settings.remindersNote":
|
||
"Jemné připomenutí, když je co opakovat. Potřebuje nainstalovanou aplikaci — na iPhonu je to podmínka.",
|
||
"settings.reminderEnable": "Zapnout připomínky",
|
||
"settings.reminderDisable": "Vypnout",
|
||
"settings.reminderTest": "Poslat zkušební",
|
||
"settings.reminderOn": "Připomínky jsou zapnuté.",
|
||
"settings.reminderOff": "Připomínky jsou vypnuté.",
|
||
"settings.reminderDenied": "Oznámení jsou v nastavení prohlížeče zablokovaná.",
|
||
"settings.reminderError": "Připomínky se nepodařilo nastavit.",
|
||
"library.learningToday": "Dnes: {n} / {goal}",
|
||
|
||
/* homework: check my work + follow-ups ----------------------------- */
|
||
"homework.mode.check": "Zkontrolovat moje řešení",
|
||
"homework.mode.checkDesc": "Oznámkuj svůj postup: co je správně, co špatně a opravená verze.",
|
||
"homework.solution.label": "Tvoje řešení",
|
||
"homework.solution.placeholder": "Sem vlož svůj postup — zadání je nahoře.",
|
||
"homework.solution.note": "Řešení napiš, vlož, nebo přidej fotky a dokumenty se svým postupem — ručně psané stránky jsou v pohodě.",
|
||
"homework.solution.add": "Přidat fotky nebo soubory",
|
||
"homework.solution.tooManyPhotos": "Až {n} fotek k řešení.",
|
||
"homework.solution.tooManyDocs": "Až {n} dokumentů k řešení.",
|
||
"homework.solution.required": "Přidej svoje řešení, ať je co zkontrolovat.",
|
||
"homework.check.title": "Zkontrolováno",
|
||
"homework.check.verdict": "Hodnocení",
|
||
"homework.check.correct": "Co je správně",
|
||
"homework.check.wrong": "Co opravit",
|
||
"homework.check.fix": "Oprava",
|
||
"homework.check.corrected": "Opravená verze",
|
||
"homework.history.checked": "Zkontrolováno",
|
||
"homework.loading.checking": "Kontroluji tvůj postup…",
|
||
"homework.followup.shorter": "Kratší",
|
||
"homework.followup.more": "Více detailů",
|
||
"homework.followup.explain": "Vysvětlit jinak",
|
||
"homework.followup.redo": "Znovu",
|
||
"homework.followup.step": "Vysvětlit krok {n} jinak",
|
||
"homework.followup.working": "Přepisuji…",
|
||
"homework.followup.failed": "Nepovedlo se. Zkus to znovu.",
|
||
"homework.followup.restore": "Původní",
|
||
"homework.toDeck": "Převést na studijní sadu",
|
||
"homework.toDeckNote": "Otevře Novou sadu s tímto obsahem a předvyplněnými možnostmi.",
|
||
"server.needSolution": "Přidej svoje řešení, ať se dá zkontrolovat.",
|
||
"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…",
|
||
|
||
/* chat ------------------------------------------------------------ */
|
||
"chat.nav": "Chat",
|
||
"chat.title": "Chat",
|
||
"chat.ask": "Zeptat se",
|
||
"chat.askAboutDeck": "Zeptat se na tuto sadu",
|
||
"chat.askQuestion": "Zeptat se na něco",
|
||
"chat.back": "Zpět",
|
||
"chat.new": "Nový chat",
|
||
"chat.placeholder": "Zeptej se na svoje poznámky…",
|
||
"chat.send": "Odeslat",
|
||
"chat.stop": "Zastavit",
|
||
"chat.copy": "Kopírovat",
|
||
"chat.copied": "Zkopírováno",
|
||
"chat.copyFailed": "Kopírování se nepovedlo",
|
||
"chat.edit": "Upravit a poslat znovu",
|
||
"chat.regenerate": "Vygenerovat znovu",
|
||
"chat.retry": "Zkusit znovu",
|
||
"chat.thinking": "Přemýšlím…",
|
||
"chat.thought": "Myšlenkový postup",
|
||
"chat.sources": "Zdroje",
|
||
"chat.jumpToBottom": "Skočit na nejnovější",
|
||
"chat.offlineNote": "Offline — posílání je pozastavené",
|
||
"chat.emptyTitle": "Zeptej se svých poznámek na cokoli",
|
||
"chat.emptyHint": "Odpovědi vycházejí z tvých sad. Pomocí chipu s kontextem níže vyber, co smí chat číst.",
|
||
"chat.oldDecksNote": "Starší sady mají jen vygenerovaný obsah. Přidej znovu poznámky a odpovědi budou lepší.",
|
||
"chat.fromLabel": "Z",
|
||
"chat.notesOffNote": "Chat má vypnuté používání textu poznámek.",
|
||
"chat.notesOffLink": "Zapnout v nastavení",
|
||
"chat.attach": "Přiložit fotky nebo dokumenty",
|
||
"chat.attachPhoto": "fotka",
|
||
"chat.attachDoc": "dokument",
|
||
"chat.attachRemove": "Odebrat přílohu",
|
||
"chat.attachFailed": "Tuto přílohu se nepovedlo přečíst.",
|
||
"chat.attachNoText": "V souboru nebyl žádný text — zkus místo toho fotku.",
|
||
"chat.attachEmpty": "V příloze nebylo nic čitelného.",
|
||
"chat.attachmentLabel": "Příloha",
|
||
"chat.modelTitle": "Model chatu",
|
||
"chat.modelText": "Text",
|
||
"chat.modelVision": "Obrázky",
|
||
"chat.modelBoth": "Text + obrázky",
|
||
"chat.modelAvailable": "dostupný",
|
||
"chat.modelUnavailable": "nenalezen",
|
||
"chat.modelNote": "Fotky a dokumenty se nejdřív přepíšou na text, takže funguje kterýkoli z nich.",
|
||
"chat.scope.title": "Co smí chat číst?",
|
||
"chat.scope.all": "Všechny poznámky",
|
||
"chat.scope.allNote": "Odpovědi mohou vycházet z poznámek všech sad.",
|
||
"chat.scope.pick": "Vybrané sady",
|
||
"chat.scope.pickNote": "Níže vyber složky nebo jednotlivé sady.",
|
||
"chat.scope.none": "Bez poznámek",
|
||
"chat.scope.noneNote": "Obecný chat — model odpovídá jen z obecných znalostí.",
|
||
"chat.scope.decks.one": "{n} sada",
|
||
"chat.scope.decks.few": "{n} sady",
|
||
"chat.scope.decks.many": "{n} sad",
|
||
"chat.scope.deckCount.one": "{n} sada",
|
||
"chat.scope.deckCount.few": "{n} sady",
|
||
"chat.scope.deckCount.many": "{n} sad",
|
||
"chat.scope.hasNotes": "text poznámek uložen",
|
||
"chat.scope.generatedOnly": "jen vygenerovaný obsah",
|
||
"chat.scope.note": "Jako kontext se posílá jen to, co tady vybereš — API klíč nikdy neopustí tento prohlížeč.",
|
||
"chat.suggest.explain": "Vysvětli hlavní myšlenku sady {deck}",
|
||
"chat.suggest.quiz": "Vyzkoušej mě ze sady {deck}",
|
||
"chat.suggest.summarize": "Shrň moje poznámky",
|
||
"chat.suggest.quizAll": "Vyzkoušej mě ze všech sad",
|
||
"chat.suggest.studyFirst": "Co mám studovat jako první?",
|
||
"chat.suggest.general": "Pomoz mi pochopit nějaké téma",
|
||
"chat.suggest.explainTopic": "Vysvětli téma krok za krokem",
|
||
"chat.seedResults": "Výsledky studia",
|
||
"chat.seedAssignment": "Zadání",
|
||
"chat.seedAnswer": "Odpověď",
|
||
"chat.seedAttempt": "Můj postup",
|
||
"chat.seedVerdict": "Hodnocení",
|
||
"chat.seedToFix": "Co opravit",
|
||
"chat.seedCorrected": "Opravená verze",
|
||
"chat.seedSteps": "Postup",
|
||
"chat.notesDeleted": "Text poznámek smazán",
|
||
"chat.deleteNotes": "Smazat text poznámek",
|
||
"chat.history": "Chaty",
|
||
"chat.searchChats": "Hledat v chatech",
|
||
"chat.noResults": "Tomuto hledání nic neodpovídá.",
|
||
"chat.noHistory": "Zatím žádné uložené chaty — na něco se zeptej a objeví se tady.",
|
||
"chat.untitled": "Chat bez názvu",
|
||
"chat.deleteChat": "Smazat chat",
|
||
"chat.deletedChat": "Chat smazán",
|
||
"chat.toFlashcards": "Převést na kartičky",
|
||
"chat.saveToNotes": "Uložit do poznámek",
|
||
"chat.savedToNotes": "Uloženo do poznámek sady {deck}",
|
||
"chat.savedToNotesTrimmed": "Uloženo do poznámek sady {deck} (zkráceno, aby se vešlo)",
|
||
"chat.savedFromChat": "Uloženo z chatu",
|
||
"legal.updated": "Naposledy upraveno {when}",
|
||
"legal.privacy": "Zásady soukromí",
|
||
"legal.terms": "Podmínky použití",
|
||
"legal.cookies": "Zásady cookies",
|
||
"settings.theme": "Téma",
|
||
"theme.aero": "Frutiger Aero",
|
||
"theme.aeroNote": "Aero nemá přepínač světlý/tmavý — vyber Světlý nebo Tmavý, pokud ho chceš zpět.",
|
||
"settings.legal": "Právní informace",
|
||
"consent.title": "O cookies",
|
||
"consent.body": "Freezmo nepoužívá cookies ani sledování — sady, úkoly a nastavení zůstávají v tomto prohlížeči.",
|
||
"consent.link": "Zásady cookies",
|
||
"consent.ok": "Rozumím",
|
||
"settings.version": "Verze",
|
||
"settings.chatNotes": "Uchovávat text poznámek pro chat",
|
||
"settings.chatNotesNote": "Chat odpovídá z poznámek, které jsi přidal (jsou uložené u každé sady). Vypnutím bude používat jen vygenerovaný obsah.",
|
||
"server.chatModelNotAllowed": "Tento model chatu není v povoleném seznamu serveru.",
|
||
"server.chatNeedsQuestion": "Konverzace musí končit zprávou uživatele.",
|
||
};
|
||
|
||
/** Used by tests/tools that want the raw tables. */
|
||
export const dictionaries = { en, cs };
|