Fonts: render Lao text with an installed Lao face instead of tofu

egui's bundled fonts have no Lao, and none of the script fallback faces
(CJK, Thai, Hebrew, symbols) carry it on macOS, so Lao text in a cell,
the formula bar or the interface drew as boxes unless the cell's own font
had Lao glyphs. Add a `sys-lao` fallback: Leelawadee UI / DokChampa on
Windows, Lao Sangam MN / Lao MN on macOS, Noto Sans Lao / Phetsarath OT
on Linux. `fallback_covers_its_script` now checks a Lao sample.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Keooudone Nanhthavilay
2026-10-17 17:39:11 +07:00
co-authored by Claude Opus 5.5
parent f1894c6da6
commit fac8f858cb
2 changed files with 29 additions and 5 deletions
+25 -3
View File
@@ -491,6 +491,7 @@ fn fallback_faces(han: HanOrder) -> Vec<(&'static str, Vec<(String, u32)>)> {
second,
("sys-cjk-hangul", cjk_hangul()),
("sys-thai", thai()),
("sys-lao", lao()),
("sys-hebrew", hebrew()),
("sys-symbols", symbols()),
("sys-symbols-2", symbols_more()),
@@ -596,6 +597,26 @@ fn thai() -> Vec<(String, u32)> {
v
}
/// Lao faces. Windows ships Leelawadee UI (and DokChampa on older versions); macOS has Lao Sangam
/// MN and Lao MN; Linux usually has Noto Sans Lao or Phetsarath OT.
#[cfg(not(target_arch = "wasm32"))]
fn lao() -> Vec<(String, u32)> {
let mac_sup = "/System/Library/Fonts/Supplemental";
let win = std::env::var("WINDIR").map(|w| format!("{w}\\Fonts")).unwrap_or_else(|_| "C:\\Windows\\Fonts".into());
let lin = ["/usr/share/fonts/truetype/noto", "/usr/share/fonts/opentype/noto", "/usr/share/fonts/truetype", "/usr/share/fonts"];
let mut v: Vec<(String, u32)> = Vec::new();
v.push((format!("{win}\\LeelawUI.ttf"), 0)); // Leelawadee UI
v.push((format!("{win}\\dokchamp.ttf"), 0)); // DokChampa
v.push((format!("{mac_sup}/Lao Sangam MN.ttf"), 0));
v.push((format!("{mac_sup}/Lao MN.ttc"), 0));
for d in lin {
v.push((format!("{d}/NotoSansLao-Regular.ttf"), 0));
v.push((format!("{d}/lao/Phetsarath_OT.ttf"), 0));
v.push((format!("{d}/Phetsarath_OT.ttf"), 0));
}
v
}
/// Hebrew faces. Windows has David and FrankRuehl (and Tahoma/Courier New carry Hebrew); macOS has
/// Arial Hebrew and David; Linux usually has Noto Sans Hebrew or DejaVu Sans.
#[cfg(not(target_arch = "wasm32"))]
@@ -815,11 +836,11 @@ mod tests {
let keys = |han| fallback_faces(han).into_iter().map(|(k, _)| k).collect::<Vec<_>>();
assert_eq!(
keys(HanOrder::Japanese),
["sys-cjk-han-ja", "sys-cjk-han-zh", "sys-cjk-hangul", "sys-thai", "sys-hebrew", "sys-symbols", "sys-symbols-2"]
["sys-cjk-han-ja", "sys-cjk-han-zh", "sys-cjk-hangul", "sys-thai", "sys-lao", "sys-hebrew", "sys-symbols", "sys-symbols-2"]
);
assert_eq!(
keys(HanOrder::Chinese),
["sys-cjk-han-zh", "sys-cjk-han-ja", "sys-cjk-hangul", "sys-thai", "sys-hebrew", "sys-symbols", "sys-symbols-2"]
["sys-cjk-han-zh", "sys-cjk-han-ja", "sys-cjk-hangul", "sys-thai", "sys-lao", "sys-hebrew", "sys-symbols", "sys-symbols-2"]
);
assert_eq!(HanOrder::of(Language::Ja), HanOrder::Japanese);
for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr, Language::De, Language::Cs] {
@@ -848,11 +869,12 @@ mod tests {
// Real render check, per script: the proportional family (what cells and the UI use) must
// resolve a sample string to a real glyph, not the tofu replacement char. Skips a script
// whose face is absent (a minimal machine) and fails if a face is present but not wired in.
let cases: [(&str, Vec<(String, u32)>, &str); 7] = [
let cases: [(&str, Vec<(String, u32)>, &str); 8] = [
("sys-cjk-han-ja", cjk_han_japanese(), "カテゴリ"),
("sys-cjk-han-zh", cjk_han_simplified(), "页面布局 客户端配置"),
("sys-cjk-hangul", cjk_hangul(), "한글"),
("sys-thai", thai(), "ทดสอบ"),
("sys-lao", lao(), "ພາສາລາວ"),
("sys-hebrew", hebrew(), "עברית"),
("ui", candidates(UI), "Русский"),
// Czech interface labels: every Czech accented letter, in both cases.
+4 -2
View File
@@ -1,6 +1,6 @@
# Localization parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Format Cells dialog looks its labels up in the catalog) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Lao system-font fallback) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
Per-language status against Excel, which ships its UI in dozens of languages (Windows language
packs cover 100+), with localized function names, argument separators, number and date formats,
@@ -26,7 +26,8 @@ right-to-left sheets and full complex-script support. Part of
localized function names and `;` separators (#45). German with localized numbers and formulas
is in PR #163 (open).
- **Script support.** System-font fallback for CJK, Thai and Hebrew landed in #38 (closes #32;
#142 is still open pending confirmation). There is no complex-text shaping: Arabic and
#142 is still open pending confirmation); Lao falls back to Lao Sangam MN / Lao MN (macOS),
Leelawadee UI / DokChampa (Windows) or Noto Sans Lao / Phetsarath OT (Linux). There is no complex-text shaping: Arabic and
Devanagari don't join or reorder, and there is no bidirectional layout. **Sheet Right-to-Left**
(`view.rightToLeft`, Page Layout › Sheet Options, XLSX `rightToLeft`) mirrors the grid (#146):
column A and the row headers on the right, the vertical scrollbar on the left; hit-testing,
@@ -78,6 +79,7 @@ language (none so far) and of localized function names.
| Date | Change | Summary |
|---|---|---|
| 2026-10-17 | minor | Lao text falls back to an installed Lao face (`sys-lao`) instead of tofu boxes |
| 2026-10-12 | minor | Format Cells looks up its tabs, labels, options and note through `Language::tr` (Fill tab keyed `Format Cells\|Fill`); dialog titles and OK/Cancel too. Labels already in a table (Number, Currency, Fill…) show translated; the rest fall back to English until the tables add them |
| 2026-10-12 | minor | Brazilian Portuguese catalog (`i18n/pt.rs`) grows from 377 to 923 entries: every key the other catalogs translate, including the dialog strings #669 starts looking up (~42%) |
| 2026-10-11 | minor | German ribbon catalog (`i18n/de.rs`, 266 labels, ~12%); `de` locale tags (de-DE, de-AT, de-CH…) resolve to German. PR #163 (localized German formulas/numbers) still open |