diff --git a/crates/ui-egui/src/i18n/ui.rs b/crates/ui-egui/src/i18n/ui.rs
--- a/crates/ui-egui/src/i18n/ui.rs
+++ b/crates/ui-egui/src/i18n/ui.rs
@@ -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.
@@ -5502,6 +5526,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.
diff --git a/crates/ui-egui/src/panels/misc.rs b/crates/ui-egui/src/panels/misc.rs
--- 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<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), egui::Id::unique(("pv-roll", key)), v, 0.05, (0.0, 600.0), 1, "s", &t);
             app.auto.add(&format!("preview.{key}"), r, text);
             if let Some(nv) = nv {
@@ -127,7 +127,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,
@@ -144,7 +144,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()];
@@ -166,7 +166,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(
@@ -189,7 +189,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;
@@ -197,7 +197,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;
