From d9165ca50a9e483d49d6ae8b1beb4fe63cb12577 Mon Sep 17 00:00:00 2001 From: devmobasa <4170275+devmobasa@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:15:24 +0200 Subject: [PATCH] fix(configurator): keep font and color config values untranslated --- configurator/src/models/color/named.rs | 16 +++--- configurator/src/models/config/tests.rs | 42 ++++++++++++++++ configurator/src/models/fields/font.rs | 36 +++++++------- configurator/src/models/fields/tests.rs | 66 +++++++++++++++++++++++++ configurator/src/models/fields/tool.rs | 16 +++--- docs/I18N.md | 6 +-- locales/zh-CN.toml | 16 ------ 7 files changed, 145 insertions(+), 53 deletions(-) diff --git a/configurator/src/models/color/named.rs b/configurator/src/models/color/named.rs index 0809b06d0..ae04cac5a 100644 --- a/configurator/src/models/color/named.rs +++ b/configurator/src/models/color/named.rs @@ -50,14 +50,14 @@ impl NamedColorOption { pub fn as_value(&self) -> &'static str { match self { - Self::Red => tr("red"), - Self::Green => tr("green"), - Self::Blue => tr("blue"), - Self::Yellow => tr("yellow"), - Self::Orange => tr("orange"), - Self::Pink => tr("pink"), - Self::White => tr("white"), - Self::Black => tr("black"), + Self::Red => "red", + Self::Green => "green", + Self::Blue => "blue", + Self::Yellow => "yellow", + Self::Orange => "orange", + Self::Pink => "pink", + Self::White => "white", + Self::Black => "black", Self::Custom => "", } } diff --git a/configurator/src/models/config/tests.rs b/configurator/src/models/config/tests.rs index 5460dc74c..1dd02da67 100644 --- a/configurator/src/models/config/tests.rs +++ b/configurator/src/models/config/tests.rs @@ -630,6 +630,48 @@ fn config_draft_round_trips_quick_colors() { ); } +/// A save rebuilds the draft in the window's language, so a translated value +/// would come back on the next save. +#[test] +fn a_config_saved_in_chinese_keeps_its_font_and_color_names() { + crate::test_env::in_language_child( + concat!( + module_path!(), + "::a_config_saved_in_chinese_keeps_its_font_and_color_names" + ), + || { + wayscriber::i18n::set_language(wayscriber::domain::Language::SimplifiedChinese); + let mut config = Config::default(); + config.drawing.font_style = "italic".to_string(); + config.drawing.font_weight = "light".to_string(); + config.drawing.default_color = ColorSpec::Name("blue".to_string()); + config.drawing.quick_colors.entries = vec![QuickColorConfig { + label: "Leaf".to_string(), + color: ColorSpec::Name("green".to_string()), + }]; + + let draft = ConfigDraft::from_config(&config); + assert_eq!(draft.drawing_font_style, "italic"); + assert_eq!(draft.drawing_font_weight, "light"); + let saved = draft.to_config(&config).expect("unchanged draft"); + + assert_eq!(saved.drawing.font_style, "italic"); + assert_eq!(saved.drawing.font_weight, "light"); + assert_eq!( + saved.drawing.default_color, + ColorSpec::Name("blue".to_string()) + ); + assert_eq!( + saved.drawing.quick_colors.entries[0].color, + ColorSpec::Name("green".to_string()) + ); + if let Err(error) = saved.validate_for_save() { + panic!("{error}"); + } + }, + ); +} + #[test] fn config_draft_preserves_implicit_quick_color_defaults() { let config = Config::default(); diff --git a/configurator/src/models/fields/font.rs b/configurator/src/models/fields/font.rs index d6b6bf37f..73cccd53e 100644 --- a/configurator/src/models/fields/font.rs +++ b/configurator/src/models/fields/font.rs @@ -24,9 +24,9 @@ impl FontStyleOption { pub fn canonical_value(&self) -> &'static str { match self { - Self::Normal => tr("normal"), - Self::Italic => tr("italic"), - Self::Oblique => tr("oblique"), + Self::Normal => "normal", + Self::Italic => "italic", + Self::Oblique => "oblique", Self::Custom => "", } } @@ -34,9 +34,9 @@ impl FontStyleOption { pub fn from_value(value: &str) -> (Self, String) { let lower = value.trim().to_lowercase(); match lower.as_str() { - "normal" => (Self::Normal, tr("normal").to_string()), - "italic" => (Self::Italic, tr("italic").to_string()), - "oblique" => (Self::Oblique, tr("oblique").to_string()), + "normal" => (Self::Normal, "normal".to_string()), + "italic" => (Self::Italic, "italic".to_string()), + "oblique" => (Self::Oblique, "oblique".to_string()), _ => (Self::Custom, value.to_string()), } } @@ -86,12 +86,12 @@ impl FontWeightOption { pub fn canonical_value(&self) -> &'static str { match self { - Self::Normal => tr("normal"), - Self::Bold => tr("bold"), - Self::Light => tr("light"), - Self::Ultralight => tr("ultralight"), - Self::Heavy => tr("heavy"), - Self::Ultrabold => tr("ultrabold"), + Self::Normal => "normal", + Self::Bold => "bold", + Self::Light => "light", + Self::Ultralight => "ultralight", + Self::Heavy => "heavy", + Self::Ultrabold => "ultrabold", Self::Custom => "", } } @@ -99,12 +99,12 @@ impl FontWeightOption { pub fn from_value(value: &str) -> (Self, String) { let lower = value.trim().to_lowercase(); match lower.as_str() { - "normal" => (Self::Normal, tr("normal").to_string()), - "bold" => (Self::Bold, tr("bold").to_string()), - "light" => (Self::Light, tr("light").to_string()), - "ultralight" => (Self::Ultralight, tr("ultralight").to_string()), - "heavy" => (Self::Heavy, tr("heavy").to_string()), - "ultrabold" => (Self::Ultrabold, tr("ultrabold").to_string()), + "normal" => (Self::Normal, "normal".to_string()), + "bold" => (Self::Bold, "bold".to_string()), + "light" => (Self::Light, "light".to_string()), + "ultralight" => (Self::Ultralight, "ultralight".to_string()), + "heavy" => (Self::Heavy, "heavy".to_string()), + "ultrabold" => (Self::Ultrabold, "ultrabold".to_string()), _ => (Self::Custom, value.to_string()), } } diff --git a/configurator/src/models/fields/tests.rs b/configurator/src/models/fields/tests.rs index 2332b7912..00bce7497 100644 --- a/configurator/src/models/fields/tests.rs +++ b/configurator/src/models/fields/tests.rs @@ -64,6 +64,72 @@ fn font_weight_option_handles_custom_values() { assert_eq!(value, "UltraHeavy"); } +#[test] +fn config_values_do_not_follow_the_interface_language() { + crate::test_env::in_language_child( + concat!( + module_path!(), + "::config_values_do_not_follow_the_interface_language" + ), + || { + use wayscriber::config::ColorSpec; + use wayscriber::domain::Language; + + const STYLES: [&str; 4] = ["normal", "italic", "oblique", ""]; + const WEIGHTS: [&str; 7] = [ + "normal", + "bold", + "light", + "ultralight", + "heavy", + "ultrabold", + "", + ]; + const COLORS: [&str; 8] = [ + "red", "green", "blue", "yellow", "orange", "pink", "white", "black", + ]; + for language in [Language::English, Language::SimplifiedChinese] { + wayscriber::i18n::set_language(language); + // The overlay parses these words; a translated one is an + // invalid font value or an unknown color. + let styles: Vec<_> = FontStyleOption::list() + .iter() + .map(FontStyleOption::canonical_value) + .collect(); + assert_eq!(styles, STYLES, "{language:?}"); + let weights: Vec<_> = FontWeightOption::list() + .iter() + .map(FontWeightOption::canonical_value) + .collect(); + assert_eq!(weights, WEIGHTS, "{language:?}"); + for option in FontStyleOption::list() { + let value = option.canonical_value(); + assert_eq!(FontStyleOption::from_value(value).1, value, "{language:?}"); + } + for option in FontWeightOption::list() { + let value = option.canonical_value(); + assert_eq!(FontWeightOption::from_value(value).1, value, "{language:?}"); + } + + let named: Vec<_> = crate::models::NamedColorOption::list() + .iter() + .map(|option| option.as_value()) + .collect(); + assert_eq!(named[..COLORS.len()], COLORS, "{language:?}"); + let drag: Vec<_> = DragColorOption::list() + .into_iter() + .filter_map(|option| option.to_color_spec(None)) + .collect(); + assert_eq!( + drag, + COLORS.map(|name| ColorSpec::Name(name.to_string())), + "{language:?}" + ); + } + }, + ); +} + #[test] fn status_position_option_round_trips() { let option = StatusPositionOption::from_status_position(StatusPosition::BottomRight); diff --git a/configurator/src/models/fields/tool.rs b/configurator/src/models/fields/tool.rs index 9b712b0fc..66fc64a7e 100644 --- a/configurator/src/models/fields/tool.rs +++ b/configurator/src/models/fields/tool.rs @@ -395,14 +395,14 @@ impl DragColorOption { let name = match self { Self::Current => return None, Self::Custom => return existing, - Self::Red => tr("red"), - Self::Green => tr("green"), - Self::Blue => tr("blue"), - Self::Yellow => tr("yellow"), - Self::Orange => tr("orange"), - Self::Pink => tr("pink"), - Self::White => tr("white"), - Self::Black => tr("black"), + Self::Red => "red", + Self::Green => "green", + Self::Blue => "blue", + Self::Yellow => "yellow", + Self::Orange => "orange", + Self::Pink => "pink", + Self::White => "white", + Self::Black => "black", }; Some(ColorSpec::Name(name.to_string())) } diff --git a/docs/I18N.md b/docs/I18N.md index 33b2e8168..04b2989e6 100644 --- a/docs/I18N.md +++ b/docs/I18N.md @@ -138,9 +138,9 @@ name, a file name — is never translated. Canvas content (typed text, sticky notes, board and page names) is user data and stays as the user wrote it. Displayed text is never an identifier. Widget IDs, stack page names, CSS names, -config keys and action IDs are fixed English spellings derived from typed -values, never from a label, and no code compares a label with translated text -to decide what to do. +config keys, config values such as `bold` or `blue`, and action IDs are fixed +English spellings derived from typed values, never from a label, and no code +compares a label with translated text to decide what to do. ### What is not translated diff --git a/locales/zh-CN.toml b/locales/zh-CN.toml index f2ac9bd19..3737cc595 100644 --- a/locales/zh-CN.toml +++ b/locales/zh-CN.toml @@ -190,12 +190,10 @@ "Balanced" = "均衡" "Bend" = "弯曲" "Black" = "黑色" -"black" = "黑色" "Black out" = "涂黑" "Blackboard" = "黑板" "Blackboard Mode" = "黑板模式" "Blue" = "蓝色" -"blue" = "蓝色" "Blue (0-255)" = "蓝 (0-255)" "Blur" = "模糊" "Blur sensitive regions on captured backgrounds" = "模糊截图背景中的敏感区域" @@ -256,7 +254,6 @@ "Boards: click to switch or add one" = "画板:点击可切换或新建" "Boards: prev/next/new/del." = "画板:上一个/下一个/新建/删除。" "Bold" = "加粗" -"bold" = "粗体" "Bold. Applies to selected text, or to the next label you type." = "加粗。应用于选中的文字或下一个输入的标签" "Border (hex)" = "边框(十六进制)" "Border width" = "边框宽度" @@ -859,12 +856,10 @@ "Go to previous page" = "转到上一页" "Got it" = "知道了" "Green" = "绿色" -"green" = "绿色" "Green (0-255)" = "绿 (0-255)" "Grid spacing (8–200 logical pixels)" = "网格间距(8–200 逻辑像素)" "gsettings is not available in {}." = "{} 中找不到 gsettings。" "Heavy" = "粗体" -"heavy" = "粗" "Help" = "帮助" "Help Overlay" = "帮助叠层" "Help Overlay Style" = "帮助叠层样式" @@ -960,7 +955,6 @@ "Invalid user service path" = "无效的用户服务路径" "Irons out the finest jitter" = "消除最细微的抖动" "Italic" = "斜体" -"italic" = "斜体" "Jump to Conflict" = "跳转到冲突" "Jump to conflicting action" = "跳转到冲突的操作" "just now" = "刚刚" @@ -1008,7 +1002,6 @@ "Less smoothing" = "减少平滑" "Letter" = "信纸" "Light" = "灯光" -"light" = "细" "Light controls include: {}" = "灯光控制包含文件:{}" "Light controls setup available via Hyprland native bindings" = "灯光控制可通过 Hyprland 原生绑定设置" "Light controls setup requires manual compositor bindings" = "灯光控制需要手动添加合成器绑定" @@ -1211,7 +1204,6 @@ "none" = "无" "None" = "无" "Normal" = "常规" -"normal" = "常规" "Not a hex color" = "不是十六进制颜色" "Not configured" = "未配置" "Not installed" = "未安装" @@ -1229,7 +1221,6 @@ "Nudge Selection Up" = "选区上移" "Nudge Selection Up (Large)" = "选区上移(大幅)" "Oblique" = "倾斜" -"oblique" = "倾斜" "OCR is already running" = "文字识别已在运行" "OCR languages (e.g. eng, eng+deu; needs the matching Tesseract packages)" = "文字识别语言(例如 eng、eng+deu;需要对应的 Tesseract 语言包)" "OCR languages: {}." = "文字识别语言:{}。" @@ -1285,7 +1276,6 @@ "Opening session folder..." = "正在打开会话文件夹..." "Opens wayscriber.com · your app details come along" = "打开 wayscriber.com · 会附带应用信息" "Orange" = "橙色" -"orange" = "橙色" "Order" = "顺序" "Orientation" = "方向" "Outline (hex)" = "描边(十六进制)" @@ -1387,7 +1377,6 @@ "Pin top toolbar" = "固定顶部工具栏" "Pin: click to open at startup" = "固定:点击以在启动时打开" "Pink" = "粉色" -"pink" = "粉色" "Pinned" = "已固定" "Pinned: opens at startup (click to disable)" = "已固定:启动时打开(点击禁用)" "Pixelate" = "像素化" @@ -1514,7 +1503,6 @@ "Rectangle" = "矩形" "Rectangle Tool" = "矩形工具" "Red" = "红色" -"red" = "红色" "Red (0-255)" = "红 (0-255)" "Redo" = "重做" "Redo All" = "全部重做" @@ -2192,9 +2180,7 @@ "Type to search... (Esc clears)" = "输入以搜索…(Esc 清除)" "UI & Modes" = "界面与模式" "Ultrabold" = "特粗" -"ultrabold" = "特粗" "Ultralight" = "特细" -"ultralight" = "特细" "Unable to locate `wayscriber` binary. Set {} or install `wayscriber` in {}." = "找不到 `wayscriber` 可执行文件。请设置 {} 或将 `wayscriber` 安装到 {} 中。" "Unable to resolve config path." = "无法解析配置路径。" "Unavailable" = "不可用" @@ -2285,14 +2271,12 @@ "were saved to {} instead, in its recovery file {} because they exceed the session size limit" = "已改为保存到 {},即其恢复文件 {},因为它们超出了会话大小限制" "What changed in each version" = "各版本的变化" "White" = "白色" -"white" = "白色" "Whiteboard" = "白板" "Whiteboard Mode" = "白板模式" "Width (px)" = "宽度(像素)" "Working..." = "处理中..." "Wrote Hyprland light controls to {}" = "已将 Hyprland 灯光控制写入 {}" "Yellow" = "黄色" -"yellow" = "黄色" "You are running {}. Wayscriber does not install updates itself. See {}" = "您正在运行 {}。Wayscriber 不会自行安装更新。请参阅 {}" "You can change this later in the configurator." = "您可以稍后在配置器中更改。" "You have unsaved changes. Save them, discard them, or cancel to keep editing." = "你有未保存的更改。保存、放弃,或取消以继续编辑。"