i18n: Czech interface language

VectorCraft ▸ Language gains Čeština (`app.language {lang: "cs"}`,
persisted as "cs"). `Language::tr` now picks a table per language.

The Czech table (crates/ui-egui/src/i18n/czech.rs) translates every
menu title, submenu, item and header in the menu tree, plus the labels
toggling items switch to (Show/Hide Guides, Lock/Unlock Grid, Outline/
Preview, Edit Contents/Edit Envelope…). It was written clean-room from the
meaning of the English labels: commands in the infinitive, menus as nouns.
Left as-is on purpose: the product name, language names, OpenType, the
built-in workspace names and the perspective grid preset names.

Tests:
- every Czech (and Japanese) entry is a menu label, toggled labels included;
- Czech covers every menu label except fonts, sizes, plug-in names and
  an explicit KEEP_AS_IS list;
- ellipses match their source label, entries are unique;
- the Language menu lists every language; `cs` validates and persists;
- each UI font family's own first font has the Czech letters and
  punctuation (no system fonts needed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Petr Leo Compel 2026-10-06 14:23:11 +02:00
parent 899a82d31d
commit b045390311
5 changed files with 784 additions and 36 deletions

View File

@ -112,7 +112,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
- Pathfinder (10 exact curve booleans), Offset, Outline Stroke, Simplify, Clean Up, Split Into Grid, Divide Objects Below. - Pathfinder (10 exact curve booleans), Offset, Outline Stroke, Simplify, Clean Up, Split Into Grid, Divide Objects Below.
- Live effects with previewing dialogs: Distort & Transform, Path, Convert to Shape, 15 Warp styles, Round Corners, Scribble, Effect → Pathfinder (all 10 operations, live on groups), Color Adjustments (Brightness/Contrast, Curves, Levels, Hue/Saturation, Shift to Color, Temperature/Tint, on vectors, live type and embedded images), and raster drop shadow, glows and feather. SVG and PDF export keep live effects (geometry baked; SVG raster effects as filters). - Live effects with previewing dialogs: Distort & Transform, Path, Convert to Shape, 15 Warp styles, Round Corners, Scribble, Effect → Pathfinder (all 10 operations, live on groups), Color Adjustments (Brightness/Contrast, Curves, Levels, Hue/Saturation, Shift to Color, Temperature/Tint, on vectors, live type and embedded images), and raster drop shadow, glows and feather. SVG and PDF export keep live effects (geometry baked; SVG raster effects as filters).
- **Type:** Text Wrap, Type on a Path effects (Rainbow/Skew/3D Ribbon/Stair Step/Gravity), Character and Paragraph Styles (override-preserving redefine), Area Type Options (rows/columns/inset/first baseline), threaded text across any closed shapes, Fit Headline. - **Type:** Text Wrap, Type on a Path effects (Rainbow/Skew/3D Ribbon/Stair Step/Gravity), Character and Paragraph Styles (override-preserving redefine), Area Type Options (rows/columns/inset/first baseline), threaded text across any closed shapes, Fit Headline.
- **Japanese interface and vertical type:** VectorCraft ▸ Language switches the menus between English and Japanese (untranslated labels stay English) and persists; the bundled OFL Shippori Mincho gives Japanese glyphs without system fonts. Vertical Type, Vertical Area Type and Vertical Type on a Path create vertical text (columns right to left, upright CJK glyphs with `vert`/`vrt2` alternates); Type ▸ Type Orientation switches existing text; caret, selection, hit testing and arrow keys follow the writing direction. Vertical text exports to PDF as real text and to SVG as outlines. - **Japanese and Czech interface, vertical type:** VectorCraft ▸ Language switches the menus between English, Japanese and Czech (untranslated labels stay English; Czech covers every menu label, tested) and persists; the bundled OFL Shippori Mincho gives Japanese glyphs and the bundled UI fonts Czech ones without system fonts. Vertical Type, Vertical Area Type and Vertical Type on a Path create vertical text (columns right to left, upright CJK glyphs with `vert`/`vrt2` alternates); Type ▸ Type Orientation switches existing text; caret, selection, hit testing and arrow keys follow the writing direction. Vertical text exports to PDF as real text and to SVG as outlines.
- **Transparency:** opacity masks (clip/invert/disable/link), exported as SVG `<mask>` and PDF soft masks. - **Transparency:** opacity masks (clip/invert/disable/link), exported as SVG `<mask>` and PDF soft masks.
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type, Gradient Mesh, Shape Builder, Live Paint, Image Trace (12 presets), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror). - **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type, Gradient Mesh, Shape Builder, Live Paint, Image Trace (12 presets), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror).
- **Paint and appearance (M3):** - **Paint and appearance (M3):**

View File

