mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
M7: find macOS's downloadable CJK system fonts (#406)
Current macOS keeps PingFang and other on-demand fonts in its asset store, which the font scan never walked, so Chinese text showed as boxes. The scan now also walks the com_apple_MobileAsset_Font* folders of /System/Library/AssetsV2 (and the older /System/Library/Assets) with the same depth, size and file checks, and Hiragino Sans GB and Heiti SC (preinstalled) join the Chinese UI faces and the text fallbacks. Tested with a folder laid out the same way; not run on a Mac.
This commit is contained in:
@@ -406,7 +406,7 @@ fn names(f: &skrifa::FontRef) -> (Option<String>, Option<String>) {
|
||||
}
|
||||
|
||||
/// Families tried, in order, for characters a text's own font lacks.
|
||||
const FALLBACK_FAMILIES: [&str; 18] = [
|
||||
const FALLBACK_FAMILIES: [&str; 20] = [
|
||||
"Inter",
|
||||
"Helvetica Neue",
|
||||
"Arial",
|
||||
@@ -416,6 +416,8 @@ const FALLBACK_FAMILIES: [&str; 18] = [
|
||||
"BIZ UDPGothic",
|
||||
"Hiragino Sans",
|
||||
"PingFang SC",
|
||||
"Hiragino Sans GB",
|
||||
"Heiti SC",
|
||||
"Noto Sans CJK SC",
|
||||
"Microsoft YaHei",
|
||||
"Apple SD Gothic Neo",
|
||||
@@ -691,11 +693,17 @@ fn system_font_dirs() -> Vec<std::path::PathBuf> {
|
||||
let mut v: Vec<std::path::PathBuf> = Vec::new();
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Subfolders (`Supplemental`) are walked too.
|
||||
v.push("/System/Library/Fonts".into());
|
||||
v.push("/Library/Fonts".into());
|
||||
if let Some(h) = std::env::var_os("HOME") {
|
||||
v.push(std::path::PathBuf::from(h).join("Library/Fonts"));
|
||||
}
|
||||
// Fonts the system downloads (PingFang and other CJK faces on current versions) live in
|
||||
// its asset store; `Assets` is the older versions' store.
|
||||
for store in ["/System/Library/AssetsV2", "/System/Library/Assets"] {
|
||||
v.extend(font_asset_dirs(std::path::Path::new(store)));
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -714,6 +722,15 @@ fn system_font_dirs() -> Vec<std::path::PathBuf> {
|
||||
v
|
||||
}
|
||||
|
||||
/// The font folders (`com_apple_MobileAsset_Font*`, each holding `<id>.asset/AssetData/*.ttc`) in
|
||||
/// the macOS asset store `store`: only these are walked, not the store's voices, dictionaries and
|
||||
/// models.
|
||||
#[cfg(any(target_os = "macos", test))]
|
||||
fn font_asset_dirs(store: &std::path::Path) -> Vec<std::path::PathBuf> {
|
||||
let Ok(rd) = std::fs::read_dir(store) else { return Vec::new() };
|
||||
rd.flatten().filter(|e| e.file_name().to_str().is_some_and(|n| n.starts_with("com_apple_MobileAsset_Font"))).map(|e| e.path()).collect()
|
||||
}
|
||||
|
||||
// Fontconfig's default configuration uses $XDG_DATA_HOME/fonts for user fonts.
|
||||
// Ignore relative XDG paths, as required by the XDG Base Directory Specification.
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
|
||||
@@ -70,6 +70,44 @@ fn the_scan_reads_names_like_a_whole_file_read() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macos_font_assets_are_found_in_the_asset_store() {
|
||||
// A store laid out like macOS's: fonts downloaded on demand sit in `<id>.asset/AssetData`
|
||||
// folders under `com_apple_MobileAsset_Font*`; other assets are never walked. Untested on a Mac.
|
||||
struct TempDir(std::path::PathBuf);
|
||||
impl Drop for TempDir {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
let tmp = TempDir(std::env::temp_dir().join(format!("figcraft-font-assets-{}", std::process::id())));
|
||||
let store = tmp.0.join("AssetsV2");
|
||||
let fonts = store.join("com_apple_MobileAsset_Font7");
|
||||
let data = |kind: &std::path::Path| kind.join("0f1e2d.asset").join("AssetData");
|
||||
for dir in [data(&fonts), data(&store.join("com_apple_MobileAsset_VoiceServices"))] {
|
||||
std::fs::create_dir_all(dir).unwrap();
|
||||
}
|
||||
// A file under a font-like name is listed but walks to nothing.
|
||||
std::fs::write(store.join("com_apple_MobileAsset_Font7.plist"), b"not a folder").unwrap();
|
||||
let found = font_asset_dirs(&store);
|
||||
assert!(found.contains(&fonts) && !found.iter().any(|d| d.ends_with("com_apple_MobileAsset_VoiceServices")), "{found:?}");
|
||||
assert!(font_asset_dirs(&tmp.0.join("missing")).is_empty());
|
||||
// With an installed font to copy in (none on CI), the scan finds it only in the font folder.
|
||||
let installed = system_font_dirs()
|
||||
.into_iter()
|
||||
.find_map(|d| std::fs::read_dir(d).into_iter().flatten().flatten().map(|e| e.path()).find(|p| p.extension().is_some_and(|x| x == "ttf")));
|
||||
let Some(font) = installed else { return };
|
||||
for kind in ["com_apple_MobileAsset_Font7", "com_apple_MobileAsset_VoiceServices"] {
|
||||
std::fs::copy(&font, data(&store.join(kind)).join("Font.ttf")).unwrap();
|
||||
}
|
||||
let mut pending = BTreeMap::new();
|
||||
for dir in found {
|
||||
scan_dir(&dir, 0, &mut pending);
|
||||
}
|
||||
let paths: Vec<_> = pending.values().flat_map(|(_, p)| p.iter().map(|p| p.path.clone())).collect();
|
||||
assert!(!paths.is_empty() && paths.iter().all(|p| p.starts_with(&fonts)), "{paths:?}");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn the_scan_skips_named_pipes() {
|
||||
|
||||
@@ -37,6 +37,8 @@ const CHINESE: &[&str] = &[
|
||||
"Microsoft YaHei UI",
|
||||
"Microsoft YaHei",
|
||||
"PingFang SC",
|
||||
"Hiragino Sans GB",
|
||||
"Heiti SC",
|
||||
"Noto Sans SC",
|
||||
"Source Han Sans SC",
|
||||
"WenQuanYi Micro Hei",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (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; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (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; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog) · **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
|
||||
@@ -68,7 +68,9 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||
- **CJK UI fonts:** egui's fonts and Inter have no CJK glyphs. Once the UI language, the open
|
||||
file's names or comments, typed text or the language picker needs Japanese kana, Hangul or Han,
|
||||
the desktop app adds one installed face per script to the UI's fallbacks (Windows: Yu Gothic UI,
|
||||
Meiryo, Malgun Gothic, Microsoft YaHei; macOS: Hiragino Sans, Apple SD Gothic Neo, PingFang SC;
|
||||
Meiryo, Malgun Gothic, Microsoft YaHei; macOS: Hiragino Sans, Apple SD Gothic Neo, PingFang SC,
|
||||
Hiragino Sans GB, Heiti SC, also from the system's downloaded font assets
|
||||
(`/System/Library/AssetsV2/com_apple_MobileAsset_Font*`, where current versions keep PingFang);
|
||||
Linux and BSD: Noto Sans CJK, Source Han Sans, Nanum, WenQuanYi; the craft-fonts build's BIZ
|
||||
UDPGothic first), the UI language's script first; Han without kana outside a Japanese or Korean UI
|
||||
reads as Chinese, and a Chinese UI puts its Chinese face (Microsoft YaHei, PingFang SC, Noto Sans
|
||||
@@ -118,6 +120,7 @@ Hindi, Vietnamese and Arabic. Traditional Chinese is not planned (no catalog; it
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-17 | minor | Chinese showed as boxes on macOS (#406): the font scan also walks the system's downloaded font assets (`/System/Library/AssetsV2` and the older `/System/Library/Assets`, only their `com_apple_MobileAsset_Font*` folders), where current versions keep PingFang SC, and Hiragino Sans GB and Heiti SC join the Chinese UI faces and the text fallbacks. Tested with a folder laid out the same way; not yet run on a Mac |
|
||||
| 2026-10-17 | minor | String counts re-measured from the catalogs: 1,680 UI keys, 561 command labels and 31 menu wordings (2,272 entries); Spanish complete, the other drafts at 95.3–99.2% (strings added since 2026-10-12 show English), es-419 101 entries. Indonesian has a draft (98.8%; it was listed as none); Russian (98.8%, reviewed), Swedish (99.6%, command labels and menu wordings from #599), Turkish (98.7%) and Czech (98.8%) listed with counts; the language picker's list updated; a paragraph repeated by a merge removed. Grade and hours unchanged (Indonesian's draft is offset by the drafts' new gaps) |
|
||||
| 2026-10-14 | minor | Windows UI language (#291): the interface language comes from Windows' first preferred language alone; that list is also the input-language list (a German keyboard on a Polish system adds Deutsch), and taking the first entry with a catalog turned the interface German there. A first preferred language without a catalog reads English; macOS AppleLanguages and gettext's `LANGUAGE` still take the first language with a catalog |
|
||||
| 2026-10-12 | minor | Drafts catch up again, and the counts with them: French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese translate the 71 UI strings and 5 command labels they lacked since sharing with a join code and open hosting (the Share popover, join codes, owner tokens, reports, the .fig export notes), and with Spanish the 26 strings and 3 command labels of your online files (Home ▸ Online, its menu and dialogs, the marks on tabs); machine-drafted in each file's conventions, still drafts. The Latin American overlay adds computadora and agrega where the new Spanish says ordenador and añade (98 entries). 2,076 entries per catalog (1,503 UI strings, 544 command labels, 29 menu wordings) |
|
||||
|
||||
Reference in New Issue
Block a user