// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Package i18n holds the admin interface strings in every language the // UI ships. Simple messages are keyed by their English text, so the // template keeps its meaning and an untranslated key falls back to // English rather than an identifier. Messages with a numeral use an // explicit id and per-language plural forms, because Czech declines // the counted noun where English does not. // // The catalogue never stores markup: sentences with , and // friends stay in the template, split into fragments, so every string // the funcs return can take the template engine's escaping. The one // exception is trh, for static sentences that only make sense whole. // // Some keys never appear literally outside this file: the editor // matches payloads validation errors against the catalogue at runtime // ("Slug is required." and friends), and the page scripts read their // strings from the JSON dump, so a key being absent from the templates // does not mean it is dead. package i18n import ( "slices" "strconv" "strings" ) // Languages the admin interface ships, in the order the settings // switch lists them. var Languages = []string{"en", "cs"} // Cookie carries the interface language across the login screen, where // no account is known yet. const Cookie = "volumen_admin_lang" // Valid reports whether the value is a shipped language. func Valid(lang string) bool { return slices.Contains(Languages, lang) } // Normalize maps a language tag, which may carry a region ("cs-CZ"), // onto a shipped language, or "" when nothing matches. func Normalize(tag string) string { tag = strings.ToLower(strings.TrimSpace(tag)) for _, l := range Languages { if tag == l || strings.HasPrefix(tag, l+"-") { return l } } return "" } // pluralSet holds the counted forms of one message: one for exactly // one, few for the Czech "2 to 4" class (22 to 24 included), other for // the rest. English never uses few. type pluralSet struct { one, few, other string } // pluralForms are the messages that carry a numeral. Every language // defines one and other; few exists for Czech alone. var pluralForms = map[string]map[string]pluralSet{ "posts.total": { "en": {one: "%d post in total", other: "%d posts in total"}, "cs": {one: "Celkem %d publikace", few: "Celkem %d publikace", other: "Celkem %d publikací"}, }, "media.count": { "en": {one: "%d file uploaded", other: "%d files uploaded"}, "cs": {one: "Nahraný %d soubor", few: "Nahrané %d soubory", other: "Nahraných %d souborů"}, }, "revisions.count": { "en": {one: "%d archived version", other: "%d archived versions"}, "cs": {one: "Archivovaná %d verze", few: "Archivované %d verze", other: "Archivovaných %d verzí"}, }, "posts.published": { "en": {one: "%d post published.", other: "%d posts published."}, "cs": {one: "Zveřejněna %d publikace.", few: "Zveřejněny %d publikace.", other: "Zveřejněno %d publikací."}, }, "posts.drafted": { "en": {one: "%d post moved to draft.", other: "%d posts moved to draft."}, "cs": {one: "Přesunuta %d publikace do konceptu.", few: "Přesunuty %d publikace do konceptu.", other: "Přesunuto %d publikací do konceptu."}, }, "posts.deleted": { "en": {one: "%d post deleted.", other: "%d posts deleted."}, "cs": {one: "Smazána %d publikace.", few: "Smazány %d publikace.", other: "Smazáno %d publikací."}, }, "diff.unchanged": { "en": {one: "%d unchanged line", other: "%d unchanged lines"}, "cs": {one: "%d nezměněný řádek", few: "%d nezměněné řádky", other: "%d nezměněných řádků"}, }, "password.min": { "en": {one: "Password must be at least %d character long.", other: "Password must be at least %d characters long."}, "cs": {one: "Heslo musí mít nejméně %d znak.", few: "Heslo musí mít nejméně %d znaky.", other: "Heslo musí mít nejméně %d znaků."}, }, "password.max": { "en": {one: "Password must be at most %d character long.", other: "Password must be at most %d characters long."}, "cs": {one: "Heslo musí mít nejvýše %d znak.", few: "Heslo musí mít nejvýše %d znaky.", other: "Heslo musí mít nejvýše %d znaků."}, }, "editor.words": { "en": {one: "%d word", other: "%d words"}, "cs": {one: "%d slovo", few: "%d slova", other: "%d slov"}, }, "refs.count": { "en": {one: "%d reference", other: "%d references"}, "cs": {one: "%d reference", few: "%d reference", other: "%d referencí"}, }, "editor.chars": { "en": {one: "%d char", other: "%d chars"}, "cs": {one: "%d znak", few: "%d znaky", other: "%d znaků"}, }, "editor.reading": { "en": {one: "%d min read", other: "%d min read"}, "cs": {one: "%d min čtení", few: "%d min čtení", other: "%d min čtení"}, }, "bulk.selected": { "en": {one: "%d selected", other: "%d selected"}, "cs": {one: "Vybráno %d", few: "Vybráno %d", other: "Vybráno %d"}, }, "login.seconds": { "en": {one: "Too many login attempts. Try again in %d second.", other: "Too many login attempts. Try again in %d seconds."}, "cs": {one: "Příliš mnoho pokusů o přihlášení. Zkuste to znovu za %d s.", few: "Příliš mnoho pokusů o přihlášení. Zkuste to znovu za %d s.", other: "Příliš mnoho pokusů o přihlášení. Zkuste to znovu za %d s."}, }, "deliveries.attempts": { "en": {one: "failed (%d attempt)", other: "failed (%d attempts)"}, "cs": {one: "selhalo (%d pokus)", few: "selhalo (%d pokusy)", other: "selhalo (%d pokusů)"}, }, "posts.count": { "en": {one: "%d post", other: "%d posts"}, "cs": {one: "%d publikace", few: "%d publikace", other: "%d publikací"}, }, "backup.files": { "en": {one: "Backup restored (%d file).", other: "Backup restored (%d files)."}, "cs": {one: "Záloha obnovena (%d soubor).", few: "Záloha obnovena (%d soubory).", other: "Záloha obnovena (%d souborů)."}, }, } // cs translates the simple messages, keyed by the English text the // templates and handlers use. Markup stays out: where the template // carries or , the sentence is split into fragments. var cs = map[string]string{ // Layout: sidebar, topbar, mobile sheet, shared dialogs. "Content": "Obsah", "Configure": "Nastavení", "Posts": "Publikace", "New post": "Nová publikace", "Import": "Import", "Media": "Média", "Settings": "Nastavení", "Log out": "Odhlásit se", "Menu": "Nabídka", "Main menu": "Hlavní nabídka", "Collapse sidebar": "Sbalit postranní panel", "Expand sidebar": "Rozbalit postranní panel", "administration": "administrace", "Skip to content": "Přejít na obsah", "Breadcrumb": "Navigace", "Colour mode": "Režim barev", "Follow system": "Podle systému", "Light": "Světlý", "Dark": "Tmavý", "Update now": "Aktualizovat", "Details": "Podrobnosti", "is available; you are running": "je k dispozici; provozujete", "Update to %s now?": "Aktualizovat na %s hned?", "The server will restart.": "Server se restartuje.", "Are you sure?": "Opravdu?", "Continue": "Pokračovat", "URL": "URL", "OK": "Budiž", // The admin 404 page. "Page not found": "Stránka nenalezena", "The address does not exist; it may have been moved or deleted.": "Tato adresa neexistuje; možná byla přesunuta nebo zrušena.", "Back to dashboard": "Zpět na přehled", // Login page. "Volumen · administration": "Volumen · administrace", "Sign in": "Přihlásit se", "Welcome back. Sign in to manage your posts.": "Vítejte zpět. Přihlaste se ke správě publikací.", "Username": "Uživatel", "Password": "Heslo", "Show password": "Zobrazit heslo", "Hide password": "Skrýt heslo", "Invalid username or password.": "Neplatné uživatelské jméno nebo heslo.", "Auto-unlock in {} s.": "Odemčení za {} s.", " You can try again now.": " Můžete to zkusit znovu.", // First-run wizard. "Welcome to Volumen": "Vítejte ve Volumenu", "Set up Volumen": "Nastavení Volumenu", "Set up the administrator account to open this installation.": "Nastavte účet správce, tím se instalace otevře.", "The page takes the colours as you choose.": "Stránka mění barvy podle vaší volby.", "Create account": "Vytvořit účet", "The users file cannot be read; repair it before setting up.": "Soubor uživatelů nelze přečíst; před nastavením ho opravte.", "The account could not be created: %s": "Účet se nepodařilo vytvořit: %s", "Weak": "Slabé", "Fair": "Dostatečné", "Good": "Dobré", "Strong": "Silné", // Posts list. "Published": "Publikované", "Drafts": "Koncepty", "Scheduled": "Naplánované", "Total": "Celkem", "Recent published": "Nedávno publikované", "Search title, slug, tag...": "Hledat v titulku, slagu, značce…", "Select all posts": "Vybrat všechny publikace", "All": "Vše", "Filter by status": "Filtrovat podle stavu", "Filter by tag": "Filtrovat podle značky", "Publish": "Publikovat", "Move to draft": "Přesunout do konceptu", "Delete": "Smazat", "Cancel": "Zrušit", "Two-factor authentication": "Dvoufázové přihlášení", "Two-factor code": "Kód dvoufázového přihlášení", "The password is accepted; answer the second question.": "Heslo přijato; odpovězte na druhou otázku.", "Verification code": "Ověřovací kód", "six digits, or a recovery code": "šest číslic, nebo záložní kód", "Six digits from your application, or one of your recovery codes.": "Šest číslic z vaší aplikace, nebo jeden ze záložních kódů.", "Back to sign in": "Zpět na přihlášení", "Your sign-in asks for a code from your application after the password.": "Po přihlášení heslem se žádá ještě kód z vaší aplikace.", "Current code": "Současný kód", "Regenerate recovery codes": "Vygenerovat záložní kódy znovu", "Turn two-factor off? Your application will no longer be asked.": "Vypnout dvoufázové přihlášení? Vaše aplikace se už nebude dotazovat.", "Turn off": "Vypnout", "Scan the code with your authenticator application, or enter the secret by hand, then confirm with the code it shows.": "Naskenujte kód svou aplikací pro ověřování, nebo zadejte tajný klíč ručně, a potvrďte kódem, který ukáže.", "Secret": "Tajný klíč", "Verify and enable": "Ověřit a zapnout", "Ask for a code from your authenticator application after the password. Voluntary: nothing changes until you finish the setup.": "Po hesle se bude žádat o kód z vaší ověřovací aplikace. Dobrovolné: nic se nezmění, dokud nastavení nedokončíte.", "Set up two-factor": "Nastavit dvoufázové přihlášení", "Wrong or expired code.": "Nesprávný nebo propadlý kód.", "That code did not match; start again.": "Kód nesouhlasil; začněte znovu.", "Two-factor is already on.": "Dvoufázové přihlášení už je zapnuté.", "Two-factor could not be enabled: %s": "Dvoufázové přihlášení nešlo zapnout: %s", "Two-factor could not be disabled: %s": "Dvoufázové přihlášení nešlo vypnout: %s", "The codes could not be replaced: %s": "Záložní kódy nešlo vyměnit: %s", "Two-factor is off.": "Dvoufázové přihlášení je vypnuté.", "Two-factor is on. Store these recovery codes now; they will not be shown again.": "Dvoufázové přihlášení je zapnuté. Uschovejte si tyto záložní kódy; znovu se neukážou.", "New recovery codes. Store them now; they will not be shown again.": "Nové záložní kódy. Uschovejte si je; znovu se neukážou.", "No posts yet": "Zatím žádné publikace", "Create your first post to get started.": "Začněte vytvořením první publikace.", "Create post": "Vytvořit publikaci", "No posts match your search": "Žádná publikace neodpovídá hledání", "Try a different search term or status filter.": "Zkuste jiné hledané slovo nebo jiný filtr stavu.", "Clear filters": "Vymazat filtry", "Draft": "Koncept", "Edit": "Upravit", "Delete post": "Smazat publikaci", "Delete %s?": "Smazat %s?", "Post created.": "Publikace vytvořena.", "Post saved.": "Publikace uložena.", "Post deleted.": "Publikace smazána.", "Post duplicated.": "Publikace zkopírována.", "Post restored.": "Publikace obnovena.", "Could not restore the post; it may already be back.": "Publikaci se nepodařilo obnovit; možná už je zpět.", "The post could not be duplicated.": "Publikaci se nepodařilo zdvojit.", "Undo": "Zpět", "Undo failed.": "Obnovení se nezdařilo.", "Delete the selected posts?": "Smazat vybrané publikace?", "Delete this post?": "Smazat tuto publikaci?", // Editor. "Draft a new article in Markdown.": "Napište nový článek v Markdownu.", "Edit post": "Úprava publikace", "Update and publish this post.": "Upravte a zveřejněte tuto publikaci.", "Draft, not visible to the public": "Koncept, veřejnosti nezobrazovaný", "Scheduled for %s": "Naplánováno na %s", "Scheduled for later": "Naplánováno na později", "Preview": "Náhled", "Download": "Stáhnout", "History": "Historie", "Duplicate": "Zkopírovat", "Duplicate as draft": "Zkopírovat jako koncept", "Duplicate this post as a draft?": "Zkopírovat tuto publikaci jako koncept?", "Save": "Uložit", "Save (Ctrl+S)": "Uložit (Ctrl+S)", "Revision restored. The previous content was archived; see": "Verze obnovena. Předchozí obsah byl archivován; viz", "Duplicated as draft. The new slug is": "Zkopírováno jako koncept. Nový slug je", ", adjust the title and body, then publish.": ", upravte titulek a obsah a publikujte.", "Template": "Šablona", "Blank": "Prázdná", "Restore": "Obnovit", "Discard": "Zahodit", "Title, slug, and publishing options": "Titulek, slug a volby publikování", "Title": "Titulek", "A clear, descriptive title": "Výstižný, popisný titulek", "Slug": "Slug", "my-post-slug": "moje-publikace", "Language": "Jazyk", "Author": "Autor", "Author name": "Jméno autora", "Fediverse creator": "Autor na fediverse", "@user@instance.tld": "@uzivatel@instance.tld", "DOI": "DOI", "Author ORCID": "ORCID autora", "10.5281/zenodo.1234567": "10.5281/zenodo.1234567", "0000-0002-1825-0097": "0000-0002-1825-0097", "Digital Object Identifier; a doi.org URL is stored as the bare 10.… form.": "Digital Object Identifier; URL na doi.org se uloží jako holý tvar 10.…", "The author's Open Researcher and Contributor ID, checked against its own digit.": "Open Researcher and Contributor ID autora, kontroluje se i kontrolní číslice.", "DOI must look like 10.xxxx/suffix.": "DOI musí mít tvar 10.xxxx/sufix.", "Bibliography": "Bibliografie", "The numbered reference list; cite entries as [n] in the body": "Číslovaný seznam literatury; v textu citujte jako [n]", "No references yet.": "Zatím žádné reference.", "Add reference": "Přidat referenci", "Insert [[refs]] marker into the body": "Vložit do textu značku [[refs]]", "Fill the verbatim citation line, or the structured fields; the marker places the list in the body and without it the list is appended.": "Vyplňte doslovný citovaný řádek nebo strukturovaná pole; značka umístí seznam do textu a bez něj se seznam připojí na konec.", "Move up": "Posunout výš", "Move down": "Posunout níž", "Remove reference": "Odebrat referenci", "Verbatim citation": "Doslovná citace", "Structured fields": "Strukturovaná pole", "One author per line, optionally with [orcid:0000-0002-1825-0097]": "Každý autor na vlastním řádku, volitelně s [orcid:0000-0002-1825-0097]", "Authors": "Autoři", "Journal or proceedings": "Časopis nebo sborník", "Venue": "Místo publikace", "Year": "Rok", "Volume": "Ročník", "Pages": "Strany", "arXiv": "arXiv", "Author, title, venue, year": "Autor, titul, kde vyšlo, rok", "Publish date": "Datum publikace", "Schedule for": "Naplánovat na", "Pick a date": "Vyberte datum", "Leave empty to publish immediately. Date is interpreted as your local timezone.": "Necháte-li prázdné, publikuje se hned. Datum se interpretuje v místním časovém pásmu.", "Markdown": "Markdown", "Visual": "Vizuální", "Tags": "Značky", "comma, separated": "čárkami, oddělené", "Series": "Série", "e.g. rust-tutorial": "např. rust-tutorial", "Group multi-part posts; leave empty for standalone posts": "Sdružuje díly publikací; pro samostatné nechte prázdné", "Part number": "Číslo dílu", "Order within the series": "Pořadí v sérii", "Excerpt": "Perex", "Auto-generated from the first paragraph if left empty": "Nevyplněno: vygeneruje se z prvního odstavce", "Available in all languages": "Dostupné ve všech jazycích", "Cover image": "Úvodní obrázek", "Header image shown on the article page": "Obrázek v záhlaví stránky článku", "/media/… or https://…": "/media/… nebo https://…", "Upload": "Nahrát", "Clear": "Vymazat", "Today": "Dnes", "ALT text (for accessibility)": "ALT text (pro přístupnost)", "Caption (e.g. 'Generated by AI')": "Popisek (např. „Vygenerováno AI“)", "Write your post in Markdown…": "Pište publikaci v Markdownu…", "Drop image to upload": "Pusťte obrázek a nahrajte", "Bold (Ctrl+B)": "Tučně (Ctrl+B)", "Italic (Ctrl+I)": "Kurzíva (Ctrl+I)", "Heading 2": "Nadpis 2", "Heading 3": "Nadpis 3", "Link (Ctrl+K)": "Odkaz (Ctrl+K)", "Bullet list": "Odrážky", "Numbered list": "Číslovaný seznam", "Quote": "Citace", "Code": "Kód", "Image": "Obrázek", "Toggle preview": "Přepnout náhled", "✓ saved": "✓ uloženo", "Keyboard shortcuts": "Klávesové zkratky", "Bold": "Tučně", "Italic": "Kurzíva", "Link": "Odkaz", "Save post": "Uložit publikaci", "Show this help": "Zobrazit tuto nápovědu", "Close modal": "Zavřít okno", "Editor": "Editor", "Unsaved changes from %s are available.": "K dispozici jsou neuložené změny z %s.", "Slug is available.": "Slug je volný.", "Already used by “%s”.": "Už ho používá „%s“.", "Will be published immediately (date is today or earlier).": "Publikuje se okamžitě (datum je dnešní nebo dřívější).", "Link URL": "Adresa odkazu", "Only WebP, AVIF and SVG images are accepted (got “%s”).": "Přijímají se jen obrázky WebP, AVIF a SVG (obdrženo „%s“).", "Upload failed:\n\n%s": "Nahrání selhalo:\n\n%s", // History. "Back to editor": "Zpět do editoru", "No revisions yet": "Zatím žádné verze", "Every time this post is saved, the previous version is archived here.": "Při každém uložení publikace se předchozí verze archivuje tady.", "Saved": "Uloženo", "Size": "Velikost", "Actions": "Akce", "Restore this revision? The current content will be archived first.": "Obnovit tuto verzi? Současný obsah se nejdřív archivuje.", "Compare": "Porovnat", "Back to history": "Zpět do historie", "Changes": "Změny", "Archived version": "Archivovaná verze", "compared with the current content.": "srovnáno se současným obsahem.", "No differences from the current content.": "Žádné rozdíly oproti současnému obsahu.", "removed": "odebráno", "added": "přidáno", "Upcoming": "Nadcházející", // Import. "Import post": "Import publikace", "frontmatter": "hlavička", "Bring a post written anywhere: a Markdown file with optional TOML frontmatter.": "Přineste publikaci psanou kdekoliv: soubor Markdown s volitelnou TOML hlavičkou.", "Drop your post here": "Pusťte sem svou publikaci", "or click to browse": "nebo klikněte a vyberte", "Remove": "Odebrat", "No frontmatter found; the slug will be derived from the file name.": "Hlavička nenalezena; slug se odvodí z názvu souboru.", "Only .md files are accepted, got “%s”": "Přijímají se jen soubory .md, obdrženo „%s“", // Media. "No media yet": "Zatím žádná média", "Upload an image to use as a cover or inline content. WebP, AVIF and SVG are supported.": "Nahrajte obrázek jako úvodní nebo do obsahu. Podporovány jsou WebP, AVIF a SVG.", "You can also paste or drop an image directly into the editor body.": "Obrázek můžete také vložit nebo přetáhnout přímo do těla editoru.", "Upload images": "Nahrát obrázky", "Drop images here, or click to browse": "Pusťte sem obrázky, nebo klikněte a vyberte", "WebP, AVIF or SVG, up to 10 MB each.": "WebP, AVIF nebo SVG, každý nejvýše 10 MB.", "Search files...": "Hledat soubory…", "No files match your search.": "Žádný soubor neodpovídá hledání.", "Copy link": "Kopírovat odkaz", "Open in new tab": "Otevřít v nové kartě", "Link copied.": "Odkaz zkopírován.", "Copy the link:": "Zkopírujte odkaz:", "Upload failed: %s": "Nahrání selhalo: %s", "Upload complete.": "Nahrání dokončeno.", "Uploading %s…": "Nahrávám %s…", // Settings, shared. "Manage your account and users": "Spravujte svůj účet a uživatele", "Account": "Účet", "Users": "Uživatelé", "Templates": "Šablony", "Backup": "Záloha", "Version": "Verze", "Webhooks": "Webhooky", "API tokens": "API tokeny", "Signed in as": "Přihlášen jako", // Account section. "Profile photo": "Profilová fotka", "WebP, AVIF or SVG, max 10 MB. Shown in the sidebar.": "WebP, AVIF nebo SVG, nejvýše 10 MB. Zobrazuje se v postranní liště.", "Identity": "Identita", "Display name": "Zobrazované jméno", "Your real name": "Vaše skutečné jméno", "Save name": "Uložit jméno", "Fediverse handle": "Fediverse účet", "Save handle": "Uložit účet", "Security": "Zabezpečení", "Change your password and username. You can also lock yourself out.": "Změňte heslo i uživatelské jméno. Můžete si i sami zablokovat přístup.", "Current password": "Současné heslo", "New password": "Nové heslo", "Update password": "Změnit heslo", "Update username": "Změnit uživatele", "Language version": "Jazyková verze", "The interface language of your account. The login screen follows your last choice.": "Jazyk rozhraní vašeho účtu. Přihlašovací stránka se řídí poslední volbou.", "Unsupported language.": "Nepodporovaný jazyk.", "The language could not be saved: %s": "Jazyk nešlo uložit: %s", "Colour scheme": "Barevné schéma", "The colour scheme of your account. The login screen follows your last choice.": "Barevné schéma vašeho účtu. Přihlašovací stránka se řídí poslední volbou.", "Unsupported colour scheme.": "Nepodporované barevné schéma.", "The colour scheme could not be saved: %s": "Barevné schéma nešlo uložit: %s", "The colour scheme is set.": "Barevné schéma je nastaveno.", // Users section. "Add or remove admin and editor accounts": "Přidávejte a odstraňujte účty správců a autorů", "you": "vy", "Remove this user?": "Odebrat tohoto uživatele?", "Add user": "Přidat uživatele", "Role": "Role", "jane-doe": "jana-novakova", // Templates section. "Post templates": "Šablony publikací", "Pre-fill new posts with a reusable structure": "Předvyplňují nové publikace opakovaně použitelnou strukturou", "No templates yet. Templates pre-fill the new-post form so you can keep your favourite structure on hand.": "Zatím žádné šablony. Předvyplňují formulář nové publikace, abyste měli svou oblíbenou strukturu po ruce.", "Name": "Název", "Fields to pre-fill": "Předvyplněná pole", "Extra editor inputs as TOML key = value lines, one per line, strings quoted: author, lang, doi, orcid, series, series_order, cover, excerpt.": "Doplňující hodnoty editoru jako řádky TOML key = value, jeden na řádek, řetězce v uvozovkách: author, lang, doi, orcid, series, series_order, cover, excerpt.", "Template fields must be key = value TOML lines.": "Pole šablony musí být řádky TOML key = value.", "Unknown template field %s.": "Neznámé pole šablony %s.", "Default title": "Výchozí titulek", "Optional": "Volitelné", "Default slug": "Výchozí slug", "Tags (comma-separated)": "Značky (oddělené čárkou)", "Body template (Markdown)": "Šablona obsahu (Markdown)", "e.g. Review": "např. Recenze", "Add template": "Přidat šablonu", // Backup section. "Backup & restore": "Záloha a obnova", "Export or import all posts, media, users, and templates": "Exportujte nebo importujte všechny publikace, média, uživatele i šablony", "Export": "Export", "Download backup": "Stáhnout zálohu", "Restore from a previously exported archive. Overwrites existing data.": "Obnova z dříve exportovaného archivu. Přepíše existující data.", "This will overwrite existing posts and users. Continue?": "Tím se přepíšou existující publikace i uživatelé. Pokračovat?", "Restore backup": "Obnovit ze zálohy", // Version section. "Keep Volumen up to date": "Udržujte Volumen aktuální", "Installed version": "Nainstalovaná verze", "Running": "Provozuji", "latest": "nejnovější", "Update to %s": "Aktualizovat na %s", "Check for updates": "Zkontrolovat aktualizace", // Webhooks section. "Notify external services when posts change": "Upozorněte externí služby na změny publikací", "Endpoints": "Koncové body", "Signed JSON payloads are POSTed on post changes. Changes apply without a restart.": "Podepsané zprávy JSON se posílají při změnách publikací. Změny se projeví bez restartu.", "Events": "Události", "Send test": "Poslat test", "disabled": "vypnuto", "signed": "podepsáno", "from config": "z konfigurace", "Add webhook": "Přidat webhook", "Endpoint URL": "URL koncového bodu", "Signing secret (optional)": "Podpisový tajný klíč (nepovinný)", "Events (comma-separated, empty for all)": "Události (oddělené čárkou, prázdné znamená všechny)", "Enabled": "Zapnuto", "Add": "Přidat", "Enable": "Zapnout", "Disable": "Vypnout", "Remove this webhook?": "Odebrat tento webhook?", "Webhook added.": "Webhook přidán.", "Webhook updated.": "Webhook uložen.", "Webhook removed.": "Webhook odebrán.", "That URL is not a valid http(s) endpoint.": "Tato URL není platný koncový bod http(s).", "That URL is already configured.": "Tato URL už je nastavená.", "The webhook store could not be read: %s": "Soubor webhooků se nepodařilo přečíst: %s", "The webhook could not be saved: %s": "Webhook se nepodařilo uložit: %s", "Recent deliveries": "Nedávná doručení", "Kept in memory; cleared on restart.": "Uchovaná v paměti; po restartu smazána.", "When": "Kdy", "Event": "Událost", "Endpoint": "Kam", "Result": "Výsledek", // Tokens section. "Programmatic write access to the public API": "Programový zápis do veřejného API", "Access tokens": "Přístupové tokeny", "Copy this token now; it will never be shown again.": "Token si zkopírujte hned; znovu se nezobrazí.", "No tokens yet. Create one to publish posts from scripts or CI.": "Zatím žádné tokeny. Vytvořte si jeden pro publikování ze skriptů nebo CI.", "Created": "Vytvořeno", "Last used": "Naposledy", "Revoke token “%s”? Scripts using it will stop working.": "Odvolat token „%s“? Skripty, které ho používají, přestanou fungovat.", "Revoke": "Odvolat", "Token name": "Název tokenu", "e.g. deploy-script": "např. deploy-script", "Scopes": "Oprávnění", "Write": "Zápis", "Write creates and edits posts; delete removes them. Read endpoints are public, so no scope is needed for them. Selecting neither grants full access.": "Zápis vytváří a upravuje publikace; mazání je odstraňuje. Čtení je veřejné, oprávnění nepotřebuje. Bez výběru platí plný přístup.", "Create token": "Vytvořit token", // Update page. "The server is restarting. This page will reload automatically.": "Server se restartuje. Tato stránka se obnoví sama.", "Server is back; reloading.": "Server je zpět; obnovuji.", "The server did not come back within 3 minutes. Check the service manually.": "Server se do 3 minut nevrátil. Zkontrolujte službu ručně.", // Date picker. "Previous month": "Předchozí měsíc", "Next month": "Další měsíc", // Editor page actions and crumbs. "Select %s": "Vybrat: %s", "Updated to": "Aktualizováno na", "Short summary for listings and previews": "Krátké shrnutí pro výčty a náhledy", "Volumen admin": "Volumen administrace", // Editor crumbs and roles. "Untitled": "Bez názvu", "%d of %d": "%d z %d", "admin": "administrátor", "author": "autor", "date must be an ISO 8601 date.": "Datum musí být ve tvaru ISO 8601 (RRRR-MM-DD).", "publish_at must be an ISO 8601 date.": "Datum publikace musí být ve tvaru ISO 8601 (RRRR-MM-DD).", // Go handler messages: posts. "No file selected.": "Nevybrán žádný soubor.", "No file was uploaded.": "Nevybrán žádný soubor.", "The upload could not be stored.": "Nahraný soubor nešlo uložit.", "Only .md files are accepted.": "Přijímají se jen soubory .md.", "File is too large.": "Soubor je příliš velký.", "The file could not be read as a post: %s": "Soubor nešlo přečíst jako publikaci: %s", "Import failed.": "Import selhal.", "The file could not be read (limit %s bytes).": "Soubor nešlo přečíst (limit %s bajtů).", "Only WebP, AVIF and SVG images are supported.": "Podporovány jsou jen obrázky WebP, AVIF a SVG.", "The post could not be saved: %s": "Publikaci nešlo uložit: %s", "The post could not be deleted: %s": "Publikaci nešlo smazat: %s", "Slug is required.": "Slug je povinný.", "Invalid slug.": "Neplatný slug.", "Invalid language.": "Neplatný jazyk.", "A post with that slug already exists.": "Publikace s tímto slugem už existuje.", "Fediverse creator must look like @user@host.": "Autor fediverse musí mít tvar @uzivatel@hostitel.", // Go handler messages: login and security. "Invalid CSRF token": "Neplatný CSRF token", // Go handler messages: settings, account. "Current password is incorrect.": "Současné heslo není správné.", "New password cannot be empty.": "Nové heslo nesmí být prázdné.", "The new password could not be saved: %s": "Nové heslo nešlo uložit: %s", "Password updated.": "Heslo změněno.", "Username cannot be empty.": "Uživatelské jméno nesmí být prázdné.", "Username may use letters, numbers, dot, dash, underscore.": "Uživatelské jméno smí používat písmena, čísla, tečku, pomlčku a podtržítko.", "Username unchanged.": "Uživatelské jméno nezměněno.", "The username could not be changed: %s": "Uživatelské jméno nešlo změnit: %s", "Username updated.": "Uživatelské jméno změněno.", "The display name could not be saved: %s": "Zobrazované jméno nešlo uložit: %s", "Display name cleared.": "Zobrazované jméno vymazáno.", "Display name updated.": "Zobrazované jméno uloženo.", "The handle could not be saved: %s": "Fediverse účet nešlo uložit: %s", "Fediverse handle cleared.": "Fediverse účet vymazán.", "Fediverse handle must look like @user@host.": "Fediverse účet musí mít tvar @uzivatel@hostitel.", "Fediverse handle updated.": "Fediverse účet uložen.", "ORCID iD": "ORCID iD", "Save ORCID": "Uložit ORCID", "ORCID updated.": "ORCID uložen.", "ORCID cleared.": "ORCID vymazán.", "The ORCID could not be saved: %s": "ORCID nešlo uložit: %s", "ORCID must look like 0000-0002-1825-0097.": "ORCID musí mít tvar 0000-0002-1825-0097.", "Your Open Researcher and Contributor ID; it pre-fills the author field of new publications.": "Váš Open Researcher and Contributor ID; předvyplňuje pole autora nových publikací.", "Display name pre-fills the author field. Fediverse handle pre-fills the creator, and the ORCID pre-fills the author identifier of new publications.": "Zobrazované jméno předvyplňuje pole autora, fediverse účet tvůrce a ORCID identifikátor autora nových publikací.", "The photo could not be stored.": "Fotku nešlo uložit.", "The profile photo could not be saved: %s": "Profilovou fotku nešlo uložit: %s", "Profile photo updated.": "Profilová fotka uložena.", "The profile photo could not be removed: %s": "Profilovou fotku nešlo odstranit: %s", "Profile photo removed.": "Profilová fotka odstraněna.", "The interface language is set.": "Jazyk rozhraní je nastaven.", // Go handler messages: settings, users. "Username and password are required.": "Uživatelské jméno a heslo jsou povinné.", "That user could not be added: %s": "Uživatele nešlo přidat: %s", "User added.": "Uživatel přidán.", "You cannot change your own role.": "Vlastní roli nelze změnit.", "The role could not be changed: %s": "Roli nešlo změnit: %s", "Role updated.": "Role uložena.", "You cannot delete your own account.": "Vlastní účet nelze smazat.", "The user could not be removed: %s": "Uživatele nešlo odebrat: %s", "User removed.": "Uživatel odebrán.", "Reset password": "Resetovat heslo", "You cannot reset your own password here.": "Vlastní heslo tady resetovat nemůžete.", "That user was not found.": "Takový uživatel nebyl nalezen.", "Password reset; that user's sessions were signed out.": "Heslo resetováno; relace toho uživatele byly odhlášeny.", "Reset this user's password? Their sessions will be signed out.": "Resetovat heslo tohoto uživatele? Jeho relace budou odhlášeny.", // Go handler messages: settings, templates and tokens. "Template name is required.": "Název šablony je povinný.", "That template could not be added: %s": "Šablonu nešlo přidat: %s", "Template added.": "Šablona přidána.", "The template could not be deleted: %s": "Šablonu nešlo smazat: %s", "Template deleted.": "Šablona smazána.", "The token could not be created: %s": "Token nešlo vytvořit: %s", "Token revoked.": "Token odvolán.", "That token was not found.": "Token nenalezen.", // Go handler messages: settings, webhooks, backup and version. "No webhooks configured.": "Žádné webhooky nenastaveny.", "Webhook not found.": "Webhook nenalezen.", "Test delivery sent.": "Testovací doručení odesláno.", "Test delivery failed.": "Testovací doručení selhalo.", "No backup file selected.": "Nevybrán žádný soubor zálohy.", "Could not restore backup: %s": "Zálohu nešlo obnovit: %s", "The archive holds no files this deployment recognises.": "Archiv neobsahuje žádné soubory, které by tahle instalace poznala.", "Update checks are not available in this build.": "Kontrola aktualizací není v tomto buildu dostupná.", "Update check failed: %s": "Kontrola aktualizací selhala: %s", "volumen %s is already the latest release.": "Volumen %s je už nejnovější vydání.", "volumen %s is available.": "Volumen %s je k dispozici.", "Self-update is not available in this build.": "Automatická aktualizace není v tomto buildu dostupná.", "The upgrade failed: %s": "Aktualizace selhala: %s", "Upgrade to %s failed: %s": "Aktualizace na %s selhala: %s", } // csHTML translates the static sentences that only make sense with // their markup whole. They never carry an interpolated value, so // returning them unescaped is safe. var csHTML = map[string]string{ "On macOS use Cmd instead of Ctrl.": "Na macOS použijte Cmd místo Ctrl.", "Download a .tar.gz archive of your entire site.": "Stáhněte .tar.gz archiv celého webu.", "No webhooks yet. Add an endpoint below, or declare [[webhooks]] blocks in config.toml (those are read-only here):": "Žádné webhooky. Přidejte koncový bod níže, nebo deklarujte bloky [[webhooks]] v config.toml (ty tady jsou jen ke čtení):", "Send a token as Authorization: Bearer <token> on POST/PUT/DELETE /api/volumen/posts.": "Token posílejte jako Authorization: Bearer <token> na POST/PUT/DELETE /api/volumen/posts.", } // Catalog translates the admin interface. type Catalog struct{} // Admin is the catalogue the binary ships. var Admin = Catalog{} // T translates a simple message. The key is the English text; an // unknown key or the "en" language returns the key itself, so English // needs no table and a missing translation degrades to English rather // than to an identifier. func (Catalog) T(lang, s string) string { if lang != "cs" { return s } if out, ok := cs[s]; ok && out != "" { return out } return s } // TH translates a static sentence that carries its own markup. The // sentences are authored here, never interpolated, so returning them // unescaped cannot smuggle request data into the page. func (Catalog) TH(lang, s string) string { if lang != "cs" { return s } if out, ok := csHTML[s]; ok { return out } return s } // Tf translates a simple message with one %s value. The result stays a // plain string, so the template engine escapes the interpolated value. func (c Catalog) Tf(lang, s, arg string) string { t := c.T(lang, s) if before, after, ok := strings.Cut(t, "%s"); ok { return before + arg + after } return t } // Tf2 translates a simple message with two %s values. func (c Catalog) Tf2(lang, s, first, second string) string { t := c.T(lang, s) for _, arg := range []string{first, second} { if i := strings.Index(t, "%s"); i >= 0 { t = t[:i] + arg + t[i+2:] } } return t } // N translates a numbered message, picking the form the language's // cardinal rule asks for. Czech follows CLDR: one for 1, few for the // 2 to 4 class except 12 to 14, other otherwise. func (c Catalog) N(lang, id string, n int) string { forms, ok := pluralForms[id] if !ok { return id } set := forms["en"] if lang != "en" { if l := forms[lang]; l.one != "" || l.other != "" { set = l } } var form string switch { case n == 1: form = set.one case lang == "cs" && n%10 >= 2 && n%10 <= 4 && (n%100 < 12 || n%100 > 14): form = set.few default: form = set.other } // A plural set without a few form (an id added with English forms // only) would otherwise render an empty string for the 2 to 4 class. if form == "" { form = set.other } num := strconv.Itoa(n) if i := strings.Index(form, "%d"); i >= 0 { return form[:i] + num + form[i+2:] } return form } // JS returns the strings the page scripts need, as a map ready for // JSON encoding into the page. Plural messages carry their three // forms; the client picks one with the same cardinal rule as N. func (c Catalog) JS(lang string) map[string]any { if lang == "" { lang = "en" } out := map[string]any{} for _, id := range []string{ "editor.words", "editor.chars", "editor.reading", "bulk.selected", "posts.count", "refs.count", } { set := pluralForms[id]["en"] if lang != "en" { if l := pluralForms[id][lang]; l.one != "" || l.other != "" { set = l } } out[id] = []string{set.one, set.few, set.other} } plain := map[string]string{ "post.created": c.T(lang, "Post created."), "post.updated": c.T(lang, "Post saved."), "post.deleted": c.T(lang, "Post deleted."), "post.duplicated": c.T(lang, "Post duplicated."), "post.undone": c.T(lang, "Post restored."), "post.undelete_failed": c.T(lang, "Could not restore the post; it may already be back."), "post.duplicate_failed": c.T(lang, "The post could not be duplicated."), "undo": c.T(lang, "Undo"), "undo.failed": c.T(lang, "Undo failed."), "autosave.available": c.T(lang, "Unsaved changes from %s are available."), "slug.ok": c.T(lang, "Slug is available."), "slug.taken": c.T(lang, "Already used by “%s”."), "upload.prefix": c.T(lang, "Upload failed:\n\n%s"), "upload.rejected": c.T(lang, "Only WebP, AVIF and SVG images are accepted (got “%s”)."), "link.prompt": c.T(lang, "Link URL"), "Show password": c.T(lang, "Show password"), "Hide password": c.T(lang, "Hide password"), "Collapse sidebar": c.T(lang, "Collapse sidebar"), "Expand sidebar": c.T(lang, "Expand sidebar"), "login.unlock": c.T(lang, "Auto-unlock in {} s."), "login.retry": c.T(lang, " You can try again now."), "update.reload": c.T(lang, "Server is back; reloading."), "update.gaveup": c.T(lang, "The server did not come back within 3 minutes. Check the service manually."), "datepicker.prev": c.T(lang, "Previous month"), "datepicker.next": c.T(lang, "Next month"), "Pick a date": c.T(lang, "Pick a date"), "Clear": c.T(lang, "Clear"), "Today": c.T(lang, "Today"), "filter.of": c.T(lang, "%d of %d"), "import.rejected": c.T(lang, "Only .md files are accepted, got “%s”"), "media.uploading": c.T(lang, "Uploading %s…"), "media.upload_failed": c.T(lang, "Upload failed: %s"), "media.copied": c.T(lang, "Link copied."), "media.copy_prompt": c.T(lang, "Copy the link:"), "upload.complete": c.T(lang, "Upload complete."), "pw.weak": c.T(lang, "Weak"), "pw.fair": c.T(lang, "Fair"), "pw.good": c.T(lang, "Good"), "pw.strong": c.T(lang, "Strong"), } for id, v := range plain { out[id] = v } return out }