@ -20,6 +20,9 @@ pub const KIND: &str = "plugin";
const CMD: &str = "plugin.run"; const CMD: &str = "plugin.run";
/// Object › Plug-ins' header while no filter plug-in is installed.
pub const NO_FILTERS: &str = "No filter plug-ins installed";
pub(super) const SPEC: DialogSpec = pub(super) const SPEC: DialogSpec =
DialogSpec { heading: |d| d.str("__label"), body, confirm, preview: true, max_width: Some(420.0), ..DialogSpec::FORM }; DialogSpec { heading: |d| d.str("__label"), body, confirm, preview: true, max_width: Some(420.0), ..DialogSpec::FORM };
@ -37,7 +40,7 @@ pub fn object_menu() -> Vec<Item> {
.map(|p| Item::Cmd(label(&p.manifest().name, !p.manifest().params.is_empty()), "plugin.dialog", json!({ "id": p.id() }))) .map(|p| Item::Cmd(label(&p.manifest().name, !p.manifest().params.is_empty()), "plugin.dialog", json!({ "id": p.id() })))
.collect(); .collect();
if items.is_empty() { if items.is_empty() {
items.push(Item::Header("No filter plug-ins installed")); items.push(Item::Header(NO_FILTERS));
} }
items.extend([ items.extend([
Item::Sep, Item::Sep,

View File

@ -2,21 +2,26 @@
//! Untranslated labels fall back to English so coverage can grow incrementally. Every entry is a //! Untranslated labels fall back to English so coverage can grow incrementally. Every entry is a
//! label VectorCraft's menus show (tested), so labels from other apps don't creep in. //! label VectorCraft's menus show (tested), so labels from other apps don't creep in.
mod czech;
use czech::CZECH;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum Language { pub enum Language {
#[default] #[default]
En, En,
Ja, Ja,
Cs,
} }
impl Language { impl Language {
pub const ALL: [Self; 2] = [Self::En, Self::Ja]; pub const ALL: [Self; 3] = [Self::En, Self::Ja, Self::Cs];
pub fn name(self) -> &'static str { pub fn name(self) -> &'static str {
match self { match self {
Self::En => "English", Self::En => "English",
Self::Ja => "日本語", Self::Ja => "日本語",
Self::Cs => "Čeština",
} }
} }
@ -24,17 +29,18 @@ impl Language {
match code { match code {
"en" => Some(Self::En), "en" => Some(Self::En),
"ja" => Some(Self::Ja), "ja" => Some(Self::Ja),
"cs" => Some(Self::Cs),
_ => None, _ => None,
} }
} }
pub fn tr(self, text: &str) -> &str { pub fn tr(self, text: &str) -> &str {
if self == Self::Ja let table = match self {
&& let Some((_, japanese)) = JAPANESE.iter().find(|(english, _)| *english == text) Self::En => return text,
{ Self::Ja => JAPANESE,
return japanese; Self::Cs => CZECH,
} };
text table.iter().find(|(english, _)| *english == text).map_or(text, |(_, translated)| translated)
} }
} }
@ -79,55 +85,187 @@ const JAPANESE: &[(&str, &str)] = &[
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serde_json::{Value, json};
use super::*; use super::*;
use crate::menus::Item;
#[test] const TABLES: [(Language, &[(&str, &str)]); 2] = [(Language::Ja, JAPANESE), (Language::Cs, CZECH)];
fn translations_are_unique_and_preserve_unknown_text() {
for (i, (en, ja)) in JAPANESE.iter().enumerate() {
assert!(!ja.is_empty());
assert!(JAPANESE.iter().take(i).all(|(other, _)| en != other));
assert_eq!(Language::En.tr(en), *en);
}
assert_eq!(Language::Ja.tr("File"), "ファイル");
assert_eq!(Language::Ja.tr("日本語の文書.pdf"), "日本語の文書.pdf");
assert_eq!(Language::parse("xx"), None);
}
#[test] /// Menu labels Czech shows as they are: the product name, language names, a format name, the
fn every_translated_label_is_a_menu_label() { /// built-in workspace names and the perspective grid presets (names, shown untranslated
fn walk(items: &[crate::menus::Item], out: &mut Vec<&'static str>) { /// wherever else they appear).
const KEEP_AS_IS: &[&str] = &[
"VectorCraft",
"English",
"日本語",
"Čeština",
"OpenType",
"Essentials",
"Essentials Classic",
"Automation",
"Layout",
"Painting",
"Printing and Proofing",
"Tracing",
"Typography",
"Web",
"[1P-Normal View]",
"[1P-Low View]",
"[1P-High View]",
"[2P-Normal View]",
"[2P-Low View]",
"[2P-High View]",
"[3P-Normal View]",
"[3P-Low View]",
];
/// Every menu title, submenu, item and header label with its command and params (`""`/null
/// for the ones that run nothing).
fn menu_labels() -> Vec<(&'static str, &'static str, Value)> {
fn walk(items: &[Item], out: &mut Vec<(&'static str, &'static str, Value)>) {
for i in items { for i in items {
match i { match i {
crate::menus::Item::Cmd(l, ..) | crate::menus::Item::Todo(l, _) | crate::menus::Item::Header(l) => out.push(l), Item::Cmd(l, id, p) => out.push((l, id, p.clone())),
crate::menus::Item::Sub(l, children) => { Item::Todo(l, _) | Item::Header(l) => out.push((l, "", Value::Null)),
out.push(l); Item::Sub(l, children) => {
out.push((l, "", Value::Null));
walk(children, out); walk(children, out);
} }
crate::menus::Item::Sep => {} Item::Sep => {}
} }
} }
} }
let mut labels = vec![]; let mut labels = vec![];
for (title, items) in crate::menus::menu_tree() { for (title, items) in crate::menus::menu_tree() {
labels.push(title); labels.push((title, "", Value::Null));
walk(&items, &mut labels); walk(&items, &mut labels);
} }
for (en, _) in JAPANESE { labels
assert!(labels.contains(en), "`{en}` isn't a VectorCraft menu label"); }
/// The labels toggling items switch to ("Show Guides" once guides are hidden…), from a
/// document with every toggle flipped.
fn toggled_labels() -> Vec<String> {
let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap();
app.run("object.envelope.makeWithWarp", json!({})).unwrap();
let toggles = [
"view.outline",
"view.edges",
"view.cornerWidget",
"view.textThreads",
"type.hiddenCharacters",
"view.gradientAnnotator",
"view.artboards",
"view.rulers",
"view.boundingBox",
"view.transparencyGrid",
"view.guides",
"view.grid",
"view.guides.lock",
"view.slices.hide",
"view.printTiling",
"perspective.grid.show",
"perspective.grid.rulers",
"perspective.grid.lock",
"object.envelope.editContents",
];
let mut labels = vec![];
for id in toggles {
let before = crate::menus::dynamic_label(&app, id, "");
app.run(id, json!({})).unwrap();
let after = crate::menus::dynamic_label(&app, id, "");
assert_ne!(before, after, "{id} didn't toggle its label");
labels.extend([before, after]);
}
labels
}
#[test]
fn translations_are_unique_and_preserve_unknown_text() {
for (language, table) in TABLES {
for (i, (en, translated)) in table.iter().enumerate() {
assert!(!translated.is_empty());
assert!(table.iter().take(i).all(|(other, _)| en != other), "`{en}` twice");
assert_eq!(en.ends_with('…'), translated.ends_with('…'), "`{en}` and `{translated}` differ in their ellipsis");
assert_eq!(Language::En.tr(en), *en);
assert_eq!(language.tr(en), *translated);
}
assert_eq!(language.tr("日本語の文書.pdf"), "日本語の文書.pdf");
}
assert_eq!(Language::Ja.tr("File"), "ファイル");
assert_eq!(Language::Cs.tr("File"), "Soubor");
assert_eq!(Language::parse("xx"), None);
for language in Language::ALL {
let code = serde_json::to_value(language).unwrap();
assert_eq!(Language::parse(code.as_str().unwrap()), Some(language));
}
assert_eq!(serde_json::to_value(Language::Cs).unwrap(), json!("cs"));
assert_eq!(Language::Cs.name(), "Čeština");
}
#[test]
fn every_translated_label_is_a_menu_label() {
let mut labels: Vec<String> = menu_labels().into_iter().map(|(l, ..)| l.to_string()).collect();
// Object › Plug-ins' header leaves the menu while any test has a filter plug-in installed.
labels.push(crate::dialogs::plugin::NO_FILTERS.into());
labels.extend(toggled_labels());
for (language, table) in TABLES {
for (en, _) in table {
assert!(labels.iter().any(|l| l == en), "{language:?}: `{en}` isn't a VectorCraft menu label");
}
}
}
#[test]
fn czech_translates_every_menu_label() {
let translated = |l: &str| CZECH.iter().any(|(en, _)| *en == l);
let labels = menu_labels();
// Font names and sizes, and installed plug-ins' own names, are not interface text.
let interface = |(label, id, p): &(&str, &str, Value)| {
let plugin = *id == "plugin.dialog" || p.get("effect").and_then(Value::as_str).is_some_and(|e| e.starts_with("plugin."));
*id != "text.setStyle" && !plugin && !KEEP_AS_IS.contains(label)
};
let mut missing: Vec<String> = labels.iter().filter(|l| interface(l)).map(|(l, ..)| l.to_string()).collect();
missing.extend(toggled_labels());
missing.retain(|l| !translated(l));
missing.dedup();
assert!(missing.is_empty(), "untranslated Czech menu labels: {missing:?}");
for keep in KEEP_AS_IS {
assert!(labels.iter().any(|(l, ..)| l == keep), "`{keep}` isn't a menu label");
assert!(!translated(keep), "`{keep}` is both kept and translated");
} }
} }
#[test] #[test]
fn language_command_validates_and_persists_without_a_document() { fn language_command_validates_and_persists_without_a_document() {
let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default()); let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
crate::menus::run_ui_command(&mut app, "app.language", &serde_json::json!({"lang": "ja"})).unwrap().unwrap(); crate::menus::run_ui_command(&mut app, "app.language", &json!({"lang": "ja"})).unwrap().unwrap();
assert_eq!(app.ui.language, Language::Ja); assert_eq!(app.ui.language, Language::Ja);
assert_eq!(crate::menus::checked(&app, "app.language", &serde_json::json!({"lang": "ja"})), Some(true)); assert_eq!(crate::menus::checked(&app, "app.language", &json!({"lang": "ja"})), Some(true));
assert!(crate::menus::run_ui_command(&mut app, "app.language", &serde_json::json!({"lang": "xx"})).unwrap().is_err()); assert!(crate::menus::run_ui_command(&mut app, "app.language", &json!({"lang": "xx"})).unwrap().is_err());
assert_eq!(app.ui.language, Language::Ja); assert_eq!(app.ui.language, Language::Ja);
let saved = serde_json::to_string(&app.ui).unwrap(); let saved = serde_json::to_string(&app.ui).unwrap();
let restored: crate::state::UiState = serde_json::from_str(&saved).unwrap(); let restored: crate::state::UiState = serde_json::from_str(&saved).unwrap();
assert_eq!(restored.language, Language::Ja); assert_eq!(restored.language, Language::Ja);
assert_eq!(crate::menus::run_ui_command(&mut app, "app.language", &json!({"lang": "cs"})).unwrap(), Ok(json!("cs")));
assert_eq!(app.ui.language, Language::Cs);
assert_eq!(crate::menus::checked(&app, "app.language", &json!({"lang": "cs"})), Some(true));
assert_eq!(crate::menus::checked(&app, "app.language", &json!({"lang": "ja"})), Some(false));
let restored: crate::state::UiState = serde_json::from_str(&serde_json::to_string(&app.ui).unwrap()).unwrap();
assert_eq!(restored.language, Language::Cs);
}
#[test]
fn the_language_menu_offers_every_language() {
let items: Vec<_> = menu_labels().into_iter().filter(|(_, id, _)| *id == "app.language").collect();
assert_eq!(items.len(), Language::ALL.len());
for language in Language::ALL {
let code = serde_json::to_value(language).unwrap();
assert!(items.iter().any(|(l, _, p)| *l == language.name() && p["lang"] == code), "{language:?} isn't in the Language menu");
}
} }
#[test] #[test]
@ -146,4 +284,26 @@ mod tests {
} }
}); });
} }
/// Czech letters (and the punctuation Czech text uses) come from each family's own first font,
/// not from a fallback further down the stack. (`has_glyph` can't tell: it counts characters of
/// the face that draws missing glyphs, the first one, as missing.)
#[test]
fn czech_glyphs_are_available_without_system_fonts() {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut output = ctx.run_ui(egui::RawInput::default(), |_| {});
output.textures_delta.clear();
ctx.fonts_mut(|fonts| {
let families: Vec<_> = fonts.definitions().families.iter().map(|(f, stack)| (f.clone(), stack.first().cloned())).collect();
for (family, first) in families {
let first = first.unwrap();
let mut font = fonts.fonts.font(&family);
let chars = font.characters();
for ch in "áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ„“‚‘…–".chars() {
assert!(chars.get(&ch).is_some_and(|fonts| fonts.contains(&first)), "{first} ({family:?}) has no {ch}");
}
}
});
}
} }

