Merge pull request #2018 from TrevorBrown-dev/fix/ui-script-fallbacks-2014

M10: the UI takes a system face for Ethiopic, the Indic scripts and the other scripts its fonts lack (#2014)
This commit is contained in:
BFlat
2026-11-07 10:14:14 +08:00
committed by GitHub
5 changed files with 134 additions and 24 deletions
+3 -2
View File
File diff suppressed because one or more lines are too long
+71 -17
View File
@@ -48,25 +48,79 @@ pub fn single_line(ui: &mut Ui, id: Id, text: &mut String, font: FontId, width:
.response
}
/// One letter per script the UI's own fonts may lack: Hebrew, Arabic, Ethiopic, Devanagari,
/// Gurmukhi, Gujarati, Tamil, Telugu, Kannada, Malayalam, Sinhala, Lao, Myanmar, Khmer, Armenian
/// and Georgian. egui's shaper selects the joined contextual forms itself. Bengali and Thai are
/// not here: [`crate::cjk`] picks their faces, in its own order of preference, after this list.
pub(crate) const SCRIPT_SAMPLES: [char; 16] = [
'\u{05D0}', '\u{0628}', '\u{1200}', '\u{0915}', '\u{0A15}', '\u{0A95}', '\u{0B95}', '\u{0C15}', '\u{0C95}', '\u{0D15}', '\u{0D9A}', '\u{0E81}',
'\u{1000}', '\u{1780}', '\u{0531}', '\u{10D0}',
];
/// System families that draw those scripts, in order of preference: the first installed one that
/// has a script's letter is taken, so a family covering several scripts (Segoe UI, Nirmala UI,
/// Kohinoor, Noto Sans) joins once. Windows, macOS and the common Linux fonts for each script.
pub(crate) const SCRIPT_FAMILIES: [&str; 46] = [
// Hebrew and Arabic.
"Segoe UI",
"Arial",
"Tahoma",
"Noto Sans Hebrew",
"Noto Sans Arabic",
"Arial Hebrew",
"Geeza Pro",
"Lucida Grande",
"DejaVu Sans",
"FreeSans",
"Noto Sans",
// Ethiopic (#2014).
"Ebrima",
"Nyala",
"Kefa",
"Noto Sans Ethiopic",
"Abyssinica SIL",
// Devanagari, Gurmukhi, Gujarati, Tamil, Telugu, Kannada, Malayalam and Sinhala.
"Nirmala UI",
"Kohinoor Devanagari",
"Kohinoor Gujarati",
"Kohinoor Telugu",
"Gurmukhi MN",
"Tamil Sangam MN",
"Kannada Sangam MN",
"Malayalam Sangam MN",
"Sinhala Sangam MN",
"Noto Sans Devanagari",
"Noto Sans Gurmukhi",
"Noto Sans Gujarati",
"Noto Sans Tamil",
"Noto Sans Telugu",
"Noto Sans Kannada",
"Noto Sans Malayalam",
"Noto Sans Sinhala",
"Lohit Devanagari",
// Lao, Myanmar and Khmer.
"Leelawadee UI",
"Lao Sangam MN",
"Myanmar Text",
"Myanmar Sangam MN",
"Khmer Sangam MN",
"Noto Sans Lao",
"Noto Sans Myanmar",
"Noto Sans Khmer",
// Armenian and Georgian.
"Sylfaen",
"Mshtakan",
"Noto Sans Armenian",
"Noto Sans Georgian",
];
/// Faces the UI adds for scripts its own fonts lack (desktop: from the system), as egui font data;
/// `taken` as for [`crate::theme::system_fallbacks`].
/// `taken` as for [`crate::theme::system_fallbacks`]. Hebrew and Arabic names were covered first;
/// Ethiopic and the other scripts in [`SCRIPT_SAMPLES`] drew as boxes in the panels (a String
/// variable's Amharic value, #2014) until the system face for each was taken too. Bengali and
/// Thai keep [`crate::cjk`]'s choice, made after this list.
pub fn script_fallbacks(have: &dyn Fn(char) -> bool, taken: &mut Vec<Arc<figcraft_text::Face>>) -> Vec<(String, egui::FontData)> {
// Hebrew and Arabic letters; egui's shaper selects the joined contextual forms itself.
const SAMPLES: [char; 2] = ['\u{05D0}', '\u{0628}'];
const FAMILIES: [&str; 11] = [
"Segoe UI",
"Arial",
"Tahoma",
"Noto Sans Hebrew",
"Noto Sans Arabic",
"Arial Hebrew",
"Geeza Pro",
"Lucida Grande",
"DejaVu Sans",
"FreeSans",
"Noto Sans",
];
crate::theme::system_fallbacks(SAMPLES, &FAMILIES, have, taken)
crate::theme::system_fallbacks(SCRIPT_SAMPLES, &SCRIPT_FAMILIES, have, taken)
}
#[cfg(test)]
+49
View File
@@ -310,3 +310,52 @@ fn clicks_place_the_logical_caret_by_what_is_shown() {
run(vec![Event::Text("א".into())], &mut text, &mut rect);
assert_eq!(text, "שלוםא");
}
/// Scripts beyond Hebrew and Arabic (#2014): a String variable's Amharic value drew as boxes
/// because the UI took system faces for two scripts only. Now every script in `SCRIPT_SAMPLES`
/// with an installed face gets one, and the UI's fonts then have its glyphs. A machine without a
/// face for a script skips that script (CI has few fonts); a face the UI's own fonts cover is not
/// taken twice.
#[test]
fn other_scripts_take_an_installed_system_face() {
figcraft_text::scan_system_fonts();
let installed = |c: char| figcraft_text::face_with_char(c, &crate::bidi::SCRIPT_FAMILIES).is_some();
let mut taken = Vec::new();
let added = crate::bidi::script_fallbacks(&|_| false, &mut taken);
let scripts = [('\u{1200}', "Ethiopic"), ('\u{0915}', "Devanagari"), ('\u{0B95}', "Tamil"), ('\u{1000}', "Myanmar"), ('\u{0531}', "Armenian")];
let mut checked = 0;
for (c, script) in scripts {
if !installed(c) {
eprintln!("no {script} face on this system; skipping");
continue;
}
let families: Vec<&str> = taken.iter().map(|f| f.family.as_str()).collect();
assert!(taken.iter().any(|f| f.has_char(c)), "a face for {script} is taken: {families:?}");
checked += 1;
}
// A face that several scripts share is added once, under its system key.
let keys: Vec<&str> = added.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(keys.len(), taken.len(), "{keys:?}");
assert!(keys.iter().all(|k| k.starts_with("system-")), "{keys:?}");
let mut seen = keys.clone();
seen.sort_unstable();
seen.dedup();
assert_eq!(seen.len(), keys.len(), "no face twice: {keys:?}");
// Nothing is taken for a script the UI's own fonts draw.
assert!(crate::bidi::script_fallbacks(&|_| true, &mut Vec::new()).is_empty());
if checked == 0 {
eprintln!("no face for any of the sampled scripts on this system; the UI font check was not run");
return;
}
// Through the whole font setup: the UI draws the letters of every script it found a face for.
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx, true, 0);
ctx.run_ui(RawInput::default(), |_| {}).textures_delta.clear();
ctx.fonts_mut(|f| {
for (c, script) in scripts {
if installed(c) {
assert!(f.has_glyph(&regular(11.0), c), "the UI fonts draw {script} ({c:?})");
}
}
});
}
+2 -1
View File
@@ -271,7 +271,8 @@ pub fn on_dark<R>(ui: &mut egui::Ui, f: impl FnOnce(&mut egui::Ui) -> R) -> R {
/// UI fonts: Inter and the Archivo wordmark (from the craft-fonts build input) when present;
/// otherwise egui's defaults. With
/// `system`, also system faces for scripts those lack (Hebrew, joined Arabic) so names in them show,
/// `system`, also system faces for scripts those lack (Hebrew, joined Arabic, Ethiopic, the Indic
/// scripts and the others [`crate::bidi::script_fallbacks`] names) so names in them show,
/// and faces for the CJK scripts in `cjk` ([`crate::cjk`]).
///
/// The craft-fonts set embeds Noto Sans Arabic and Noto Sans Hebrew; with `system` off those
+9 -4
View File
@@ -1,6 +1,6 @@
# Localization parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (documents: a paragraph's base direction can be forced with Text ▸ Text direction, as Add right-to-left text says (#1830); Bosnian draft catalog, one/few/other plurals; German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft; Arabic draft catalog, count-neutral plurals and web font gate; Thai draft catalog and Thai UI fonts; a missing translation fails no check: `cargo xtask i18n-report` lists them; Vietnamese draft catalog; string counts re-measured: 2,352 entries; Russian and Turkish catalogs reviewed by native speakers, Russian one/few/many plurals; Czech and Indonesian drafts; the Japanese draft completed; counts re-measured; Windows UI language from the first preferred language; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog; macOS finds its downloadable CJK system fonts (#406); string counts re-measured: 2,272 entries, the drafts at 95–99%; Swedish command labels and menu wordings; Russian, Swedish, Turkish, Czech and Indonesian listed; a duplicated paragraph removed) · **Target:** Figma Design (desktop app 126.10.7)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-05 · **Change:** minor (; documents: a paragraph's base direction can be forced with Text ▸ Text direction, as Add right-to-left text says (#1830); Bosnian draft catalog, one/few/other plurals; German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft; Arabic draft catalog, count-neutral plurals and web font gate; Thai draft catalog and Thai UI fonts; a missing translation fails no check: `cargo xtask i18n-report` lists them; Vietnamese draft catalog; string counts re-measured: 2,352 entries; Russian and Turkish catalogs reviewed by native speakers, Russian one/few/many plurals; Czech and Indonesian drafts; the Japanese draft completed; counts re-measured; Windows UI language from the first preferred language; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog; macOS finds its downloadable CJK system fonts (#406); string counts re-measured: 2,272 entries, the drafts at 95–99%; Swedish command labels and menu wordings; Russian, Swedish, Turkish, Czech and Indonesian listed; a duplicated paragraph removed) · **Target:** Figma Design (desktop app 126.10.7)
FigCraft's interface has a localization layer with an English catalog, a Russian catalog reviewed
by a native speaker, and Spanish (with a Latin American overlay), French, German, Brazilian
@@ -60,8 +60,12 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
layers; a paragraph's base direction forced with Text ▸ Text direction (#1830); right-to-left
list markers and indents; right-to-left names in the layers panel, tabs
and canvas labels (visual order, joined Arabic); inline IME composition with the candidate window
at the caret; system-font fallback for scripts the UI fonts lack. Devanagari shapes through
harfrust. No vertical text (Figma has none either).
at the caret; system-font fallback for scripts the UI fonts lack: Hebrew and Arabic, Bengali
and Thai (with the CJK faces), and since #2014 Ethiopic, Devanagari, Gurmukhi, Gujarati, Tamil, Telugu, Kannada and Malayalam, Sinhala, Lao, Myanmar, Khmer, Armenian and
Georgian, so a layer name or a String variable's value in them
draws in the panels when the system has a face (`crates/ui-egui/src/bidi.rs`, `SCRIPT_SAMPLES`
and `SCRIPT_FAMILIES`). Devanagari shapes through harfrust. No vertical text (Figma has none
either).
- **Script support in the UI:** menus widen to their longest label; dropdowns, the two-column
labels of the right panel and the home screen's cards keep a long translation on one line with an
ellipsis and show it in full on hover (checked in German, the longest); no mirrored
@@ -91,7 +95,7 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
| English | en | 2,352 / 2,352 (100%, source) | yes | Latin | yes (source) | full | 0 |
| Simplified Chinese (Mandarin) | zh-Hans | 2,216 / 2,352 (94.2%, draft) | yes | CJK text and IME in documents; UI text in an installed Chinese face, ahead of Japanese ones (web: the `FIGCRAFT_EMBED_CJK` build); no Traditional Chinese catalog (zh-TW, zh-HK read English) | no | draft | 1–2 (after native review) |
| Spanish | es, es-419 | 2,352 / 2,352 (100%, draft); es-419 overlays 104 Latin American wordings (draft) | yes | Latin | no | draft | 0–1 (after native review) |
| Hindi | hi | 0 (0%) | no | Devanagari shaping works in documents; UI font fallback to check | no | none | 4–7 |
| Hindi | hi | 0 (0%) | no | Devanagari shaping works in documents; the desktop UI takes an installed Devanagari face (Nirmala UI, Kohinoor Devanagari, Noto Sans Devanagari, Lohit) for names and values (#2014); no UI catalog | no | none | 4–7 |
| Arabic | ar | 1,857 / 1,857 UI strings, 620 command labels, 31 menu wordings (draft, counted 2026-10-28) | catalog complete; native review pending | Existing bidi/shaping and Arabic system fallback; web not offered (font budget excludes Arabic); panels do not mirror | no | draft | review + panel mirroring (not re-estimated) |
| French | fr | 2,328 / 2,352 (99.0%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
| Portuguese | pt-BR | 2,331 / 2,352 (99.1%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
@@ -126,6 +130,7 @@ Hindi and Arabic. Traditional Chinese is not planned (no catalog; its users see
| Date | Change | Summary |
|---|---|---|
| 2026-11-05 | patch | #2014: the panels drew a String variable's Amharic value as boxes: the UI took a system face for Hebrew and Arabic only. `bidi::script_fallbacks` now samples one letter of each of 16 scripts (Ethiopic, Devanagari, Gurmukhi, Gujarati, Tamil, Telugu, Kannada and Malayalam, Sinhala, Lao, Myanmar, Khmer, Armenian and Georgian as well as Hebrew and Arabic; Bengali and Thai keep `crate::cjk`'s faces) against the system's families for each (Windows, macOS, the common Linux fonts), the first installed one that draws the letter taken once. Desktop only: the web build has no system fonts. Test: `bidi::tests::other_scripts_take_an_installed_system_face` (skips scripts the machine has no face for). |
| 2026-11-05 | minor | Documents: a paragraph's base direction can be forced left to right or right to left (Text ▸ Text direction, `text.direction`, `node.set textDirection`, the Type settings control, the `.fig` lines' `sourceDirectionality`), as Add right-to-left text says (#1830); detection stays the default. Four new UI strings (English only; other catalogs fall back). |
| 2026-10-30 | minor | Bosnian (`bs`, Bosanski) draft catalog: every UI string (1,906), command label (629) and menu wording (31), machine-drafted in Latin script with ijekavian forms and marked as a draft awaiting native review. Bosnian plurals: one for 1, 21, 31… (not 11), few for 2–4, 22–24… (not 12–14), other for the rest. Grade and hours unchanged (Bosnian is not one of the twelve key languages) |
| 2026-10-29 | minor | German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft. Nine existing translations corrected; keys, placeholders, coverage, grade and estimates unchanged |