diff --git a/crates/ui-egui/src/panels/text_panels.rs b/crates/ui-egui/src/panels/text_panels.rs
--- a/crates/ui-egui/src/panels/text_panels.rs
+++ b/crates/ui-egui/src/panels/text_panels.rs
@@ -717,10 +717,11 @@ pub(super) fn font_picker(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: egui:
             let resp = ui.add_sized([row_width, 24.0], egui::Button::selectable(index == selected, ""));
             resp.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, ui.is_enabled(), index == selected, &r.display));
             let label_rect = Rect::from_min_size(resp.rect.min + vec2(5.0, 0.0), vec2(label_width - 5.0, resp.rect.height()));
+            let name = drawn_font_name(ui.ctx(), r);
             ui.painter().with_clip_rect(label_rect.intersect(ui.clip_rect())).text(
                 label_rect.left_center(),
                 Align2::LEFT_CENTER,
-                &r.display,
+                name,
                 Tokens::ui(12.0),
                 t.text,
             );
@@ -754,7 +755,7 @@ pub(super) fn font_picker(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: egui:
             if resp.clicked() {
                 chosen = Some(r.family.clone());
             }
-            resp.on_hover_text(&r.display);
+            resp.on_hover_text(name);
         }
         chosen
     };
@@ -900,6 +901,13 @@ fn align_glyph(p: &egui::Painter, r: Rect, op: &str, distribute: bool, t: &Token
     }
 }
 
+/// The name a font menu row draws: the font's own-language name when the interface fonts have a
+/// glyph for each of its characters, else its English family name. A Simplified Chinese name
+/// such as 微软雅黑 can have characters that the Han face of an English interface lacks.
+fn drawn_font_name<'a>(ctx: &egui::Context, row: &'a effectcraft_engine::FontRow) -> &'a str {
+    if ctx.fonts_mut(|f| f.has_glyphs(&Tokens::ui(12.0), &row.display)) { &row.display } else { &row.family }
+}
+
 /// Where a font's "Sample" outlines (`lines`, baseline at y = 0) go in `room`: `(scale, origin)`,
 /// a point `p` landing at `origin + p * scale`. At their natural size on `baseline` when they fit;
 /// fonts with large or odd metrics are scaled down (never up) and moved inside, so no preview
@@ -958,4 +966,17 @@ mod tests {
         assert!(fit_preview(&[], room, baseline).is_none());
         assert!(fit_preview(&[vec![[f32::NAN, 1.0]]], room, baseline).is_none());
     }
+
+    /// A font menu row draws the font's own-language name when the interface fonts have its
+    /// glyphs, and the English family name when they do not.
+    #[test]
+    fn font_menu_rows_draw_a_name_the_interface_fonts_have() {
+        let ctx = egui::Context::default();
+        crate::theme::install(&ctx, &Tokens::for_kind(Default::default()), "en");
+        ctx.run_ui(Default::default(), |_| {}).textures_delta.clear();
+        let row = |display: &str| effectcraft_engine::FontRow { family: "Menu Name Test".into(), display: display.into(), recent: false, variable: false };
+        // U+F1234 is in a private use plane that no interface font maps.
+        assert_eq!(drawn_font_name(&ctx, &row("Menu \u{F1234}")), "Menu Name Test");
+        assert_eq!(drawn_font_name(&ctx, &row("Nom du menu")), "Nom du menu");
+    }
 }
diff --git a/crates/ui-egui/tests/it/ui_font_search.rs b/crates/ui-egui/tests/it/ui_font_search.rs
--- a/crates/ui-egui/tests/it/ui_font_search.rs
+++ b/crates/ui-egui/tests/it/ui_font_search.rs
@@ -106,6 +106,52 @@ fn font_search_preserves_edited_text_and_applies_only_to_selected_characters() {
     assert_eq!(doc.style_at(8).font, "Inter");
 }
 
+/// A font file of just `name`: the family and Regular in English, and the family again in
+/// Simplified Chinese (language 0x804).
+fn font_with_native_name(family: &str, native: &str) -> Vec<u8> {
+    let u16s = |v: &[u16]| -> Vec<u8> { v.iter().flat_map(|x| x.to_be_bytes()).collect() };
+    let records = [(0x409, 1, family), (0x409, 2, "Regular"), (0x804, 1, native)];
+    let strings: Vec<Vec<u8>> = records.iter().map(|(_, _, s)| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();
+    let mut name = u16s(&[0, records.len() as u16, 6 + 12 * records.len() as u16]);
+    let mut off = 0;
+    for ((lang, id, _), st) in records.iter().zip(&strings) {
+        name.extend(u16s(&[3, 1, *lang, *id, st.len() as u16, off]));
+        off += st.len() as u16;
+    }
+    strings.iter().for_each(|st| name.extend(st));
+    let mut out = u16s(&[1, 0, 1, 0, 0, 0]);
+    out.extend(b"name");
+    out.extend([0u8; 4]);
+    out.extend(28u32.to_be_bytes());
+    out.extend((name.len() as u32).to_be_bytes());
+    out.extend(name);
+    out
+}
+
+/// A family whose own-language name has a character no interface font has is listed under its
+/// English name: the harness fails on a missing glyph, and the app would draw boxes. Picking the
+/// row still applies the family.
+#[test]
+fn font_menu_lists_a_name_the_interface_fonts_lack_in_english() {
+    const FAMILY: &str = "Native Name Menu Test";
+    // U+F1234 is in a private use plane that no interface font maps.
+    const NATIVE: &str = "中文\u{F1234}字体";
+    assert_eq!(effectcraft_engine::text::fonts::add_font_data(font_with_native_name(FAMILY, NATIVE)).len(), 1);
+    assert_eq!(effectcraft_engine::text::fonts::native_families().get(FAMILY).map(String::as_str), Some(NATIVE));
+    let mut s = Session::default();
+    s.execute("comp.new", json!({"width": 320, "height": 180})).unwrap();
+    let lid = s.execute("layer.newText", json!({"text": "Native", "font": "Inter"})).unwrap()["layer"].as_u64().unwrap();
+    let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(move |_| EffectcraftApp::new(s));
+    h.state_mut().show_panel(PanelKind::Character);
+    h.run_steps(3);
+    click(&mut h, "character.font");
+    type_text(&mut h, "native name menu");
+    let shown: Vec<String> = h.state().auto.query("character.font.option.").iter().map(|e| e.id.clone()).collect();
+    assert_eq!(shown, vec![format!("character.font.option.{FAMILY}")]);
+    click(&mut h, &format!("character.font.option.{FAMILY}"));
+    assert_eq!(layer_doc(&h.state().session, LayerId(lid)).unwrap().font, FAMILY);
+}
+
 /// A font file of just `name` (the family, Regular) and an `fvar` table with one axis pinned at
 /// 400: a variable font as far as the font menu's filter is concerned.
 fn pinned_variable_font(family: &str) -> Vec<u8> {
