i18n: the Preview panel's labels reach the catalog

The Preview panel draws Shortcut, Range, Play From, Frame Rate, Resolution, Overlays, Layer
Controls, Loop, Cache Before Playback, Pre-roll, Post-roll, Full Screen and the caching hint from a
local closure, a check table and a roll tuple, so the call-shape scan cannot see them and no catalog
had rows for most of them. They ask the catalog now, with Simplified and Traditional rows.

Verified on Linux: fmt, cargo test -p effectcraft-ui-egui, clippy --all-targets -D warnings.
This commit is contained in:
FeiLiuEM committed 2026-10-29 19:33:57 +08:00
1 parent 990fa668cd
commit 02211f29eb
2 files changed
+70 -9

No files matched your search

+61
View File
@@ -1641,6 +1641,18 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Total Offset", "总偏移"),
("Per Layer", "每个图层"),
("Track", "跟踪"),
("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)] = &[
@@ -2327,6 +2339,18 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Total Offset", "總偏移"),
("Per Layer", "每個圖層"),
("Track", "追蹤"),
("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.
@@ -5518,6 +5542,43 @@ mod tests {
/// 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_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");
}
}
#[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.
+9 -9
View File
@@ -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<String> = 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<String> = 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<String> = 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<String> = 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;