View File

@ -0,0 +1,578 @@
//! Czech (Čeština) menu labels, in menu order.
//!
//! Clean-room: every entry is written from the meaning of VectorCraft's English label, following
//! common Czech desktop-software conventions (commands in the infinitive, menus as nouns); no
//! other application's Czech localisation was consulted or copied.
//!
//! Left in English on purpose (see `KEEP_AS_IS` in the tests): the product name, language names,
//! OpenType, the built-in workspace names (names the workspace switcher and saved preferences
//! show as-is) and the bracketed perspective grid preset names (preset names stored in documents).
pub(super) const CZECH: &[(&str, &str)] = &[
// VectorCraft
("About VectorCraft", "O aplikaci VectorCraft"),
("Join Our Discord", "Připojit se k našemu Discordu"),
("Settings…", "Nastavení…"),
("Language", "Jazyk"),
("UI Brightness", "Jas rozhraní"),
("Dark", "Tmavý"),
("Medium Dark", "Středně tmavý"),
("Medium Light", "Středně světlý"),
("Light", "Světlý"),
("Quit VectorCraft", "Ukončit VectorCraft"),
// File
("File", "Soubor"),
("New…", "Nový…"),
("New from Template…", "Nový ze šablony…"),
("Open…", "Otevřít…"),
("Open Recent Files", "Otevřít nedávné soubory"),
("Recent File", "Nedávný soubor"),
("Clear Recent Files", "Vymazat seznam nedávných souborů"),
("Show in Folder", "Zobrazit ve složce"),
("Close", "Zavřít"),
("Close All", "Zavřít vše"),
("Save", "Uložit"),
("Save As…", "Uložit jako…"),
("Save a Copy…", "Uložit kopii…"),
("Save as Template…", "Uložit jako šablonu…"),
("Save as PDF…", "Uložit jako PDF…"),
("Save for Office Documents…", "Uložit pro kancelářské dokumenty…"),
("Revert", "Vrátit k uložené verzi"),
("Place…", "Umístit…"),
("Export", "Export"),
("Export for Screens…", "Exportovat pro obrazovky…"),
("Export As…", "Exportovat jako…"),
("Export As SVG…", "Exportovat jako SVG…"),
("Export As PNG…", "Exportovat jako PNG…"),
("Save for Web (Legacy)…", "Uložit pro web (starší)…"),
("Export Selection…", "Exportovat výběr…"),
("Package…", "Sbalit…"),
("Scripts", "Skripty"),
("Other Script…", "Jiný skript…"),
("Document Setup…", "Nastavení dokumentu…"),
("Document Color Mode", "Barevný režim dokumentu"),
("CMYK Color", "Barvy CMYK"),
("RGB Color", "Barvy RGB"),
("File Info…", "Informace o souboru…"),
("Print…", "Tisk…"),
// Edit
("Edit", "Úpravy"),
("Undo", "Zpět"),
("Redo", "Znovu"),
("Cut", "Vyjmout"),
("Copy", "Kopírovat"),
("Paste", "Vložit"),
("Paste in Front", "Vložit dopředu"),
("Paste in Back", "Vložit dozadu"),
("Paste in Place", "Vložit na místo"),
("Paste on All Artboards", "Vložit na všechny pracovní plochy"),
("Paste without Formatting", "Vložit bez formátování"),
("Clear", "Smazat"),
("Find and Replace…", "Najít a nahradit…"),
("Find Next", "Najít další"),
("Spelling", "Pravopis"),
("Auto Spell Check", "Automatická kontrola pravopisu"),
("Check Spelling…", "Zkontrolovat pravopis…"),
("Edit Custom Dictionary…", "Upravit vlastní slovník…"),
("Edit Colors", "Upravit barvy"),
("Recolor Artwork…", "Přebarvit kresbu…"),
("Recolor with Preset", "Přebarvit podle přednastavení"),
("1 Color Job…", "Zakázka s 1 barvou…"),
("2 Color Job…", "Zakázka se 2 barvami…"),
("3 Color Job…", "Zakázka se 3 barvami…"),
("Color Library…", "Knihovna barev…"),
("Adjust Color Balance…", "Upravit vyvážení barev…"),
("Blend Front to Back", "Prolnout zepředu dozadu"),
("Blend Horizontally", "Prolnout vodorovně"),
("Blend Vertically", "Prolnout svisle"),
("Convert to CMYK", "Převést na CMYK"),
("Convert to Grayscale", "Převést na stupně šedi"),
("Convert to RGB", "Převést na RGB"),
("Invert Colors", "Invertovat barvy"),
("Overprint Black…", "Přetisknout černou…"),
("Saturate…", "Upravit sytost…"),
("Edit Original", "Upravit originál"),
("Transparency Flattener Presets…", "Přednastavení sloučení průhlednosti…"),
("Print Presets…", "Přednastavení tisku…"),
("PDF Presets…", "Přednastavení PDF…"),
("Perspective Grid Presets…", "Přednastavení perspektivní mřížky…"),
("Color Settings…", "Nastavení barev…"),
("Assign Profile…", "Přiřadit profil…"),
("Keyboard Shortcuts…", "Klávesové zkratky…"),
("Preferences…", "Předvolby…"),
// Object
("Object", "Objekt"),
("Transform", "Transformace"),
("Transform Again", "Transformovat znovu"),
("Move…", "Přesunout…"),
("Rotate…", "Otočit…"),
("Reflect…", "Zrcadlit…"),
("Scale…", "Změnit velikost…"),
("Shear…", "Zkosit…"),
("Transform Each…", "Transformovat jednotlivě…"),
("Reset Bounding Box", "Obnovit ohraničovací rámeček"),
("Arrange", "Uspořádat"),
("Bring to Front", "Přenést do popředí"),
("Bring Forward", "Přenést blíž"),
("Send Backward", "Přesunout dál"),
("Send to Back", "Přesunout do pozadí"),
("Send to Current Layer", "Přesunout do aktuální vrstvy"),
("Align", "Zarovnat"),
("Horizontal Align Left", "Vodorovně zarovnat doleva"),
("Horizontal Align Center", "Vodorovně zarovnat na střed"),
("Horizontal Align Right", "Vodorovně zarovnat doprava"),
("Vertical Align Top", "Svisle zarovnat nahoru"),
("Vertical Align Center", "Svisle zarovnat na střed"),
("Vertical Align Bottom", "Svisle zarovnat dolů"),
("Group", "Seskupit"),
("Ungroup", "Oddělit"),
("Lock", "Zamknout"),
("Selection", "Výběr"),
("All Artwork Above", "Veškerou kresbu výše"),
("Other Layers", "Ostatní vrstvy"),
("Unlock All", "Odemknout vše"),
("Hide", "Skrýt"),
("Show All", "Zobrazit vše"),
("Expand…", "Rozložit…"),
("Expand Appearance", "Rozložit vzhled"),
("Crop Image", "Oříznout obraz"),
("Rasterize…", "Rastrovat…"),
("Create Gradient Mesh…", "Vytvořit přechodovou síť…"),
("Create Object Mosaic…", "Vytvořit mozaiku objektů…"),
("Vector Halftone…", "Vektorový polotón…"),
("Create Trim Marks", "Vytvořit značky ořezu"),
("Flatten Transparency…", "Sloučit průhlednost…"),
("Make Pixel Perfect", "Zarovnat na obrazové body"),
("Slice", "Řez"),
("Make", "Vytvořit"),
("Release", "Uvolnit"),
("Create from Guides", "Vytvořit z vodítek"),
("Create from Selection", "Vytvořit z výběru"),
("Duplicate Slice", "Duplikovat řez"),
("Combine Slices", "Spojit řezy"),
("Divide Slices…", "Rozdělit řezy…"),
("Delete All", "Odstranit vše"),
("Slice Options…", "Možnosti řezu…"),
("Clip to Artboard", "Oříznout na pracovní plochu"),
("Path", "Cesta"),
("Join", "Spojit"),
("Average…", "Zprůměrovat…"),
("Outline Stroke", "Převést tah na obrys"),
("Offset Path…", "Posunout cestu…"),
("Reverse Path Direction", "Obrátit směr cesty"),
("Simplify…", "Zjednodušit…"),
("Add Anchor Points", "Přidat kotevní body"),
("Remove Anchor Points", "Odstranit kotevní body"),
("Divide Objects Below", "Rozdělit objekty pod"),
("Split Into Grid…", "Rozdělit do mřížky…"),
("Clean Up…", "Vyčistit…"),
("Shape", "Tvar"),
("Convert to Shape", "Převést na tvar"),
("Expand Shape", "Rozložit tvar"),
("Pattern", "Vzorek"),
("Edit Pattern", "Upravit vzorek"),
("Tile Edge Color…", "Barva okraje dlaždice…"),
("Repeat", "Opakování"),
("Radial", "Kruhové"),
("Grid", "Mřížkové"),
("Mirror", "Zrcadlové"),
("Options…", "Možnosti…"),
("Blend", "Prolnutí"),
("Blend Options…", "Možnosti prolnutí…"),
("Expand", "Rozložit"),
("Replace Spine", "Nahradit páteř"),
("Reverse Spine", "Obrátit páteř"),
("Reverse Front to Back", "Obrátit pořadí zepředu dozadu"),
("Envelope Distort", "Deformace obálkou"),
("Make with Warp…", "Vytvořit pokřivením…"),
("Reset with Warp…", "Obnovit pokřivením…"),
("Make with Mesh…", "Vytvořit sítí…"),
("Reset with Mesh…", "Obnovit sítí…"),
("Make with Top Object", "Vytvořit horním objektem"),
("Envelope Options…", "Možnosti obálky…"),
("Edit Contents", "Upravit obsah"),
("Edit Envelope", "Upravit obálku"),
("Perspective", "Perspektiva"),
("Attach to Active Plane", "Připojit k aktivní rovině"),
("Release with Perspective", "Uvolnit s perspektivou"),
("Move Plane to Match Object", "Posunout rovinu k objektu"),
("Edit Text", "Upravit text"),
("Live Paint", "Živá malba"),
("Merge", "Sloučit"),
("Gap Options…", "Možnosti mezer…"),
("Image Trace", "Vektorizace obrazu"),
("Make…", "Vytvořit…"),
("Make and Expand…", "Vytvořit a rozložit…"),
("Text Wrap", "Obtékání textu"),
("Text Wrap Options…", "Možnosti obtékání textu…"),
("Clipping Mask", "Ořezová maska"),
("Edit Clipping Path", "Upravit ořezovou cestu"),
("Compound Path", "Složená cesta"),
("Artboards", "Pracovní plochy"),
("Convert to Artboards", "Převést na pracovní plochy"),
("Rearrange All Artboards…", "Přeuspořádat všechny pracovní plochy…"),
("Fit to Artwork Bounds", "Přizpůsobit hranicím kresby"),
("Fit to Selected Art", "Přizpůsobit vybrané kresbě"),
("Graph", "Graf"),
("Type…", "Typ…"),
("Data…", "Údaje…"),
("Design…", "Návrh…"),
("Column…", "Sloupec…"),
("Marker…", "Značka…"),
("Collect for Export", "Shromáždit pro export"),
("As Single Asset", "Jako jeden prvek"),
("As Multiple Assets", "Jako více prvků"),
("Plug-ins", "Zásuvné moduly"),
("No filter plug-ins installed", "Nejsou nainstalovány žádné filtry"),
("Install Plug-in…", "Nainstalovat zásuvný modul…"),
("Reload Plug-ins", "Znovu načíst zásuvné moduly"),
// Type
("Type", "Text"),
("Font", "Písmo"),
("Recent Fonts", "Nedávná písma"),
("Recent Font", "Nedávné písmo"),
("Size", "Velikost"),
("Glyphs", "Glyfy"),
("Insert Special Character", "Vložit speciální znak"),
("Bullet", "Odrážka"),
("Copyright Symbol", "Symbol copyrightu"),
("Ellipsis", "Výpustka"),
("Paragraph Symbol", "Symbol odstavce"),
("Registered Trademark Symbol", "Symbol registrované ochranné známky"),
("Section Symbol", "Symbol paragrafu"),
("Trademark Symbol", "Symbol ochranné známky"),
("Em Dash", "Dlouhá pomlčka"),
("En Dash", "Krátká pomlčka"),
("Discretionary Hyphen", "Volitelný spojovník"),
("Nonbreaking Hyphen", "Nezlomitelný spojovník"),
("Double Left Quotation Marks", "Levé dvojité uvozovky"),
("Double Right Quotation Marks", "Pravé dvojité uvozovky"),
("Single Left Quotation Mark", "Levá jednoduchá uvozovka"),
("Single Right Quotation Mark", "Pravá jednoduchá uvozovka"),
("Insert Whitespace Character", "Vložit znak mezery"),
("Em Space", "Čtverčíková mezera"),
("En Space", "Půlčtverčíková mezera"),
("Hair Space", "Vlasová mezera"),
("Sixth Space", "Šestinová mezera"),
("Thin Space", "Úzká mezera"),
("Nonbreaking Space", "Nezlomitelná mezera"),
("Figure Space", "Číslicová mezera"),
("Punctuation Space", "Interpunkční mezera"),
("Third Space", "Třetinová mezera"),
("Quarter Space", "Čtvrtinová mezera"),
("Insert Break Character", "Vložit znak zalomení"),
("Forced Line Break", "Vynucené zalomení řádku"),
("Tab", "Tabulátor"),
("Convert To Area Type", "Převést na text v oblasti"),
("Convert To Point Type", "Převést na bodový text"),
("Area Type Options…", "Možnosti textu v oblasti…"),
("Type on a Path", "Text na cestě"),
("Rainbow", "Duha"),
("Skew", "Zkosení"),
("3D Ribbon", "3D stuha"),
("Stair Step", "Schody"),
("Gravity", "Gravitace"),
("Type on a Path Options…", "Možnosti textu na cestě…"),
("Threaded Text", "Zřetězený text"),
("Create", "Vytvořit"),
("Release Selection", "Uvolnit výběr"),
("Remove Threading", "Odstranit zřetězení"),
("Fit Headline", "Přizpůsobit titulek"),
("Find Font…", "Najít písmo…"),
("Change Case", "Změnit velikost písmen"),
("UPPERCASE", "VELKÁ PÍSMENA"),
("lowercase", "malá písmena"),
("Title Case", "Velká Počáteční Písmena"),
("Sentence case", "Velké první písmeno věty"),
("Smart Punctuation…", "Inteligentní interpunkce…"),
("Create Outlines", "Převést na křivky"),
("Optical Margin Alignment", "Optické zarovnání okrajů"),
("Fill with Placeholder Text", "Vyplnit zástupným textem"),
("Show Hidden Characters", "Zobrazit skryté znaky"),
("Hide Hidden Characters", "Skrýt skryté znaky"),
("Type Orientation", "Orientace textu"),
("Horizontal", "Vodorovně"),
("Vertical", "Svisle"),
// Select
("Select", "Výběr"),
("All", "Vybrat vše"),
("All on Active Artboard", "Vše na aktivní pracovní ploše"),
("Deselect", "Zrušit výběr"),
("Reselect", "Znovu vybrat"),
("Inverse", "Invertovat výběr"),
("Next Object Above", "Další objekt nad"),
("Next Object Below", "Další objekt pod"),
("Same", "Stejné"),
("Appearance", "Vzhled"),
("Appearance Attribute", "Atribut vzhledu"),
("Blending Mode", "Režim míchání"),
("Fill & Stroke", "Výplň a tah"),
("Fill Color", "Barva výplně"),
("Opacity", "Krytí"),
("Stroke Color", "Barva tahu"),
("Stroke Weight", "Tloušťka tahu"),
("Graphic Style", "Grafický styl"),
("Symbol Instance", "Instance symbolu"),
("Font Family", "Rodina písma"),
("Font Family & Style", "Rodina a řez písma"),
("Font Family, Style & Size", "Rodina, řez a velikost písma"),
("Font Size", "Velikost písma"),
("Text Fill Color", "Barva výplně textu"),
("Text Stroke Color", "Barva tahu textu"),
("All on Same Layers", "Vše ve stejných vrstvách"),
("Direction Handles", "Směrové úchyty"),
("Brush Strokes", "Tahy štětcem"),
("Bristle Brush Strokes", "Tahy štětinovým štětcem"),
("Clipping Masks", "Ořezové masky"),
("Slices", "Řezy"),
("Stray Points", "Osamocené body"),
("Open Paths", "Otevřené cesty"),
("All Text Objects", "Všechny textové objekty"),
("Point Text Objects", "Bodové textové objekty"),
("Area Text Objects", "Textové objekty v oblasti"),
("Start Global Edit", "Zahájit globální úpravy"),
("Save Selection…", "Uložit výběr…"),
("Edit Selection…", "Upravit výběr…"),
// Effect
("Effect", "Efekt"),
("Apply Last Effect", "Použít poslední efekt"),
("Last Effect…", "Poslední efekt…"),
("Document Raster Effects Settings…", "Nastavení rastrových efektů dokumentu…"),
("Vector Effects", "Vektorové efekty"),
("3D and Materials", "3D a materiály"),
("Extrude & Bevel…", "Vysunutí a zkosení…"),
("Revolve…", "Rotační těleso…"),
("Inflate…", "Nafouknutí…"),
("Materials…", "Materiály…"),
("Color Adjustments", "Úpravy barev"),
("Brightness/Contrast…", "Jas/kontrast…"),
("Curves…", "Křivky…"),
("Hue/Saturation…", "Odstín/sytost…"),
("Levels…", "Úrovně…"),
("Shift to Color…", "Posun k barvě…"),
("Temperature/Tint…", "Teplota/nádech…"),
("Rectangle…", "Obdélník…"),
("Rounded Rectangle…", "Zaoblený obdélník…"),
("Ellipse…", "Elipsa…"),
("Crop Marks", "Ořezové značky"),
("Distort & Transform", "Deformace a transformace"),
("Free Distort…", "Volná deformace…"),
("Pucker & Bloat…", "Smrštění a vyboulení…"),
("Roughen…", "Zdrsnění…"),
("Transform…", "Transformovat…"),
("Tweak…", "Náhodné posunutí…"),
("Twist…", "Zkroucení…"),
("Zig Zag…", "Cikcak…"),
("Pathfinder", "Operace s cestami"),
("Add", "Sjednotit"),
("Intersect", "Protnout"),
("Exclude", "Vyloučit"),
("Subtract", "Odečíst"),
("Minus Back", "Odečíst zadní"),
("Divide", "Rozdělit"),
("Trim", "Zastřihnout"),
("Crop", "Oříznout"),
("Outline", "Obrys"),
("Stylize", "Stylizace"),
("Round Corners…", "Zaoblit rohy…"),
("Scribble…", "Čmáranice…"),
("Drop Shadow…", "Vržený stín…"),
("Inner Glow…", "Vnitřní záře…"),
("Outer Glow…", "Vnější záře…"),
("Feather…", "Měkké okraje…"),
("SVG Filters", "Filtry SVG"),
("Apply SVG Filter…", "Použít filtr SVG…"),
("Warp", "Pokřivení"),
("Arc…", "Oblouk…"),
("Arc Lower…", "Dolní oblouk…"),
("Arc Upper…", "Horní oblouk…"),
("Arch…", "Klenba…"),
("Bulge…", "Vydutí…"),
("Shell Lower…", "Dolní mušle…"),
("Shell Upper…", "Horní mušle…"),
("Flag…", "Vlajka…"),
("Wave…", "Vlna…"),
("Fish…", "Ryba…"),
("Rise…", "Stoupání…"),
("Fisheye…", "Rybí oko…"),
("Squeeze…", "Stlačení…"),
("Raster Effects", "Rastrové efekty"),
("Blur", "Rozostření"),
("Gaussian Blur…", "Gaussovské rozostření…"),
// View
("View", "Zobrazení"),
("Preview", "Náhled"),
("Overprint Preview", "Náhled přetisků"),
("Pixel Preview", "Náhled obrazových bodů"),
("Trim View", "Oříznuté zobrazení"),
("Presentation Mode", "Režim prezentace"),
("Screen Mode", "Režim obrazovky"),
("Normal Screen Mode", "Normální režim obrazovky"),
("Full Screen Mode with Menu Bar", "Celá obrazovka s pruhem nabídek"),
("Full Screen Mode", "Celá obrazovka"),
("Proof Setup", "Nastavení nátisku"),
("Working CMYK", "Pracovní CMYK"),
("Legacy Macintosh RGB (Gamma 1.8)", "Starší Macintosh RGB (gama 1,8)"),
("Internet Standard RGB (sRGB)", "Standardní internetové RGB (sRGB)"),
("Monitor RGB", "RGB monitoru"),
("Color blindness – Protanopia-type", "Barvoslepost – protanopie"),
("Color blindness – Deuteranopia-type", "Barvoslepost – deuteranopie"),
("Customize…", "Vlastní…"),
("Proof Colors", "Barvy nátisku"),
("Zoom In", "Zvětšit"),
("Zoom Out", "Zmenšit"),
("Fit Artboard in Window", "Přizpůsobit pracovní plochu oknu"),
("Fit All in Window", "Přizpůsobit vše oknu"),
("Actual Size", "Skutečná velikost"),
("Reset Rotate View", "Obnovit otočení zobrazení"),
("Hide Edges", "Skrýt okraje"),
("Show Edges", "Zobrazit okraje"),
("Hide Artboards", "Skrýt pracovní plochy"),
("Show Artboards", "Zobrazit pracovní plochy"),
("Show Print Tiling", "Zobrazit dlaždice tisku"),
("Hide Print Tiling", "Skrýt dlaždice tisku"),
("Hide Slices", "Skrýt řezy"),
("Show Slices", "Zobrazit řezy"),
("Lock Slices", "Zamknout řezy"),
("Rulers", "Pravítka"),
("Show Rulers", "Zobrazit pravítka"),
("Hide Rulers", "Skrýt pravítka"),
("Change to Global Rulers", "Přepnout na globální pravítka"),
("Show Video Rulers", "Zobrazit video pravítka"),
("Hide Bounding Box", "Skrýt ohraničovací rámeček"),
("Show Bounding Box", "Zobrazit ohraničovací rámeček"),
("Show Transparency Grid", "Zobrazit mřížku průhlednosti"),
("Hide Transparency Grid", "Skrýt mřížku průhlednosti"),
("Hide Text Threads", "Skrýt zřetězení textu"),
("Show Text Threads", "Zobrazit zřetězení textu"),
("Hide Gradient Annotator", "Skrýt ovladač přechodu"),
("Show Gradient Annotator", "Zobrazit ovladač přechodu"),
("Hide Corner Widget", "Skrýt ovladač rohů"),
("Show Corner Widget", "Zobrazit ovladač rohů"),
("Guides", "Vodítka"),
("Hide Guides", "Skrýt vodítka"),
("Show Guides", "Zobrazit vodítka"),
("Lock Guides", "Zamknout vodítka"),
("Unlock Guides", "Odemknout vodítka"),
("Make Guides", "Vytvořit vodítka"),
("Release Guides", "Uvolnit vodítka"),
("Clear Guides", "Vymazat vodítka"),
("Smart Guides", "Chytrá vodítka"),
("Perspective Grid", "Perspektivní mřížka"),
("Show Grid", "Zobrazit mřížku"),
("Hide Grid", "Skrýt mřížku"),
("Snap to Grid", "Přichytávat k mřížce"),
("Lock Grid", "Zamknout mřížku"),
("Unlock Grid", "Odemknout mřížku"),
("Lock Station Point", "Zamknout bod pozorovatele"),
("Define Grid…", "Definovat mřížku…"),
("One Point Perspective", "Jednobodová perspektiva"),
("User Preset", "Uživatelské přednastavení"),
("Two Point Perspective", "Dvoubodová perspektiva"),
("Three Point Perspective", "Tříbodová perspektiva"),
("Save Grid as Preset…", "Uložit mřížku jako přednastavení…"),
("Snap to Pixel", "Přichytávat k obrazovým bodům"),
("Snap to Point", "Přichytávat k bodům"),
("Snap to Glyph", "Přichytávat ke glyfům"),
("New View…", "Nové zobrazení…"),
("Edit Views…", "Upravit zobrazení…"),
("Saved View 1", "Uložené zobrazení 1"),
("Saved View 2", "Uložené zobrazení 2"),
("Saved View 3", "Uložené zobrazení 3"),
("Saved View 4", "Uložené zobrazení 4"),
("Saved View 5", "Uložené zobrazení 5"),
("Saved View 6", "Uložené zobrazení 6"),
("Saved View 7", "Uložené zobrazení 7"),
("Saved View 8", "Uložené zobrazení 8"),
("Saved View 9", "Uložené zobrazení 9"),
("Saved View 10", "Uložené zobrazení 10"),
// Window
("Window", "Okno"),
("New Window", "Nové okno"),
("Cascade", "Do kaskády"),
("Tile", "Vedle sebe"),
("Float in Window", "Uvolnit do samostatného okna"),
("Consolidate All Windows", "Sloučit všechna okna"),
("Workspace", "Pracovní prostor"),
("Reset Essentials", "Obnovit Essentials"),
("New Workspace…", "Nový pracovní prostor…"),
("Manage Workspaces…", "Spravovat pracovní prostory…"),
("Control", "Ovládací panel"),
("Contextual Task Bar", "Kontextový panel úloh"),
("Tools", "Nástroje"),
("Toolbars", "Panely nástrojů"),
("Advanced", "Rozšířený"),
("Single / Double Column", "Jeden / dva sloupce"),
("Actions", "Akce"),
("Asset Export", "Export prvků"),
("Attributes", "Atributy"),
("Brushes", "Štětce"),
("Color", "Barva"),
("Color Guide", "Průvodce barvami"),
("Color Themes", "Barevné motivy"),
("CSS Properties", "Vlastnosti CSS"),
("Document Info", "Informace o dokumentu"),
("Flattener Preview", "Náhled sloučení"),
("Gradient", "Přechod"),
("Graphic Styles", "Grafické styly"),
("History", "Historie"),
("Info", "Informace"),
("Layers", "Vrstvy"),
("Libraries", "Knihovny"),
("Links", "Propojení"),
("Magic Wand", "Kouzelná hůlka"),
("Navigator", "Navigátor"),
("Pattern Options", "Možnosti vzorku"),
("Properties", "Vlastnosti"),
("Separations Preview", "Náhled separací"),
("Stroke", "Tah"),
("SVG Interactivity", "Interaktivita SVG"),
("Swatches", "Vzorník"),
("Symbols", "Symboly"),
("Transparency", "Průhlednost"),
("Character", "Znak"),
("Character Styles", "Styly znaků"),
("Paragraph", "Odstavec"),
("Paragraph Styles", "Styly odstavců"),
("Tabs", "Tabulátory"),
("Variables", "Proměnné"),
("Brush Libraries", "Knihovny štětců"),
("Built-in Libraries", "Vestavěné knihovny"),
("User Defined", "Uživatelské"),
("Other Library…", "Jiná knihovna…"),
("Graphic Style Libraries", "Knihovny grafických stylů"),
("Shadows and Glows", "Stíny a záře"),
("Outlines and Rules", "Obrysy a linky"),
("Hand-Drawn", "Ručně kreslené"),
("Gradient Finishes", "Přechodové povrchy"),
("Shape Effects", "Efekty tvarů"),
("Blends and Transparency", "Prolnutí a průhlednost"),
("User Library", "Uživatelská knihovna"),
("Save Graphic Style Library…", "Uložit knihovnu grafických stylů…"),
("Swatch Libraries", "Knihovny vzorníků"),
("Default Swatches", "Výchozí vzorník"),
("Web Safe 216", "Webově bezpečné 216"),
("Grays and Neutrals", "Šedé a neutrální"),
("Earth Tones", "Zemité tóny"),
("Skin Tone Ramps", "Škály odstínů pleti"),
("Pastels", "Pastelové"),
("Brights", "Zářivé"),
("Metallic Gradients", "Kovové přechody"),
("Perceptual Scales", "Percepční škály"),
("Harmony Sets", "Harmonické sady"),
("Gradients", "Přechody"),
("Spectrum Gradients", "Spektrální přechody"),
("Sky Gradients", "Přechody oblohy"),
("Neutral Gradients", "Neutrální přechody"),
("Duotone Gradients", "Dvoutónové přechody"),
("Radial Glows", "Kruhové záře"),
("Save Swatch Library…", "Uložit knihovnu vzorníku…"),
("Symbol Libraries", "Knihovny symbolů"),
// Help
("Help", "Nápověda"),
("ArtCraft Website", "Web ArtCraft"),
("VectorCraft on getartcraft.com", "VectorCraft na getartcraft.com"),
("VectorCraft on GitHub", "VectorCraft na GitHubu"),
("Search Commands…", "Hledat příkazy…"),
("VectorCraft Help…", "Nápověda VectorCraft…"),
];

