diff --git a/crates/ui-egui/src/i18n/ui.rs b/crates/ui-egui/src/i18n/ui.rs index 56c1649e5..061090871 100644 --- a/crates/ui-egui/src/i18n/ui.rs +++ b/crates/ui-egui/src/i18n/ui.rs @@ -1674,6 +1674,18 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[ ("Puppet Bend Pin Tool", "人偶弯曲控点工具"), ("Puppet Advanced Pin Tool", "人偶高级控点工具"), ("Puppet Overlap Pin Tool", "人偶重叠控点工具"), + ("Shortcut", "快捷键"), + ("Range", "范围"), + ("Play From", "从以下位置播放"), + ("Frame Rate", "帧速率"), + ("Resolution", "分辨率"), + ("Overlays", "叠加"), + ("Layer Controls", "图层控制"), + ("Loop", "循环"), + ("Cache Before Playback", "播放前缓存"), + ("Pre-roll", "预卷"), + ("Post-roll", "后卷"), + ("If caching, play cached frames", "缓存时播放已缓存帧"), ]; const TRADITIONAL_CHINESE: &[(&str, &str)] = &[ @@ -2392,6 +2404,18 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[ ("Puppet Bend Pin Tool", "人偶彎曲控點工具"), ("Puppet Advanced Pin Tool", "人偶高級控點工具"), ("Puppet Overlap Pin Tool", "人偶重疊控點工具"), + ("Shortcut", "快速鍵"), + ("Range", "範圍"), + ("Play From", "從下列位置播放"), + ("Frame Rate", "影格速率"), + ("Resolution", "解析度"), + ("Overlays", "覆疊"), + ("Layer Controls", "圖層控制"), + ("Loop", "循環"), + ("Cache Before Playback", "播放前快取"), + ("Pre-roll", "預卷"), + ("Post-roll", "後卷"), + ("If caching, play cached frames", "快取時播放已快取影格"), ]; /// Ukrainian (uk): original translations of the same interface rows as the Chinese catalogs. @@ -5885,8 +5909,6 @@ mod tests { rows.iter().find(|(source, _)| *source == en).map_or(en, |(_, translated)| *translated) } - /// A row that translates nothing, repeats a source string, or changes the number of values the - /// English source carries is a bug in the catalog itself. #[test] fn the_toolbar_asks_the_catalog_for_the_tool_names() { let header = include_str!("../header.rs"); @@ -5899,6 +5921,45 @@ mod tests { ); } + #[test] + fn the_preview_panel_labels_are_translated() { + // The Preview panel draws these from a local closure, a check table and a roll tuple, none of + // which the call-shape scan sees; they had no rows at all. + for key in [ + "Shortcut", + "Range", + "Play From", + "Frame Rate", + "Resolution", + "Overlays", + "Layer Controls", + "Loop", + "Cache Before Playback", + "Pre-roll", + "Post-roll", + "If caching, play cached frames", + ] { + for (name, table) in [("Simplified", SIMPLIFIED_CHINESE), ("Traditional", TRADITIONAL_CHINESE)] { + let value = table.iter().find(|(k, _)| *k == key).map(|(_, v)| *v); + assert!(value.is_some_and(|v| !v.is_empty()), "{key} has no {name} row"); + } + } + // A row is not a translation until the panel asks for it: the labels drawn one by one carry + // `tr(..)`, and the table and the roll tuple wrap the text they read at the call. + let source = include_str!("../panels/misc.rs"); + for literal in ["Shortcut", "Range", "Play From", "Frame Rate", "Resolution", "Full Screen", "If caching, play cached frames"] { + assert!(source.contains(&format!("tr(\"{literal}\")")), "{literal} is drawn without asking the catalog"); + } + for call in ["check_row(app, ui, &p, pos2(cx, cy), key, tr(text), on)", "Align2::LEFT_CENTER, tr(text),"] { + assert!(source.contains(call), "{call} no longer asks the catalog"); + } + for table in ["\"Overlays\"", "\"Layer Controls\"", "\"Loop\"", "\"Cache Before Playback\"", "\"Pre-roll\"", "\"Post-roll\""] { + assert!(source.contains(table), "{table} is not in the panel"); + } + } + + /// A row that translates nothing, repeats a source string, or changes the number of values the + /// English source carries is a bug in the catalog itself. #[test] fn catalogs_are_unique_and_keep_the_english_shape() { // Grouped by language: a source listed in two tables of one language is still a repeat. diff --git a/crates/ui-egui/src/panels/misc.rs b/crates/ui-egui/src/panels/misc.rs index dfa9a42e0..65e188c90 100644 --- a/crates/ui-egui/src/panels/misc.rs +++ b/crates/ui-egui/src/panels/misc.rs @@ -61,7 +61,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { let mut yy = y + 46.0; // Shortcut. - label(yy, "Shortcut"); + label(yy, tr("Shortcut")); let names: Vec = PreviewShortcut::ALL.iter().map(|s| s.label().to_string()).collect(); if let Some(i) = dropdown_row( app, @@ -91,14 +91,14 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { let row = if i < 4 { i / 2 } else { i - 2 }; let cx = x0 + col as f32 * 110.0 - 2.0; let cy = yy + row as f32 * 22.0; - if check_row(app, ui, &p, pos2(cx, cy), key, text, on) { + if check_row(app, ui, &p, pos2(cx, cy), key, tr(text), on) { changes.push(json!({"shortcut": current.id(), key: !on})); } } yy += 4.0 * 22.0 + 6.0; // Range (and the pre/post-roll of Play Around Current Time). - label(yy, "Range"); + label(yy, tr("Range")); let ranges: Vec = PreviewRange::ALL.iter().map(|r| r.label().to_string()).collect(); let range_ids = ["workArea", "workAreaExtended", "entireDuration", "aroundCurrentTime"]; if let Some(i) = dropdown_row( @@ -116,7 +116,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { if o.range == PreviewRange::AroundCurrentTime { for (k, (key, text, v)) in [("preRoll", "Pre-roll", o.pre_roll), ("postRoll", "Post-roll", o.post_roll)].into_iter().enumerate() { let lx = xv + k as f32 * 100.0; - p.text(pos2(lx, yy), Align2::LEFT_CENTER, text, Tokens::ui(11.5), t.text_dim); + p.text(pos2(lx, yy), Align2::LEFT_CENTER, tr(text), Tokens::ui(11.5), t.text_dim); let (r, nv, _) = widgets::hot_number_at( ui, pos2(lx + 52.0, yy - 9.0), @@ -134,7 +134,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { } // Play From. - label(yy, "Play From"); + label(yy, tr("Play From")); let froms: Vec = PlayFrom::ALL.iter().map(|f| f.label().to_string()).collect(); if let Some(i) = dropdown_row( app, @@ -151,7 +151,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { // Frame Rate, Skip, Resolution. let comp_fps = app.session.active_comp().map(|c| c.frame_rate.as_f64()); - label(yy, "Frame Rate"); + label(yy, tr("Frame Rate")); const RATES: [f64; 12] = [8.0, 10.0, 12.0, 12.5, 15.0, 23.976, 24.0, 25.0, 29.97, 30.0, 50.0, 60.0]; let auto = format!("({:.2}) Auto", comp_fps.unwrap_or(0.0)); let mut rates = vec![auto.clone()]; @@ -180,7 +180,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { changes.push(json!({"shortcut": current.id(), "skip": nv.round() as u32})); } yy += 24.0; - label(yy, "Resolution"); + label(yy, tr("Resolution")); let ress: Vec = PreviewResolution::ALL.iter().map(|r| r.label().to_string()).collect(); let res_ids = ["auto", "full", "half", "third", "quarter", "custom"]; if let Some(i) = dropdown_row( @@ -209,7 +209,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { } } yy += 24.0; - if check_row(app, ui, &p, pos2(x0 - 2.0, yy), "fullScreen", "Full Screen", o.full_screen) { + if check_row(app, ui, &p, pos2(x0 - 2.0, yy), "fullScreen", tr("Full Screen"), o.full_screen) { changes.push(json!({"shortcut": current.id(), "fullScreen": !o.full_screen})); } yy += 24.0; @@ -217,7 +217,7 @@ pub fn preview(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { // What stopping does. p.text(pos2(x0, yy), Align2::LEFT_CENTER, tr_args("On ({}) stop:", &[&(current.label())]), Tokens::ui(11.5), t.text_dim); yy += 20.0; - if check_row(app, ui, &p, pos2(x0 - 2.0, yy), "playCachedFrames", "If caching, play cached frames", o.play_cached_frames) { + if check_row(app, ui, &p, pos2(x0 - 2.0, yy), "playCachedFrames", tr("If caching, play cached frames"), o.play_cached_frames) { changes.push(json!({"shortcut": current.id(), "playCachedFrames": !o.play_cached_frames})); } yy += 22.0;