View File

@ -50,7 +50,7 @@ use Item::Sep;
/// UI-level commands: (id, label, shortcut, params doc). /// UI-level commands: (id, label, shortcut, params doc).
pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
("app.language", "Interface Language", "", "{lang: en|ja} — persistent interface language"), ("app.language", "Interface Language", "", "{lang: en|ja|cs} — persistent interface language"),
("file.open", "Open…", "Cmd+O", "{path?}"), ("file.open", "Open…", "Cmd+O", "{path?}"),
( (
"file.save", "file.save",
@ -674,7 +674,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<Value, String>> { pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<Value, String>> {
if id == "app.language" { if id == "app.language" {
let language = p.get("lang").and_then(Value::as_str).and_then(crate::i18n::Language::parse); let language = p.get("lang").and_then(Value::as_str).and_then(crate::i18n::Language::parse);
let Some(language) = language else { return Some(Err("lang must be en or ja".into())) }; let Some(language) = language else { return Some(Err("lang must be en, ja or cs".into())) };
app.ui.language = language; app.ui.language = language;
return Some(Ok(json!(language))); return Some(Ok(json!(language)));
} }
@ -1450,7 +1450,14 @@ pub fn menu_tree() -> Vec<(&'static str, Vec<Item>)> {
c("Join Our Discord", "help.discord"), c("Join Our Discord", "help.discord"),
Sep, Sep,
c("Settings…", "edit.preferences"), c("Settings…", "edit.preferences"),
sub("Language", vec![cp("English", "app.language", json!({"lang": "en"})), cp("日本語", "app.language", json!({"lang": "ja"}))]), sub(
"Language",
vec![
cp("English", "app.language", json!({"lang": "en"})),
cp("日本語", "app.language", json!({"lang": "ja"})),
cp("Čeština", "app.language", json!({"lang": "cs"})),
],
),
Sep, Sep,
sub("UI Brightness", Brightness::ALL.iter().map(|b| cp(b.label(), "window.brightness", json!({"brightness": b.id()}))).collect()), sub("UI Brightness", Brightness::ALL.iter().map(|b| cp(b.label(), "window.brightness", json!({"brightness": b.id()}))).collect()),
Sep, Sep,