M9: Simplified Chinese draft catalog
- `crates/ui-egui/locales/zh-Hans.toml`: Simplified Chinese, translated from our English catalog:
every UI string (1,371 keys), command label (524) and menu wording (29), 1,924 entries. Concise UI
style, full-width punctuation in Chinese text, a space between Chinese and Latin words or numbers;
plural entries give only `other`. `[meta] status = "draft — needs native review"`; registered as
a draft (not in `COMPLETE`), like fr/de/pt-BR/ja/ko.
- Preferences > Language shows the native name (Simplified Chinese in Chinese characters).
- Detection: zh, zh-CN, zh-SG, zh-Hans, zh-Hans-CN (and zh-Hans-HK) -> zh-Hans. Traditional Chinese
(zh-Hant, zh-TW, zh-HK, zh-MO, zh_TW.UTF-8) has no catalog: it resolves to English, or to the next
OS language in the list, never to Simplified (before this, the regional fallback would have picked
zh-Hans for zh-TW).
- `plural_one` never picks "one" for zh-Hans.
- CJK UI fonts (`cjk.rs`, `theme.rs`, `lib.rs`):
- `cjk::samples` takes the UI language's scripts: a Chinese UI puts Han first, looked up in the
Chinese faces first (Noto Sans CJK SC, Microsoft YaHei UI / YaHei, PingFang SC, ...), even when
the document also has kana.
- `install_fonts`: in a Chinese UI the craft-fonts build's built-in Japanese BIZ UDPGothic goes
after the system faces and doesn't count as drawing Han, so Han takes Simplified Chinese shapes
instead of Japanese ones (and characters BIZ lacks no longer mix in from another face).
- The installed CJK faces are keyed by UI language too, so switching languages lays them out
again (before: switching from Japanese to Chinese kept the Japanese face in front).
- `pref.language` params doc and `docs/mcp.md` list zh-Hans.
- Docs: localization-parity, translating, target-app-parity, ROADMAP, gaps (gap 5), roadmap (focus
item 4). String counts re-measured from source: 1,371 UI keys, 524 command labels, 29 menu
wordings (1,924 entries; the docs said 1,899); fr/de/pt-BR/ja/ko drafts have 1,899 of them (25
strings added since read English), graded 99%. Localization 36% -> 39%, 31-54 h -> 29-51 h; ready
for real work unchanged (~53%); full parity ~267-527 h -> ~265-524 h.
This commit is contained in:
parent
08df188e1a
commit
9f13cb4ba2
24
ROADMAP.md
24
ROADMAP.md
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents)
|
**Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents)
|
||||||
|
|
||||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; Windows QA pass with eight fixes; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%) · **Target:** Figma Design (desktop app 126.10.7)
|
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; Windows QA pass with eight fixes; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%) · **Target:** Figma Design (desktop app 126.10.7)
|
||||||
|
|
||||||
FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow,
|
FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow,
|
||||||
native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through
|
native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through
|
||||||
|
|
@ -19,13 +19,13 @@ start with [`docs/gaps.md`](docs/gaps.md).
|
||||||
| Essentials user | **~57%** | estimated: core features 77.8% × 0.92 launch/stability × 0.92 discoverability × 0.88 opening files people send ([method](docs/target-app-parity.md#essentials-user)) |
|
| Essentials user | **~57%** | estimated: core features 77.8% × 0.92 launch/stability × 0.92 discoverability × 0.88 opening files people send ([method](docs/target-app-parity.md#essentials-user)) |
|
||||||
| Mainstream practitioner | **~49%** | estimated: weekly areas 72.6% × 0.9 interaction × 0.9 stability × 0.85 file exchange with Figma users ([method](docs/target-app-parity.md#mainstream-practitioner)) |
|
| Mainstream practitioner | **~49%** | estimated: weekly areas 72.6% × 0.9 interaction × 0.9 stability × 0.85 file exchange with Figma users ([method](docs/target-app-parity.md#mainstream-practitioner)) |
|
||||||
| Remaining to beta | **~155–280 h** one agent | estimated |
|
| Remaining to beta | **~155–280 h** one agent | estimated |
|
||||||
| Remaining to full parity | **~264–522 h** one agent (~83–145 h wall-clock with 5 agents) | estimated |
|
| Remaining to full parity | **~262–519 h** one agent (~83–145 h wall-clock with 5 agents) | estimated |
|
||||||
|
|
||||||
### Readiness by audience
|
### Readiness by audience
|
||||||
|
|
||||||
| Audience | Ready % | Opus 5.5 agent wall-clock hours to ~95% | Work that dominates |
|
| Audience | Ready % | Opus 5.5 agent wall-clock hours to ~95% | Work that dominates |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Full target (ready for real work) | ~53% | ~270–525 h | multiplayer (42–84), localization (31–54), plugins (8–26), the interaction pass (40–60), GPU canvas (30–60), AI (30–60), the `.fig` import's tail (5–10) |
|
| Full target (ready for real work) | ~53% | ~268–522 h | multiplayer (42–84), localization (29–51), plugins (8–26), the interaction pass (40–60), GPU canvas (30–60), AI (30–60), the `.fig` import's tail (5–10) |
|
||||||
| Mainstream practitioner | ~49% | ~130–244 h | core editor depth incl. the `.fig` import's tail (49–98), the interaction pass (40–60), real-file corpus and platform QA (25–50), `.fig` export for handing work back (15–30) |
|
| Mainstream practitioner | ~49% | ~130–244 h | core editor depth incl. the `.fig` import's tail (49–98), the interaction pass (40–60), real-file corpus and platform QA (25–50), `.fig` export for handing work back (15–30) |
|
||||||
| Essentials user | ~57% | ~60–110 h | the `.fig` import's tail so the files people send open well (5–10), core tool polish (25–45), interaction pass on the core tools (15–25), stability QA (10–20), onboarding and starter templates (5–10) |
|
| Essentials user | ~57% | ~60–110 h | the `.fig` import's tail so the files people send open well (5–10), core tool polish (25–45), interaction pass on the core tools (15–25), stability QA (10–20), onboarding and starter templates (5–10) |
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ native speakers' reviews of the six language drafts need people.
|
||||||
| Stability | 55% | 20–40 | [gaps](docs/gaps.md) |
|
| Stability | 55% | 20–40 | [gaps](docs/gaps.md) |
|
||||||
| Performance | 60% | 30–60 | [hardware-parity](docs/hardware-parity.md) |
|
| Performance | 60% | 30–60 | [hardware-parity](docs/hardware-parity.md) |
|
||||||
| Hardware | 60% | 10–21 | [hardware-parity](docs/hardware-parity.md) |
|
| Hardware | 60% | 10–21 | [hardware-parity](docs/hardware-parity.md) |
|
||||||
| Localization | 36% (layer; English; Spanish with a Latin American overlay, French, German, Brazilian Portuguese, Japanese, Korean drafts; CJK UI fonts) | 31–54 | [localization-parity](docs/localization-parity.md) |
|
| Localization | 39% (layer; English; Spanish with a Latin American overlay, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese drafts; CJK UI fonts) | 29–51 | [localization-parity](docs/localization-parity.md) |
|
||||||
| Platforms | 75% | 10–20 | [hardware-parity](docs/hardware-parity.md) |
|
| Platforms | 75% | 10–20 | [hardware-parity](docs/hardware-parity.md) |
|
||||||
| Ecosystem / plugins | 38% (own WASM plugins with panels, settings and widgets; a self-hosted plugin directory) | 8–26 | [gaps](docs/gaps.md) |
|
| Ecosystem / plugins | 38% (own WASM plugins with panels, settings and widgets; a self-hosted plugin directory) | 8–26 | [gaps](docs/gaps.md) |
|
||||||
| AI features | 0% | 30–60 | [gaps](docs/gaps.md) |
|
| AI features | 0% | 30–60 | [gaps](docs/gaps.md) |
|
||||||
|
|
@ -99,21 +99,22 @@ native speakers' reviews of the six language drafts need people.
|
||||||
| Language | Code | Strings | Status |
|
| Language | Code | Strings | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| English | en | 100% | full |
|
| English | en | 100% | full |
|
||||||
| Simplified Chinese | zh-Hans | 0% | none |
|
| Simplified Chinese | zh-Hans | 100% | draft (needs native review) |
|
||||||
| Spanish | es, es-419 | 100% (es-419: an overlay of 87 entries) | draft (needs native review) |
|
| Spanish | es, es-419 | 100% (es-419: an overlay of 87 entries) | draft (needs native review) |
|
||||||
| Hindi | hi | 0% | none |
|
| Hindi | hi | 0% | none |
|
||||||
| Arabic | ar | 0% | none |
|
| Arabic | ar | 0% | none |
|
||||||
| French | fr | 100% | draft (needs native review) |
|
| French | fr | 99% | draft (needs native review) |
|
||||||
| Portuguese (Brazil) | pt-BR | 100% | draft (needs native review) |
|
| Portuguese (Brazil) | pt-BR | 99% | draft (needs native review) |
|
||||||
| Indonesian | id | 0% | none |
|
| Indonesian | id | 0% | none |
|
||||||
| Japanese | ja | 100% | draft (needs native review) |
|
| Japanese | ja | 99% | draft (needs native review) |
|
||||||
| German | de | 100% | draft (needs native review) |
|
| German | de | 99% | draft (needs native review) |
|
||||||
| Korean | ko | 100% | draft (needs native review) |
|
| Korean | ko | 99% | draft (needs native review) |
|
||||||
| Vietnamese | vi | 0% | none |
|
| Vietnamese | vi | 0% | none |
|
||||||
|
|
||||||
No other languages. Figma ships English, Japanese, Korean, French, German, Spanish and Portuguese
|
No other languages. Figma ships English, Japanese, Korean, French, German, Spanish and Portuguese
|
||||||
(Brazil); FigCraft has the localization layer, English and Spanish (Spain and Latin America),
|
(Brazil); FigCraft has the localization layer, English and Spanish (Spain and Latin America),
|
||||||
French, German, Brazilian Portuguese, Japanese and Korean drafts (Preferences ▸ Language; CJK UI
|
French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts (Preferences ▸
|
||||||
|
Language; Traditional Chinese has no catalog; CJK UI
|
||||||
text uses an installed CJK font, loaded only when needed; adding a language:
|
text uses an installed CJK font, loaded only when needed; adding a language:
|
||||||
[`docs/translating.md`](docs/translating.md)). Document text in any script (bidi, Arabic shaping,
|
[`docs/translating.md`](docs/translating.md)). Document text in any script (bidi, Arabic shaping,
|
||||||
IME) already works. Detail: [`docs/localization-parity.md`](docs/localization-parity.md).
|
IME) already works. Detail: [`docs/localization-parity.md`](docs/localization-parity.md).
|
||||||
|
|
@ -236,6 +237,7 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md).
|
||||||
|
|
||||||
| Date | Change | Summary |
|
| Date | Change | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Localization 36% → 39%, 31–54 h → 29–51 h; ready for real work unchanged (~53%); full parity ~267–527 h → ~265–524 h |
|
||||||
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Localization 35% → 36%, 32–55 h → 31–54 h; ready for real work unchanged (~53%); full parity ~268–528 h → ~267–527 h |
|
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Localization 35% → 36%, 32–55 h → 31–54 h; ready for real work unchanged (~53%); full parity ~268–528 h → ~267–527 h |
|
||||||
| 2026-10-12 | minor | Prototyping tail (gap 15, the part that doesn't need video): key/gamepad triggers take controller buttons (`Gamepad A` … `Gamepad Home`, the standard layout's 17 buttons, from a Controller menu beside the key field; the REST import maps controller triggers instead of skipping them); the player reads controllers on Windows (Windows.Gaming.Input through the `windows` crate the app already used; no new dependency) and in the browser (the Gamepad API), polling only while the prototype has such a trigger and the window has the focus, and the control channel presses buttons (`ui.input {gamepad}`); macOS, Linux and FreeBSD read no controllers yet. Widgets react to clicks in a presented prototype (the canvas's callback and sandbox limits, on the prototype's copy: the file and its history don't change, restarting starts over). `proto.evaluate` computes an expression where a layer's interaction would run (MCP); the mode lookup and set-variable-mode rules are cases in a behaviour suite (they already held). Prototyping breadth 90% → 92%, ready unchanged at 74%, 3–6 h → 2–5 h; area hours 120–260 → 119–259 (core 33–75 → 32–74), Features (depth) 29–68 h → 28–67 h; percentages unchanged at this rounding; full parity ~276–538 h → ~275–537 h |
|
| 2026-10-12 | minor | Prototyping tail (gap 15, the part that doesn't need video): key/gamepad triggers take controller buttons (`Gamepad A` … `Gamepad Home`, the standard layout's 17 buttons, from a Controller menu beside the key field; the REST import maps controller triggers instead of skipping them); the player reads controllers on Windows (Windows.Gaming.Input through the `windows` crate the app already used; no new dependency) and in the browser (the Gamepad API), polling only while the prototype has such a trigger and the window has the focus, and the control channel presses buttons (`ui.input {gamepad}`); macOS, Linux and FreeBSD read no controllers yet. Widgets react to clicks in a presented prototype (the canvas's callback and sandbox limits, on the prototype's copy: the file and its history don't change, restarting starts over). `proto.evaluate` computes an expression where a layer's interaction would run (MCP); the mode lookup and set-variable-mode rules are cases in a behaviour suite (they already held). Prototyping breadth 90% → 92%, ready unchanged at 74%, 3–6 h → 2–5 h; area hours 120–260 → 119–259 (core 33–75 → 32–74), Features (depth) 29–68 h → 28–67 h; percentages unchanged at this rounding; full parity ~276–538 h → ~275–537 h |
|
||||||
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (gap 6): the server keeps plugin bundles (manifest, module, up to 32 assets; an uncompressed length-prefixed format named by its SHA-256) published with the server key (`figcraft-cli plugin publish`, `plugin.directory.publish`), checked in full before they are kept (size cap, manifest and plugin API version, a version newer than the one kept, the module compiled but never run, plain asset names) and capped (`max_plugins`, `max_plugin_mb`, `max_plugins_mb`); anyone who reaches the server lists, searches and fetches them (hash-checked again on read, a shared download budget); the operator moderates with `figcraft-server plugins [remove]`. The app's Plugins ▸ Manage plugins gains a Browse tab: a server's plugins with Install / Update / Remove, an install confirmation that shows the permissions asked for, the source and the SHA-256, the download checked against the listed hash before anything is read, plugins kept per user in hash-named folders; `plugin.directory.list` / `install` (needs `confirm: true`) / `publish` / `unpublish` for scripts and MCP. Not in the browser build yet; no public catalogue, publisher accounts, signatures, ratings or background update checks. Plugins & widgets 55%/32% → 60%/35%, 10–30 h → 8–26 h; Ecosystem dimension 34% → 38%; weighted totals unchanged at this rounding; full parity ~281–548 h → ~279–544 h |
|
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (gap 6): the server keeps plugin bundles (manifest, module, up to 32 assets; an uncompressed length-prefixed format named by its SHA-256) published with the server key (`figcraft-cli plugin publish`, `plugin.directory.publish`), checked in full before they are kept (size cap, manifest and plugin API version, a version newer than the one kept, the module compiled but never run, plain asset names) and capped (`max_plugins`, `max_plugin_mb`, `max_plugins_mb`); anyone who reaches the server lists, searches and fetches them (hash-checked again on read, a shared download budget); the operator moderates with `figcraft-server plugins [remove]`. The app's Plugins ▸ Manage plugins gains a Browse tab: a server's plugins with Install / Update / Remove, an install confirmation that shows the permissions asked for, the source and the SHA-256, the download checked against the listed hash before anything is read, plugins kept per user in hash-named folders; `plugin.directory.list` / `install` (needs `confirm: true`) / `publish` / `unpublish` for scripts and MCP. Not in the browser build yet; no public catalogue, publisher accounts, signatures, ratings or background update checks. Plugins & widgets 55%/32% → 60%/35%, 10–30 h → 8–26 h; Ecosystem dimension 34% → 38%; weighted totals unchanged at this rounding; full parity ~281–548 h → ~279–544 h |
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||||
s.prefs.user_name = if name.is_empty() { super::comment::DEFAULT_AUTHOR.into() } else { name };
|
s.prefs.user_name = if name.is_empty() { super::comment::DEFAULT_AUTHOR.into() } else { name };
|
||||||
Ok(json!({"name": s.prefs.user_name}))
|
Ok(json!({"name": s.prefs.user_name}))
|
||||||
}),
|
}),
|
||||||
spec!("pref.language", "Interface language", "{language: system|en|es|es-419|fr|de|pt-BR|ja|ko|… (no catalog: English)}", false, |s, p| {
|
spec!("pref.language", "Interface language", "{language: system|en|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…; else English}", false, |s, p| {
|
||||||
let lang = req_string("pref.language", p, "language")?;
|
let lang = req_string("pref.language", p, "language")?;
|
||||||
let lang = lang.trim();
|
let lang = lang.trim();
|
||||||
// A BCP 47-style tag ("es", "pt-BR", "zh_Hans") or "system".
|
// A BCP 47-style tag ("es", "pt-BR", "zh_Hans") or "system".
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -73,22 +73,25 @@ pub fn can_show(code: &str) -> bool {
|
||||||
let need = language(code);
|
let need = language(code);
|
||||||
need == 0
|
need == 0
|
||||||
|| !cfg!(target_arch = "wasm32")
|
|| !cfg!(target_arch = "wasm32")
|
||||||
|| samples(need, false).iter().all(|(c, _)| figcraft_text::face_with_char(*c, &["BIZ UDPGothic", "Noto Sans CJK SC"]).is_some())
|
|| samples(need, 0).iter().all(|(c, _)| figcraft_text::face_with_char(*c, &["BIZ UDPGothic", "Noto Sans CJK SC"]).is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A character standing for each script in `need`, with the families to look for it in, the
|
/// A character standing for each script in `need`, with the families to look for it in, the UI
|
||||||
/// language's own script first. Han without kana in a non-Japanese, non-Korean UI reads as Chinese
|
/// language's script (`ui`, see [`language`]) first. Han reads as Chinese (a simplified character,
|
||||||
/// (a simplified character, Chinese faces first); else a Japanese or Korean face draws it.
|
/// Chinese faces first) in a Chinese UI, and without kana in a UI that isn't Japanese or Korean;
|
||||||
fn samples(need: u8, korean_first: bool) -> Vec<(char, Vec<&'static str>)> {
|
/// else a Japanese or Korean face draws it.
|
||||||
let chinese = need & HAN != 0 && need & KANA == 0 && !korean_first;
|
fn samples(need: u8, ui: u8) -> Vec<(char, Vec<&'static str>)> {
|
||||||
let han: (char, Vec<&str>) = match (chinese, korean_first) {
|
let chinese = need & HAN != 0 && (ui == HAN || (ui == 0 && need & KANA == 0));
|
||||||
|
let han: (char, Vec<&str>) = match (chinese, ui) {
|
||||||
(true, _) => ('们', [CHINESE, JAPANESE, KOREAN].concat()),
|
(true, _) => ('们', [CHINESE, JAPANESE, KOREAN].concat()),
|
||||||
(false, true) => ('漢', [KOREAN, JAPANESE, CHINESE].concat()),
|
(false, HANGUL) => ('漢', [KOREAN, JAPANESE, CHINESE].concat()),
|
||||||
(false, false) => ('漢', [JAPANESE, KOREAN, CHINESE].concat()),
|
(false, _) => ('漢', [JAPANESE, KOREAN, CHINESE].concat()),
|
||||||
};
|
};
|
||||||
let mut v = vec![(KANA, ('あ', JAPANESE.to_vec())), (HAN, han), (HANGUL, ('한', KOREAN.to_vec()))];
|
let mut v = vec![(KANA, ('あ', JAPANESE.to_vec())), (HAN, han), (HANGUL, ('한', KOREAN.to_vec()))];
|
||||||
if korean_first {
|
match ui {
|
||||||
v.reverse();
|
HANGUL => v.reverse(),
|
||||||
|
HAN => v.swap(0, 1),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
v.into_iter().filter(|(bit, _)| need & bit != 0).map(|(_, s)| s).collect()
|
v.into_iter().filter(|(bit, _)| need & bit != 0).map(|(_, s)| s).collect()
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +101,7 @@ fn samples(need: u8, korean_first: bool) -> Vec<(char, Vec<&'static str>)> {
|
||||||
/// tried are read from disk.
|
/// tried are read from disk.
|
||||||
pub fn faces(need: u8, lang: &str, have: &dyn Fn(char) -> bool) -> Vec<Arc<Face>> {
|
pub fn faces(need: u8, lang: &str, have: &dyn Fn(char) -> bool) -> Vec<Arc<Face>> {
|
||||||
let mut out: Vec<Arc<Face>> = Vec::new();
|
let mut out: Vec<Arc<Face>> = Vec::new();
|
||||||
for (c, families) in samples(need, language(lang) == HANGUL) {
|
for (c, families) in samples(need, language(lang)) {
|
||||||
if have(c) || out.iter().any(|f| f.has_char(c)) {
|
if have(c) || out.iter().any(|f| f.has_char(c)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -123,20 +126,27 @@ mod tests {
|
||||||
assert_eq!(language("ja"), KANA | HAN);
|
assert_eq!(language("ja"), KANA | HAN);
|
||||||
assert_eq!(language("ko"), HANGUL);
|
assert_eq!(language("ko"), HANGUL);
|
||||||
assert_eq!(language("zh-Hant"), HAN);
|
assert_eq!(language("zh-Hant"), HAN);
|
||||||
|
assert_eq!(language("zh-Hans"), HAN);
|
||||||
assert_eq!(language("fr"), 0);
|
assert_eq!(language("fr"), 0);
|
||||||
assert!(["en", "es", "fr", "de", "pt-BR"].iter().all(|c| can_show(c)));
|
assert!(["en", "es", "es-419", "fr", "de", "pt-BR"].iter().all(|c| can_show(c)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_ui_language_script_comes_first() {
|
fn the_ui_language_script_comes_first() {
|
||||||
let order = |need, ko| samples(need, ko).into_iter().map(|(c, _)| c).collect::<String>();
|
let order = |need, ui| samples(need, ui).into_iter().map(|(c, _)| c).collect::<String>();
|
||||||
assert_eq!(order(KANA | HAN | HANGUL, false), "あ漢한");
|
let first_face = |need, ui| samples(need, ui).first().and_then(|(_, f)| f.first()).copied();
|
||||||
assert_eq!(order(KANA | HAN | HANGUL, true), "한漢あ");
|
assert_eq!(order(KANA | HAN | HANGUL, 0), "あ漢한");
|
||||||
assert_eq!(order(HANGUL, false), "한");
|
assert_eq!(order(KANA | HAN | HANGUL, KANA | HAN), "あ漢한");
|
||||||
|
assert_eq!(order(KANA | HAN | HANGUL, HANGUL), "한漢あ");
|
||||||
|
assert_eq!(order(HANGUL, 0), "한");
|
||||||
// Han without kana outside a Japanese or Korean UI reads as Chinese.
|
// Han without kana outside a Japanese or Korean UI reads as Chinese.
|
||||||
assert_eq!(order(HAN, false), "们");
|
assert_eq!(order(HAN, 0), "们");
|
||||||
assert_eq!(samples(HAN, false).first().and_then(|(_, f)| f.first()).copied(), Some("Noto Sans CJK SC"));
|
assert_eq!(first_face(HAN, 0), Some("Noto Sans CJK SC"));
|
||||||
assert_eq!(samples(HAN, true).first().and_then(|(_, f)| f.first()).copied(), Some("Malgun Gothic"));
|
assert_eq!(first_face(HAN, HANGUL), Some("Malgun Gothic"));
|
||||||
|
// A Chinese UI draws Han in a Chinese face first, Japanese names in it too.
|
||||||
|
assert_eq!(order(KANA | HAN | HANGUL, HAN), "们あ한");
|
||||||
|
assert_eq!(order(KANA | HAN, language("zh-Hans")), "们あ");
|
||||||
|
assert!(["Microsoft YaHei UI", "PingFang SC", "Noto Sans CJK SC"].iter().all(|f| samples(HAN, HAN)[0].1[..4].contains(f)));
|
||||||
assert!(faces(0, "en", &|_| false).is_empty());
|
assert!(faces(0, "en", &|_| false).is_empty());
|
||||||
assert!(faces(KANA | HANGUL | HAN, "ja", &|_| true).is_empty(), "nothing is looked up for scripts the UI draws");
|
assert!(faces(KANA | HANGUL | HAN, "ja", &|_| true).is_empty(), "nothing is looked up for scripts the UI draws");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,16 @@ const LATIN_AMERICA: &[&str] =
|
||||||
|
|
||||||
/// The [`LANGS`] index for one tag: the exact tag ("pt-BR"), else its base language ("es" for
|
/// The [`LANGS`] index for one tag: the exact tag ("pt-BR"), else its base language ("es" for
|
||||||
/// "es_ES.UTF-8"), else a regional catalog of that language ("pt-BR" for "pt-PT"); Latin American
|
/// "es_ES.UTF-8"), else a regional catalog of that language ("pt-BR" for "pt-PT"); Latin American
|
||||||
/// Spanish ("es-MX") reads es-419. A language the UI can't draw (CJK on the web without CJK faces
|
/// Spanish ("es-MX") reads es-419, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) none: it
|
||||||
/// built in) doesn't count.
|
/// has no catalog and Simplified isn't a stand-in. A language the UI can't draw (CJK on the web
|
||||||
|
/// without CJK faces built in) doesn't count.
|
||||||
fn resolve_one(want: &str) -> Option<usize> {
|
fn resolve_one(want: &str) -> Option<usize> {
|
||||||
let tag = want.trim().split(['.', '@']).next().unwrap_or("").replace('_', "-");
|
let tag = want.trim().split(['.', '@']).next().unwrap_or("").replace('_', "-");
|
||||||
let base = tag.split('-').next().unwrap_or("");
|
let base = tag.split('-').next().unwrap_or("");
|
||||||
let has = |subtags: &[&str]| tag.split('-').skip(1).any(|t| subtags.iter().any(|s| s.eq_ignore_ascii_case(t)));
|
let has = |subtags: &[&str]| tag.split('-').skip(1).any(|t| subtags.iter().any(|s| s.eq_ignore_ascii_case(t)));
|
||||||
|
if base.eq_ignore_ascii_case("zh") && (has(&["Hant"]) || (!has(&["Hans"]) && has(&["TW", "HK", "MO"]))) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let find = |t: &str| LANGS.iter().position(|l| l.code.eq_ignore_ascii_case(t));
|
let find = |t: &str| LANGS.iter().position(|l| l.code.eq_ignore_ascii_case(t));
|
||||||
let alias = (base.eq_ignore_ascii_case("es") && has(LATIN_AMERICA)).then(|| find("es-419")).flatten();
|
let alias = (base.eq_ignore_ascii_case("es") && has(LATIN_AMERICA)).then(|| find("es-419")).flatten();
|
||||||
let regional = || LANGS.iter().position(|l| !base.is_empty() && l.code.split('-').next().is_some_and(|b| b.eq_ignore_ascii_case(base)));
|
let regional = || LANGS.iter().position(|l| !base.is_empty() && l.code.split('-').next().is_some_and(|b| b.eq_ignore_ascii_case(base)));
|
||||||
|
|
@ -130,12 +134,12 @@ pub fn trn(key: &str, n: usize, args: &[(&str, &dyn Display)]) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether count `n` takes the "one" form: 0 and 1 in French and Brazilian Portuguese, never in
|
/// Whether count `n` takes the "one" form: 0 and 1 in French and Brazilian Portuguese, never in
|
||||||
/// Japanese and Korean (no plural forms: always "other"), 1 elsewhere (CLDR's integer rules).
|
/// Japanese, Korean and Chinese (no plural forms: always "other"), 1 elsewhere (CLDR's integer rules).
|
||||||
// shortcut: CLDR's "many" (French, Spanish and Portuguese millions) reads as "other" here; add it
|
// shortcut: CLDR's "many" (French, Spanish and Portuguese millions) reads as "other" here; add it
|
||||||
// when a language needs a third form.
|
// when a language needs a third form.
|
||||||
fn plural_one(lang: usize, n: usize) -> bool {
|
fn plural_one(lang: usize, n: usize) -> bool {
|
||||||
match LANGS.get(lang).map(|l| l.code) {
|
match LANGS.get(lang).map(|l| l.code) {
|
||||||
Some("ja" | "ko") => false,
|
Some("ja" | "ko" | "zh-Hans") => false,
|
||||||
Some("fr" | "pt-BR") => n <= 1,
|
Some("fr" | "pt-BR") => n <= 1,
|
||||||
_ => n == 1,
|
_ => n == 1,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -359,12 +359,12 @@ fn french_and_portuguese_count_zero_as_singular() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn japanese_and_korean_have_no_plural_forms() {
|
fn japanese_korean_and_chinese_have_no_plural_forms() {
|
||||||
let code = |pref: &str, system: Option<&str>| LANGS[resolve(pref, system)].code;
|
let code = |pref: &str, system: Option<&str>| LANGS[resolve(pref, system)].code;
|
||||||
assert_eq!(code("system", Some("ja-JP")), "ja");
|
assert_eq!(code("system", Some("ja-JP")), "ja");
|
||||||
assert_eq!(code("system", Some("ko_KR.UTF-8")), "ko");
|
assert_eq!(code("system", Some("ko_KR.UTF-8")), "ko");
|
||||||
assert_eq!(code("system", Some("ja,en-US")), "ja");
|
assert_eq!(code("system", Some("ja,en-US")), "ja");
|
||||||
for code in ["ja", "ko"] {
|
for code in ["ja", "ko", "zh-Hans"] {
|
||||||
let lang = LANGS.iter().position(|l| l.code == code).unwrap();
|
let lang = LANGS.iter().position(|l| l.code == code).unwrap();
|
||||||
assert!(!plural_one(lang, 0) && !plural_one(lang, 1) && !plural_one(lang, 2), "{code}");
|
assert!(!plural_one(lang, 0) && !plural_one(lang, 1) && !plural_one(lang, 2), "{code}");
|
||||||
let ones: Vec<_> = LANGS[lang].ui.iter().filter(|(_, t)| matches!(t, Text::Plural(Some(_), _))).map(|(k, _)| k).collect();
|
let ones: Vec<_> = LANGS[lang].ui.iter().filter(|(_, t)| matches!(t, Text::Plural(Some(_), _))).map(|(k, _)| k).collect();
|
||||||
|
|
@ -372,6 +372,21 @@ fn japanese_and_korean_have_no_plural_forms() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chinese_reads_simplified_and_traditional_has_no_catalog() {
|
||||||
|
let code = |pref: &str, system: Option<&str>| LANGS[resolve(pref, system)].code;
|
||||||
|
for tag in ["zh-CN", "zh_CN.UTF-8", "zh-SG", "zh-Hans", "zh-Hans-CN", "zh-Hans-HK", "zh"] {
|
||||||
|
assert_eq!(code("system", Some(tag)), "zh-Hans", "{tag}");
|
||||||
|
}
|
||||||
|
// No Traditional Chinese catalog: English, never Simplified; else the next OS language.
|
||||||
|
for tag in ["zh-TW", "zh-HK", "zh-MO", "zh-Hant", "zh-Hant-TW", "zh_TW.UTF-8"] {
|
||||||
|
assert_eq!(code("system", Some(tag)), "en", "{tag}");
|
||||||
|
assert_eq!(code(tag, None), "en", "{tag}");
|
||||||
|
}
|
||||||
|
assert_eq!(code("system", Some("zh-TW,ja-JP")), "ja");
|
||||||
|
assert_eq!(LANGS[resolve("zh-Hans", None)].name, "简体中文");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_key_scanner_reads_rust_literals() {
|
fn the_key_scanner_reads_rust_literals() {
|
||||||
let src = r#"x(tr("A \"b\"")); trf(
|
let src = r#"x(tr("A \"b\"")); trf(
|
||||||
|
|
|
||||||
|
|
@ -419,8 +419,8 @@ pub struct FigApp {
|
||||||
worker: Option<render_worker::Worker>,
|
worker: Option<render_worker::Worker>,
|
||||||
fonts_installed: bool,
|
fonts_installed: bool,
|
||||||
/// System faces for scripts the built-in fonts lack were added (after the font scan), with
|
/// System faces for scripts the built-in fonts lack were added (after the font scan), with
|
||||||
/// faces for these CJK scripts ([`cjk`]).
|
/// faces for these CJK scripts ([`cjk`]) in this UI language's order.
|
||||||
script_fonts: Option<u8>,
|
script_fonts: Option<(u8, &'static str)>,
|
||||||
/// The tab (uid) and revision whose names were last checked for CJK scripts.
|
/// The tab (uid) and revision whose names were last checked for CJK scripts.
|
||||||
cjk_checked: Option<(u64, u64)>,
|
cjk_checked: Option<(u64, u64)>,
|
||||||
/// The language preference the UI strings follow (applied when it changes).
|
/// The language preference the UI strings follow (applied when it changes).
|
||||||
|
|
@ -718,10 +718,12 @@ impl FigApp {
|
||||||
} else if figcraft_text::system_fonts_scanned() {
|
} else if figcraft_text::system_fonts_scanned() {
|
||||||
// Once the system fonts are indexed: faces for names in scripts the UI fonts lack, and
|
// Once the system fonts are indexed: faces for names in scripts the UI fonts lack, and
|
||||||
// for each CJK script once something shows it.
|
// for each CJK script once something shows it.
|
||||||
let installed = self.script_fonts.unwrap_or(0);
|
let installed = self.script_fonts.map_or(0, |(s, _)| s);
|
||||||
let cjk = installed | self.cjk_needed(ctx, installed);
|
let cjk = installed | self.cjk_needed(ctx, installed);
|
||||||
if self.script_fonts != Some(cjk) {
|
// The UI language's script leads the CJK faces, so another language lays them out again.
|
||||||
self.script_fonts = Some(cjk);
|
let lang = i18n::current().map_or("en", |l| l.code);
|
||||||
|
if self.script_fonts != Some((cjk, lang)) {
|
||||||
|
self.script_fonts = Some((cjk, lang));
|
||||||
theme::install_fonts(ctx, true, cjk);
|
theme::install_fonts(ctx, true, cjk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,35 +254,34 @@ pub fn install_fonts(ctx: &Context, system: bool, cjk: u8) {
|
||||||
fonts.families.insert(FontFamily::Name("semibold".into()), semi);
|
fonts.families.insert(FontFamily::Name("semibold".into()), semi);
|
||||||
// CJK and other scripts from craft-fonts as fallbacks (Noto Sans CJK only with FIGCRAFT_EMBED_CJK).
|
// CJK and other scripts from craft-fonts as fallbacks (Noto Sans CJK only with FIGCRAFT_EMBED_CJK).
|
||||||
const BUILT_IN: [&str; 4] = ["Inter", "BIZ UDPGothic", "Noto Sans CJK SC", "Noto Sans Arabic"];
|
const BUILT_IN: [&str; 4] = ["Inter", "BIZ UDPGothic", "Noto Sans CJK SC", "Noto Sans Arabic"];
|
||||||
for f in figcraft_text::CRAFT_FONTS.iter().filter(|f| f.family != "Inter" && f.style == "Regular") {
|
let lang = crate::i18n::current().map_or("en", |l| l.code);
|
||||||
if BUILT_IN.contains(&f.family) {
|
// A Chinese UI draws Han in a Chinese face: the built-in Japanese one goes after the system faces.
|
||||||
let key = format!("craft-{}", f.family);
|
let late = |family: &str| family == "BIZ UDPGothic" && crate::cjk::language(lang) == crate::cjk::HAN;
|
||||||
fonts.font_data.insert(key.clone(), Arc::new(FontData::from_static(f.bytes)));
|
let craft = |last: bool| {
|
||||||
for fam in [FontFamily::Proportional, FontFamily::Name("semibold".into())] {
|
figcraft_text::CRAFT_FONTS
|
||||||
if let Some(list) = fonts.families.get_mut(&fam) {
|
.iter()
|
||||||
list.push(key.clone());
|
.filter(move |f| f.family != "Inter" && f.style == "Regular" && BUILT_IN.contains(&f.family) && late(f.family) == last)
|
||||||
}
|
.map(|f| (format!("craft-{}", f.family), FontData::from_static(f.bytes)))
|
||||||
}
|
};
|
||||||
}
|
let mut extra: Vec<(String, FontData)> = craft(false).collect();
|
||||||
}
|
|
||||||
if system {
|
if system {
|
||||||
// What the built-in faces above already draw.
|
// What the built-in faces above already draw.
|
||||||
let embedded = |c: char| {
|
let embedded = |c: char| {
|
||||||
BUILT_IN
|
BUILT_IN
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|f| figcraft_text::CRAFT_FONTS.iter().any(|cf| cf.family == *f))
|
.filter(|f| !late(f) && figcraft_text::CRAFT_FONTS.iter().any(|cf| cf.family == *f))
|
||||||
.filter_map(|f| figcraft_text::find_face(f, "Regular"))
|
.filter_map(|f| figcraft_text::find_face(f, "Regular"))
|
||||||
.any(|x| x.has_char(c))
|
.any(|x| x.has_char(c))
|
||||||
};
|
};
|
||||||
let mut extra = crate::bidi::script_fallbacks(&embedded);
|
extra.extend(crate::bidi::script_fallbacks(&embedded));
|
||||||
let lang = crate::i18n::current().map_or("en", |l| l.code);
|
|
||||||
extra.extend(crate::cjk::faces(cjk, lang, &embedded).iter().map(|f| (format!("system-{}-{}", f.family, f.style), font_data(f))));
|
extra.extend(crate::cjk::faces(cjk, lang, &embedded).iter().map(|f| (format!("system-{}-{}", f.family, f.style), font_data(f))));
|
||||||
for (key, data) in extra {
|
}
|
||||||
fonts.font_data.insert(key.clone(), Arc::new(data));
|
extra.extend(craft(true));
|
||||||
for fam in [FontFamily::Proportional, FontFamily::Name("semibold".into())] {
|
for (key, data) in extra {
|
||||||
if let Some(list) = fonts.families.get_mut(&fam) {
|
fonts.font_data.insert(key.clone(), Arc::new(data));
|
||||||
list.push(key.clone());
|
for fam in [FontFamily::Proportional, FontFamily::Name("semibold".into())] {
|
||||||
}
|
if let Some(list) = fonts.families.get_mut(&fam) {
|
||||||
|
list.push(key.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
docs/gaps.md
21
docs/gaps.md
|
|
@ -1,6 +1,6 @@
|
||||||
# Where FigCraft falls short of Figma
|
# Where FigCraft falls short of Figma
|
||||||
|
|
||||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7)
|
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7)
|
||||||
|
|
||||||
Every known shortfall against Figma, one entry each, ranked by how much it stops a Figma user from
|
Every known shortfall against Figma, one entry each, ranked by how much it stops a Figma user from
|
||||||
doing real work in FigCraft. Agents: pick work from the top unless the owner says otherwise. The
|
doing real work in FigCraft. Agents: pick work from the top unless the owner says otherwise. The
|
||||||
|
|
@ -18,7 +18,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||||
| 2 | Multiplayer and sharing are a preview, without accounts (**beta blocker**) | Feature | 42–84 | [collaboration-parity](collaboration-parity.md) |
|
| 2 | Multiplayer and sharing are a preview, without accounts (**beta blocker**) | Feature | 42–84 | [collaboration-parity](collaboration-parity.md) |
|
||||||
| 3 | Nothing checked against real Figma files | Fidelity | 15–30 | [target-app-parity](target-app-parity.md) |
|
| 3 | Nothing checked against real Figma files | Fidelity | 15–30 | [target-app-parity](target-app-parity.md) |
|
||||||
| 4 | Interaction fidelity not compared tool by tool | UI/UX | 40–60 | [ui-parity](ui-parity.md) |
|
| 4 | Interaction fidelity not compared tool by tool | UI/UX | 40–60 | [ui-parity](ui-parity.md) |
|
||||||
| 5 | Localization: English plus unreviewed Spanish (and Latin American Spanish), French, German, Brazilian Portuguese, Japanese and Korean drafts | Localization | 31–54 | [localization-parity](localization-parity.md) |
|
| 5 | Localization: English plus unreviewed Spanish (and Latin American Spanish), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts | Localization | 29–51 | [localization-parity](localization-parity.md) |
|
||||||
| 6 | Plugins: widgets take clicks only, a self-hosted directory but no public catalogue; Figma's plugins don't run | Ecosystem | 8–26 | [target-app-parity](target-app-parity.md) |
|
| 6 | Plugins: widgets take clicks only, a self-hosted directory but no public catalogue; Figma's plugins don't run | Ecosystem | 8–26 | [target-app-parity](target-app-parity.md) |
|
||||||
| 7 | CPU canvas where Figma uses the GPU | Hardware/performance | 30–60 | [hardware-parity](hardware-parity.md) |
|
| 7 | CPU canvas where Figma uses the GPU | Hardware/performance | 30–60 | [hardware-parity](hardware-parity.md) |
|
||||||
| 8 | Platform QA: Linux and the browser left (Windows pass done 2026-10-11) | Stability/platforms | 10–20 | [hardware-parity](hardware-parity.md) |
|
| 8 | Platform QA: Linux and the browser left (Windows pass done 2026-10-11) | Stability/platforms | 10–20 | [hardware-parity](hardware-parity.md) |
|
||||||
|
|
@ -101,21 +101,23 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||||
- **Estimate:** 40–60 h. Detail in [`ui-parity.md`](ui-parity.md).
|
- **Estimate:** 40–60 h. Detail in [`ui-parity.md`](ui-parity.md).
|
||||||
|
|
||||||
### 5. Localization: English plus unreviewed drafts
|
### 5. Localization: English plus unreviewed drafts
|
||||||
- **Missing:** native reviews of the Spanish, French, German, Brazilian Portuguese, Japanese and
|
- **Missing:** native reviews of the Spanish, French, German, Brazilian Portuguese, Japanese,
|
||||||
Korean drafts and the Latin American Spanish overlay, localized engine messages and number
|
Korean and Simplified Chinese drafts and the Latin American Spanish overlay, the 25 newest strings
|
||||||
|
in five drafts, Hindi, Arabic, Indonesian and Vietnamese, localized engine messages and number
|
||||||
formatting; CJK UI text on the web without the craft-fonts CJK build.
|
formatting; CJK UI text on the web without the craft-fonts CJK build.
|
||||||
- **Evidence (measured from source):** the layer landed 2026-10-11: catalogs in
|
- **Evidence (measured from source):** the layer landed 2026-10-11: catalogs in
|
||||||
`crates/ui-egui/locales/` (1,351 UI keys plus 519 command labels and 29 menu wordings),
|
`crates/ui-egui/locales/` (1,371 UI keys plus 524 command labels and 29 menu wordings),
|
||||||
Preferences ▸ Language, `pref.language`; `en.toml` is complete, and `es.toml`, `fr.toml`,
|
Preferences ▸ Language, `pref.language`; `en.toml` is complete, and `es.toml`, `fr.toml`,
|
||||||
`de.toml`, `pt-BR.toml`, `ja.toml` and `ko.toml` (2026-10-12) are complete machine drafts, with
|
`de.toml`, `pt-BR.toml`, `ja.toml`, `ko.toml` and `zh-Hans.toml` (2026-10-12) are machine drafts
|
||||||
`es-419.toml` a Latin American overlay listing only what differs from Spanish (only Spanish is
|
(all but `zh-Hans.toml` lack the 25 newest strings), with `es-419.toml` a Latin American overlay
|
||||||
held to completeness by the check; the others show English for strings added later).
|
listing only what differs from Spanish (only Spanish is held to completeness by the check; the
|
||||||
|
others show English for strings added later).
|
||||||
CJK UI text uses an installed CJK face the desktop app loads when something on screen needs it
|
CJK UI text uses an installed CJK face the desktop app loads when something on screen needs it
|
||||||
(`crates/ui-egui/src/cjk.rs`). Engine error messages and Dev Mode values stay English.
|
(`crates/ui-egui/src/cjk.rs`). Engine error messages and Dev Mode values stay English.
|
||||||
- **Impact:** non-English designers get machine drafts nobody has reviewed yet, and English
|
- **Impact:** non-English designers get machine drafts nobody has reviewed yet, and English
|
||||||
errors.
|
errors.
|
||||||
- **Estimate:** 3–7 h per language plus native review, 4–6 h for engine messages and numbers;
|
- **Estimate:** 3–7 h per language plus native review, 4–6 h for engine messages and numbers;
|
||||||
31–54 h for the key languages left and the drafts' follow-ups. Detail in
|
29–51 h for the key languages left and the drafts' follow-ups. Detail in
|
||||||
[`localization-parity.md`](localization-parity.md); how to add one in
|
[`localization-parity.md`](localization-parity.md); how to add one in
|
||||||
[`translating.md`](translating.md).
|
[`translating.md`](translating.md).
|
||||||
|
|
||||||
|
|
@ -350,6 +352,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||||
|
|
||||||
| Date | Change | Summary |
|
| Date | Change | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Gap 5 31–54 h → 29–51 h, rank unchanged |
|
||||||
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Gap 5 32–55 h → 31–54 h, rank unchanged |
|
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Gap 5 32–55 h → 31–54 h, rank unchanged |
|
||||||
| 2026-10-12 | minor | Gap 15: controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes and `proto.evaluate` landed; gap 15 now media plus controllers off Windows, 3–6 h → 2–5 h; ranking unchanged |
|
| 2026-10-12 | minor | Gap 15: controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes and `proto.evaluate` landed; gap 15 now media plus controllers off Windows, 3–6 h → 2–5 h; ranking unchanged |
|
||||||
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (publish with the server key, browse, install after a permissions confirmation, hash-checked downloads, updates by version, operator moderation) added to gap 6's landed list; gap 6 10–30 h → 8–26 h; ranking unchanged |
|
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (publish with the server key, browse, install after a permissions confirmation, hash-checked downloads, updates by version, operator moderation) added to gap 6's landed list; gap 6 10–30 h → 8–26 h; ranking unchanged |
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
# Localization parity
|
# Localization parity
|
||||||
|
|
||||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Latin American Spanish overlay) · **Target:** Figma Design (desktop app 126.10.7)
|
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Simplified Chinese draft catalog) · **Target:** Figma Design (desktop app 126.10.7)
|
||||||
|
|
||||||
FigCraft's interface has a localization layer with an English catalog and complete Spanish (with a
|
FigCraft's interface has a localization layer with an English catalog and Spanish (with a Latin
|
||||||
Latin American overlay), French, German, Brazilian Portuguese, Japanese and Korean drafts
|
American overlay), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese
|
||||||
(machine-drafted, waiting for native speakers' review). This page lists the twelve key languages of the craftrules standard, then the other languages Figma
|
drafts (machine-drafted, waiting for native speakers' review). This page lists the twelve key
|
||||||
ships. How to add a language: [`translating.md`](translating.md).
|
languages of the craftrules standard, then the other languages Figma ships. How to add a
|
||||||
|
language: [`translating.md`](translating.md).
|
||||||
|
|
||||||
## What Figma ships
|
## What Figma ships
|
||||||
|
|
||||||
|
|
@ -23,16 +24,18 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||||
(`tr("Fill")`, `trf` for `{named}` placeholders, `trn` for counts with one/other plural forms), so
|
(`tr("Fill")`, `trf` for `{named}` placeholders, `trn` for counts with one/other plural forms), so
|
||||||
a missing translation shows English, never a raw key. One English text that needs two
|
a missing translation shows English, never a raw key. One English text that needs two
|
||||||
translations takes a context (`"Fill|sizing"`).
|
translations takes a context (`"Fill|sizing"`).
|
||||||
- **Strings:** 1,351 UI keys in `en.toml` (36 with plural forms; ~110 are labels from engine and
|
- **Strings:** 1,371 UI keys in `en.toml` (36 with plural forms; ~110 are labels from engine and
|
||||||
document data, such as tool names, blend modes and Dev Mode properties) plus 519 command labels
|
document data, such as tool names, blend modes and Dev Mode properties) plus 524 command labels
|
||||||
and 29 menu wordings by command id: 1,899 entries. Command ids, the CLI, MCP and the control
|
and 29 menu wordings by command id: 1,924 entries. Command ids, the CLI, MCP and the control
|
||||||
channel stay English. Accessibility names come from the same strings.
|
channel stay English. Accessibility names come from the same strings.
|
||||||
- **Language preference:** Preferences ▸ Language (System, English, Deutsch, Español, Español
|
- **Language preference:** Preferences ▸ Language (System, English, Deutsch, Español, Español
|
||||||
(Latinoamérica), Français, 日本語, 한국어, Português (Brasil)), applied at once without a restart;
|
(Latinoamérica), Français, 日本語, 한국어, Português (Brasil), 简体中文), applied at once without a
|
||||||
System takes the first of the OS's preferred languages that has a catalog (Windows' language
|
restart; System takes the first of the OS's preferred languages that has a catalog (Windows'
|
||||||
list, macOS AppleLanguages, `LANGUAGE` then the locale variables on Linux and BSD; a regional tag
|
language list, macOS AppleLanguages, `LANGUAGE` then the locale variables on Linux and BSD; a regional tag
|
||||||
falls back to its language, pt-PT or pt to pt-BR, and es-419, es-MX, es-AR, es-CO and the other
|
falls back to its language, pt-PT or pt to pt-BR, and es-419, es-MX, es-AR, es-CO and the other
|
||||||
Latin American and US Spanish tags to es-419) or the browser's language on the web.
|
Latin American and US Spanish tags to es-419; zh, zh-CN, zh-SG and zh-Hans read zh-Hans, while
|
||||||
|
Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and reads English or the next OS
|
||||||
|
language, never Simplified) or the browser's language on the web.
|
||||||
`pref.language {language}` sets it from the CLI, MCP and the control channel.
|
`pref.language {language}` sets it from the CLI, MCP and the control channel.
|
||||||
- **Regional overlays:** a regional catalog whose language has a catalog lists only what differs
|
- **Regional overlays:** a regional catalog whose language has a catalog lists only what differs
|
||||||
(`es-419.toml` over `es.toml`); its UI strings, command labels and menu wordings read the
|
(`es-419.toml` over `es.toml`); its UI strings, command labels and menu wordings read the
|
||||||
|
|
@ -40,13 +43,13 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||||
- **Check:** `cargo test -p figcraft-ui-egui i18n` fails on keys the code uses that `en.toml`
|
- **Check:** `cargo test -p figcraft-ui-egui i18n` fails on keys the code uses that `en.toml`
|
||||||
lacks, keys no code uses, unknown command ids, placeholders that differ from English, overlay
|
lacks, keys no code uses, unknown command ids, placeholders that differ from English, overlay
|
||||||
entries that read as in their base language and, for complete languages (Spanish), anything
|
entries that read as in their base language and, for complete languages (Spanish), anything
|
||||||
untranslated. The French, German, Brazilian Portuguese, Japanese and Korean drafts translate
|
untranslated. The other drafts are not held to it: a string added later shows English there
|
||||||
everything today but are not held to it: a string added later shows English there until someone
|
until someone translates it. Simplified Chinese translates everything today; French, German,
|
||||||
translates it.
|
Brazilian Portuguese, Japanese and Korean lack the 25 strings added since they were drafted.
|
||||||
- **Not yet localized:** messages the engine writes (errors, a few toasts, Dev Mode property values
|
- **Not yet localized:** messages the engine writes (errors, a few toasts, Dev Mode property values
|
||||||
such as "inside"), number formatting (decimal points stay `.`), and plural forms beyond one/other
|
such as "inside"), number formatting (decimal points stay `.`), and plural forms beyond one/other
|
||||||
(French and Brazilian Portuguese take the singular for 0 as well; Japanese and Korean have one
|
(French and Brazilian Portuguese take the singular for 0 as well; Japanese, Korean and Chinese
|
||||||
form; CLDR's "many" for millions reads as the plural).
|
have one form; CLDR's "many" for millions reads as the plural).
|
||||||
- **Script support in documents:** Unicode bidi with Arabic shaping and mirrored brackets in text
|
- **Script support in documents:** Unicode bidi with Arabic shaping and mirrored brackets in text
|
||||||
layers; right-to-left list markers and indents; right-to-left names in the layers panel, tabs
|
layers; 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
|
and canvas labels (visual order, joined Arabic); inline IME composition with the candidate window
|
||||||
|
|
@ -63,27 +66,29 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||||
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;
|
||||||
Linux and BSD: Noto Sans CJK, Source Han Sans, Nanum, WenQuanYi; the craft-fonts build's BIZ
|
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
|
UDPGothic first), the UI language's script first; Han without kana outside a Japanese or Korean UI
|
||||||
reads as Chinese. Only that face's file is read and egui shares its bytes, so English users load
|
reads as Chinese, and a Chinese UI puts its Chinese face (Microsoft YaHei, PingFang SC, Noto Sans
|
||||||
|
CJK SC) ahead of the built-in Japanese one, so Han takes Simplified Chinese shapes; changing the
|
||||||
|
UI language lays the faces out again. Only that face's file is read and egui shares its bytes, so English users load
|
||||||
nothing (startup and memory measured unchanged); no CJK font installed means tofu, never a
|
nothing (startup and memory measured unchanged); no CJK font installed means tofu, never a
|
||||||
crash. The web build has no system fonts: built with craft-fonts and `FIGCRAFT_EMBED_CJK=1` it
|
crash. The web build has no system fonts: built with craft-fonts and `FIGCRAFT_EMBED_CJK=1` it
|
||||||
embeds Noto Sans CJK (+16 MB) and offers Japanese and Korean; otherwise the language picker and
|
embeds Noto Sans CJK SC (+16 MB) and offers Japanese, Korean and Simplified Chinese; otherwise the language picker and
|
||||||
System leave them out. No font file is in this repo.
|
System leave them out. No font file is in this repo.
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
| Language | Code | UI strings translated | Dialogs / tooltips / help | Script support | Native review | Status | Hours to full |
|
| Language | Code | UI strings translated | Dialogs / tooltips / help | Script support | Native review | Status | Hours to full |
|
||||||
|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|
|
||||||
| English | en | 1,899 / 1,899 (100%, source) | yes | Latin | yes (source) | full | 0 |
|
| English | en | 1,924 / 1,924 (100%, source) | yes | Latin | yes (source) | full | 0 |
|
||||||
| Simplified Chinese (Mandarin) | zh-Hans | 0 (0%) | no | CJK text, IME and font fallback work in documents; CJK UI faces load on demand (desktop) | no | none | 3–5 |
|
| Simplified Chinese (Mandarin) | zh-Hans | 1,924 / 1,924 (100%, 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 | 1,899 / 1,899 (100%, draft); es-419 overlays 87 Latin American wordings (draft) | yes | Latin | no | draft | 0–1 (after native review) |
|
| Spanish | es, es-419 | 1,924 / 1,924 (100%, draft); es-419 overlays 87 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; UI font fallback to check | no | none | 4–7 |
|
||||||
| Arabic | ar | 0 (0%) | no | Bidi, shaping and RTL names work; the UI does not mirror | no | none | 9–15 |
|
| Arabic | ar | 0 (0%) | no | Bidi, shaping and RTL names work; the UI does not mirror | no | none | 9–15 |
|
||||||
| French | fr | 1,899 / 1,899 (100%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
|
| French | fr | 1,899 / 1,924 (99%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
|
||||||
| Portuguese | pt-BR | 1,899 / 1,899 (100%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
|
| Portuguese | pt-BR | 1,899 / 1,924 (99%, draft) | yes | Latin | no | draft | 1–2 (after native review) |
|
||||||
| Indonesian | id | 0 (0%) | no | Latin | no | none | 3–5 |
|
| Indonesian | id | 0 (0%) | no | Latin | no | none | 3–5 |
|
||||||
| Japanese | ja | 1,899 / 1,899 (100%, draft) | yes | CJK text and IME in documents; UI text in an installed Japanese face (web: the `FIGCRAFT_EMBED_CJK` build) | no | draft | 1–2 (after native review) |
|
| Japanese | ja | 1,899 / 1,924 (99%, draft) | yes | CJK text and IME in documents; UI text in an installed Japanese face (web: the `FIGCRAFT_EMBED_CJK` build) | no | draft | 1–2 (after native review) |
|
||||||
| German | de | 1,899 / 1,899 (100%, draft) | yes | Latin; long words elide in narrow fields | no | draft | 1–2 (after native review) |
|
| German | de | 1,899 / 1,924 (99%, draft) | yes | Latin; long words elide in narrow fields | no | draft | 1–2 (after native review) |
|
||||||
| Korean | ko | 1,899 / 1,899 (100%, draft) | yes | Hangul text and IME in documents; UI text in an installed Korean face (web: the `FIGCRAFT_EMBED_CJK` build) | no | draft | 1–2 (after native review) |
|
| Korean | ko | 1,899 / 1,924 (99%, draft) | yes | Hangul text and IME in documents; UI text in an installed Korean face (web: the `FIGCRAFT_EMBED_CJK` build) | no | draft | 1–2 (after native review) |
|
||||||
| Vietnamese | vi | 0 (0%) | no | Latin with stacked diacritics | no | none | 3–5 |
|
| Vietnamese | vi | 0 (0%) | no | Latin with stacked diacritics | no | none | 3–5 |
|
||||||
|
|
||||||
Other languages: none shipped (Figma ships none beyond the eight above).
|
Other languages: none shipped (Figma ships none beyond the eight above).
|
||||||
|
|
@ -92,15 +97,18 @@ The layer is done. Per-language hours above are agent hours for a catalog of ~1,
|
||||||
(a machine draft, then a pass over the panels for overflow), plus each language's plural rule where
|
(a machine draft, then a pass over the panels for overflow), plus each language's plural rule where
|
||||||
it isn't one/other; every language also needs a native speaker's review (human). Left before full
|
it isn't one/other; every language also needs a native speaker's review (human). Left before full
|
||||||
parity besides the languages: localizing engine messages and number formatting (4–6 h);
|
parity besides the languages: localizing engine messages and number formatting (4–6 h);
|
||||||
right-to-left mirroring of the panels is in Arabic's hours. Total: **31–54 h**.
|
right-to-left mirroring of the panels is in Arabic's hours. Total: **29–51 h**.
|
||||||
|
|
||||||
Priority: native reviews of the six drafts and the es-419 overlay (every one of Figma's languages
|
Priority: native reviews of the seven drafts and the es-419 overlay (every one of Figma's languages
|
||||||
and variants now has one), then Simplified Chinese, whose UI fonts already work.
|
and variants has one, plus Simplified Chinese, which Figma doesn't ship), the 25 newer strings in the
|
||||||
|
French, German, Brazilian Portuguese, Japanese and Korean drafts, then Hindi, Indonesian,
|
||||||
|
Vietnamese and Arabic. Traditional Chinese is not planned (no catalog; its users see English).
|
||||||
|
|
||||||
## Revision history
|
## Revision history
|
||||||
|
|
||||||
| Date | Change | Summary |
|
| Date | Change | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Localization 36% → 39%, 31–54 h → 29–51 h |
|
||||||
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. A regional catalog's lookups (UI strings, command labels, menu wordings) go overlay → base language → English; the check fails on an overlay entry that reads as in its base. Localization 35% → 36%, 32–55 h → 31–54 h |
|
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. A regional catalog's lookups (UI strings, command labels, menu wordings) go overlay → base language → English; the check fails on an overlay entry that reads as in its base. Localization 35% → 36%, 32–55 h → 31–54 h |
|
||||||
| 2026-10-12 | minor | Japanese and Korean draft catalogs and CJK UI fonts (gap 5): `ja.toml` and `ko.toml` translate every UI string (1,351 keys; plurals give only `other`, and `plural_one` never picks "one" in either), command label (519) and menu wording (29), machine-drafted and marked as drafts awaiting native review; Preferences ▸ Language lists 日本語 and 한국어, System picks ja* and ko*. CJK UI text renders: 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 appends one installed face per script to egui's fallbacks (Windows Yu Gothic UI / Meiryo / Malgun Gothic / Microsoft YaHei, macOS Hiragino Sans / Apple SD Gothic Neo / PingFang SC, Linux Noto Sans CJK; the craft-fonts build's BIZ UDPGothic first), reading only that face's file and sharing its bytes with egui (English users load none: startup and memory unchanged); the web shows Japanese and Korean only when built with craft-fonts' Noto Sans CJK (`FIGCRAFT_EMBED_CJK`), else it keeps them out of the picker and System. Localization 30% → 35%, 40–65 h → 32–55 h |
|
| 2026-10-12 | minor | Japanese and Korean draft catalogs and CJK UI fonts (gap 5): `ja.toml` and `ko.toml` translate every UI string (1,351 keys; plurals give only `other`, and `plural_one` never picks "one" in either), command label (519) and menu wording (29), machine-drafted and marked as drafts awaiting native review; Preferences ▸ Language lists 日本語 and 한국어, System picks ja* and ko*. CJK UI text renders: 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 appends one installed face per script to egui's fallbacks (Windows Yu Gothic UI / Meiryo / Malgun Gothic / Microsoft YaHei, macOS Hiragino Sans / Apple SD Gothic Neo / PingFang SC, Linux Noto Sans CJK; the craft-fonts build's BIZ UDPGothic first), reading only that face's file and sharing its bytes with egui (English users load none: startup and memory unchanged); the web shows Japanese and Korean only when built with craft-fonts' Noto Sans CJK (`FIGCRAFT_EMBED_CJK`), else it keeps them out of the picker and System. Localization 30% → 35%, 40–65 h → 32–55 h |
|
||||||
| 2026-10-12 | minor | French, German and Brazilian Portuguese draft catalogs (gap 5): `fr.toml`, `de.toml` and `pt-BR.toml` translate every UI string (1,351 keys, 36 with plural forms), command label (519) and menu wording (29), machine-drafted and marked as drafts awaiting native review; they fall back to English per key, so new strings need only English and Spanish. French and Brazilian Portuguese count 0 as singular (`plural_one`); System picks the first OS language with a catalog (Windows' and macOS's preferred-language lists, `LANGUAGE` then the locale on Linux and BSD; pt-PT and pt read pt-BR); Preferences ▸ Language lists Français, Deutsch and Português (Brasil). Dropdowns, two-column labels and the home cards keep long translations on one line with an ellipsis and show them in full on hover. Localization 20% → 30%, 45–75 h → 40–65 h |
|
| 2026-10-12 | minor | French, German and Brazilian Portuguese draft catalogs (gap 5): `fr.toml`, `de.toml` and `pt-BR.toml` translate every UI string (1,351 keys, 36 with plural forms), command label (519) and menu wording (29), machine-drafted and marked as drafts awaiting native review; they fall back to English per key, so new strings need only English and Spanish. French and Brazilian Portuguese count 0 as singular (`plural_one`); System picks the first OS language with a catalog (Windows' and macOS's preferred-language lists, `LANGUAGE` then the locale on Linux and BSD; pt-PT and pt read pt-BR); Preferences ▸ Language lists Français, Deutsch and Português (Brasil). Dropdowns, two-column labels and the home cards keep long translations on one line with an ellipsis and show them in full on hover. Localization 20% → 30%, 45–75 h → 40–65 h |
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ on; `key` resolves keys the way the canvas does. Commands behind keys:
|
||||||
nested layers), Escape or a click drops it.
|
nested layers), Escape or a click drops it.
|
||||||
- `pref.reduceMotion {on?}`: reduced motion (the interface's own animations off; the system's
|
- `pref.reduceMotion {on?}`: reduced motion (the interface's own animations off; the system's
|
||||||
accessibility setting turns it on too). Prototype transitions still play.
|
accessibility setting turns it on too). Prototype transitions still play.
|
||||||
- `pref.language {language: system|en|es|es-419|fr|de|pt-BR|ja|ko|…}`: the app's interface language, applied live ("system":
|
- `pref.language {language: system|en|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…}`: the app's interface language, applied live ("system":
|
||||||
the OS or browser language; a language without a catalog shows English). Command ids, labels and
|
the OS or browser language; a language without a catalog shows English). Command ids, labels and
|
||||||
every result stay English.
|
every result stay English.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# FigCraft roadmap: milestones and what's next
|
# FigCraft roadmap: milestones and what's next
|
||||||
|
|
||||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; `.fig` import, best effort; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~79%) · **Target:** Figma Design (desktop app 126.10.7)
|
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; `.fig` import, best effort; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~79%) · **Target:** Figma Design (desktop app 126.10.7)
|
||||||
|
|
||||||
Forward-looking plan. Where FigCraft stands today: [`ROADMAP.md`](../ROADMAP.md) and
|
Forward-looking plan. Where FigCraft stands today: [`ROADMAP.md`](../ROADMAP.md) and
|
||||||
[`target-app-parity.md`](target-app-parity.md); the full ranked list: [`gaps.md`](gaps.md). Hours
|
[`target-app-parity.md`](target-app-parity.md); the full ranked list: [`gaps.md`](gaps.md). Hours
|
||||||
|
|
@ -15,8 +15,8 @@ are one Opus 5.5 agent; most milestones split across 4–6 agents on disjoint cr
|
||||||
exports, turn estimated percentages into measured ones: 15–30 h.
|
exports, turn estimated percentages into measured ones: 15–30 h.
|
||||||
3. **Tool-by-tool interaction pass** against the running Figma ([`ui-parity.md`](ui-parity.md)):
|
3. **Tool-by-tool interaction pass** against the running Figma ([`ui-parity.md`](ui-parity.md)):
|
||||||
40–60 h, parallel by tool.
|
40–60 h, parallel by tool.
|
||||||
4. **Languages** on the landed localization layer: native reviews of the es, fr, de, pt-BR, ja and
|
4. **Languages** on the landed localization layer: native reviews of the es, fr, de, pt-BR, ja,
|
||||||
ko drafts and the es-419 overlay (people), then the other key languages: 3–7 h per language
|
ko and zh-Hans drafts and the es-419 overlay (people), then the other key languages: 3–7 h per language
|
||||||
plus native review.
|
plus native review.
|
||||||
5. **Multiplayer M9**: accounts and teams on top of the live sessions and the standalone server (the WebSocket transport, hosting from the app, invite links that open the app, document sync, live cursors, selections, text carets, avatars and follow, per-user undo, reconnect with offline edits, collision-free ids for styles, variables, collections and comments, joining from the browser, and `figcraft-server` with persistence, publishing from the app and share links to edit or view landed), per [`collab-protocol.md`](collab-protocol.md) and [`server.md`](server.md): 45–90 h.
|
5. **Multiplayer M9**: accounts and teams on top of the live sessions and the standalone server (the WebSocket transport, hosting from the app, invite links that open the app, document sync, live cursors, selections, text carets, avatars and follow, per-user undo, reconnect with offline edits, collision-free ids for styles, variables, collections and comments, joining from the browser, and `figcraft-server` with persistence, publishing from the app and share links to edit or view landed), per [`collab-protocol.md`](collab-protocol.md) and [`server.md`](server.md): 45–90 h.
|
||||||
|
|
||||||
|
|
@ -73,6 +73,7 @@ blockers.
|
||||||
|
|
||||||
| Date | Change | Summary |
|
| Date | Change | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Focus item 4 adds the zh-Hans draft's review |
|
||||||
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Focus item 4 no longer lists Latin American Spanish |
|
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Focus item 4 no longer lists Latin American Spanish |
|
||||||
| 2026-10-12 | minor | M5: prototyping tail: controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes, `proto.evaluate`; M5's tail 3–6 h → 2–5 h |
|
| 2026-10-12 | minor | M5: prototyping tail: controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes, `proto.evaluate`; M5's tail 3–6 h → 2–5 h |
|
||||||
| 2026-10-12 | minor | M11: a plugin directory on `figcraft-server` (bundles published with the server key and checked in full, browsed and installed from Plugins ▸ Manage plugins after a permissions confirmation, hash-checked downloads, updates by version, `plugin.directory.*`, `figcraft-cli plugin publish`); 10–30 h → 8–26 h |
|
| 2026-10-12 | minor | M11: a plugin directory on `figcraft-server` (bundles published with the server key and checked in full, browsed and installed from Plugins ▸ Manage plugins after a permissions confirmation, hash-checked downloads, updates by version, `plugin.directory.*`, `figcraft-cli plugin publish`); 10–30 h → 8–26 h |
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# FigCraft parity with Figma
|
# FigCraft parity with Figma
|
||||||
|
|
||||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%; last major: full re-measure against Figma desktop 126.10.7) · **Target:** Figma Design (desktop app 126.10.7, macOS)
|
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%; last major: full re-measure against Figma desktop 126.10.7) · **Target:** Figma Design (desktop app 126.10.7, macOS)
|
||||||
|
|
||||||
The authoritative assessment of how close FigCraft is to Figma Design. [`ROADMAP.md`](../ROADMAP.md)
|
The authoritative assessment of how close FigCraft is to Figma Design. [`ROADMAP.md`](../ROADMAP.md)
|
||||||
summarizes it; [`gaps.md`](gaps.md) is the ranked work list; the measured menu checklist is
|
summarizes it; [`gaps.md`](gaps.md) is the ranked work list; the measured menu checklist is
|
||||||
|
|
@ -21,7 +21,7 @@ summarizes it; [`gaps.md`](gaps.md) is the ranked work list; the measured menu c
|
||||||
| Essentials user | **~57%** | estimated ([below](#essentials-user): core features 77.8% × discounts 0.92 × 0.92 × 0.88) |
|
| Essentials user | **~57%** | estimated ([below](#essentials-user): core features 77.8% × discounts 0.92 × 0.92 × 0.88) |
|
||||||
| Mainstream practitioner | **~49%** | estimated ([below](#mainstream-practitioner): weekly areas 72.6% × discounts 0.9 × 0.9 × 0.85) |
|
| Mainstream practitioner | **~49%** | estimated ([below](#mainstream-practitioner): weekly areas 72.6% × discounts 0.9 × 0.9 × 0.85) |
|
||||||
| Remaining to beta | **~155–280 h** one Opus 5.5 agent (~32–64 h wall-clock with 5 parallel agents) | estimated |
|
| Remaining to beta | **~155–280 h** one Opus 5.5 agent (~32–64 h wall-clock with 5 parallel agents) | estimated |
|
||||||
| Remaining to full parity | **~264–522 h** one agent (~83–145 h wall-clock with 5 parallel agents) | estimated |
|
| Remaining to full parity | **~262–519 h** one agent (~83–145 h wall-clock with 5 parallel agents) | estimated |
|
||||||
| Stage | **alpha**: the alpha gate passes ([roadmap](roadmap.md#alpha-gate)); multiplayer and checking the best-effort `.fig` import on real files are beta gaps (see [ROADMAP](../ROADMAP.md#stage)) | |
|
| Stage | **alpha**: the alpha gate passes ([roadmap](roadmap.md#alpha-gate)); multiplayer and checking the best-effort `.fig` import on real files are beta gaps (see [ROADMAP](../ROADMAP.md#stage)) | |
|
||||||
|
|
||||||
The previous figures (ROADMAP of 2026-10-10, "feature depth ~79%", "full parity 115–195 h") were
|
The previous figures (ROADMAP of 2026-10-10, "feature depth ~79%", "full parity 115–195 h") were
|
||||||
|
|
@ -35,7 +35,7 @@ tool; presence was being graded as behaviour in places.
|
||||||
|
|
||||||
| Audience | Ready % | Opus 5.5 agent wall-clock hours to ~95% | Work that dominates |
|
| Audience | Ready % | Opus 5.5 agent wall-clock hours to ~95% | Work that dominates |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Full target (ready for real work) | ~53% | ~270–525 h | multiplayer (42–84), localization (31–54), plugins (8–26), the interaction pass (40–60), GPU canvas (30–60), AI (30–60), the `.fig` import's tail (5–10) |
|
| Full target (ready for real work) | ~53% | ~268–522 h | multiplayer (42–84), localization (29–51), plugins (8–26), the interaction pass (40–60), GPU canvas (30–60), AI (30–60), the `.fig` import's tail (5–10) |
|
||||||
| Mainstream practitioner | ~49% | ~130–244 h | core editor depth incl. the `.fig` import's tail (49–98), the interaction pass (40–60), real-file corpus and platform QA (25–50), `.fig` export for handing work back (15–30) |
|
| Mainstream practitioner | ~49% | ~130–244 h | core editor depth incl. the `.fig` import's tail (49–98), the interaction pass (40–60), real-file corpus and platform QA (25–50), `.fig` export for handing work back (15–30) |
|
||||||
| Essentials user | ~57% | ~60–110 h | the `.fig` import's tail so the files people send open well (5–10), core tool polish (25–45), interaction pass on the core tools (15–25), stability QA (10–20), onboarding and starter templates (5–10) |
|
| Essentials user | ~57% | ~60–110 h | the `.fig` import's tail so the files people send open well (5–10), core tool polish (25–45), interaction pass on the core tools (15–25), stability QA (10–20), onboarding and starter templates (5–10) |
|
||||||
|
|
||||||
|
|
@ -129,11 +129,11 @@ Breadth: does it exist. Ready: depth, correctness and fidelity for real projects
|
||||||
| Stability | 8 | 55% | 20–40 | Never-crash lints, command guard with rollback, recovery copies, 963 tests incl. junk-parameter sweeps and a seeded retained-render walk; but two days old, one release (v0.2.0), no field reports, QA mostly on macOS | [gaps](gaps.md) |
|
| Stability | 8 | 55% | 20–40 | Never-crash lints, command guard with rollback, recovery copies, 963 tests incl. junk-parameter sweeps and a seeded retained-render walk; but two days old, one release (v0.2.0), no field reports, QA mostly on macOS | [gaps](gaps.md) |
|
||||||
| Performance | 6 | 60% | 30–60 | `cargo xtask bench`: 50k-layer page ~8 ms zoomed out, 10k-layer edits ~0.2 ms, 2,000-instance design system edits 4–64 ms; CPU (tiny-skia) canvas where Figma renders on the GPU; nothing timed against Figma on the same file | [hardware-parity](hardware-parity.md) |
|
| Performance | 6 | 60% | 30–60 | `cargo xtask bench`: 50k-layer page ~8 ms zoomed out, 10k-layer edits ~0.2 ms, 2,000-instance design system edits 4–64 ms; CPU (tiny-skia) canvas where Figma renders on the GPU; nothing timed against Figma on the same file | [hardware-parity](hardware-parity.md) |
|
||||||
| Hardware | 3 | 60% | 10–21 | Trackpad pinch and HiDPI yes; pen pressure on Windows and from browser styluses (none from macOS/Linux tablets); several windows (screen-reader tree in the main one only); no GPU canvas, no P3 on screen | [hardware-parity](hardware-parity.md) |
|
| Hardware | 3 | 60% | 10–21 | Trackpad pinch and HiDPI yes; pen pressure on Windows and from browser styluses (none from macOS/Linux tablets); several windows (screen-reader tree in the main one only); no GPU canvas, no P3 on screen | [hardware-parity](hardware-parity.md) |
|
||||||
| Localization | 3 | 36% | 31–54 | The layer, an English catalog and complete machine drafts of Spanish (with a Latin American overlay), French, German, Brazilian Portuguese, Japanese and Korean (none reviewed by a native speaker), CJK UI text in installed faces loaded on demand (web: only the craft-fonts CJK build); engine messages stay English | [localization-parity](localization-parity.md) |
|
| Localization | 3 | 39% | 29–51 | The layer, an English catalog and machine drafts of Spanish (with a Latin American overlay), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese (none reviewed by a native speaker; five lack the 25 newest strings), CJK UI text in installed faces loaded on demand (web: only the craft-fonts CJK build); engine messages stay English | [localization-parity](localization-parity.md) |
|
||||||
| Platforms | 3 | 75% | 10–20 | macOS, Windows (x64/x86/arm64), Linux, FreeBSD, web all packaged (v0.2.0); Figma also has iOS/Android apps; non-macOS QA thin | [hardware-parity](hardware-parity.md) |
|
| Platforms | 3 | 75% | 10–20 | macOS, Windows (x64/x86/arm64), Linux, FreeBSD, web all packaged (v0.2.0); Figma also has iOS/Android apps; non-macOS QA thin | [hardware-parity](hardware-parity.md) |
|
||||||
| Ecosystem/plugins | 3 | 38% | 8–26 | Own WASM plugins with a Rust SDK and three examples (#101), plugin panels and settings (ABI v2), widgets that take clicks (API 3), a self-hosted plugin directory on `figcraft-server`; no Figma plugin compatibility, no public catalogue or community files; the CLI, control channel and MCP server are ahead of Figma for agents | [gaps](gaps.md) |
|
| Ecosystem/plugins | 3 | 38% | 8–26 | Own WASM plugins with a Rust SDK and three examples (#101), plugin panels and settings (ABI v2), widgets that take clicks (API 3), a self-hosted plugin directory on `figcraft-server`; no Figma plugin compatibility, no public catalogue or community files; the CLI, control channel and MCP server are ahead of Figma for agents | [gaps](gaps.md) |
|
||||||
| AI features | 2 | 0% | 30–60 | None | [gaps](gaps.md) |
|
| AI features | 2 | 0% | 30–60 | None | [gaps](gaps.md) |
|
||||||
| **Ready for real work** | 100 | **~53%** | **~264–522** | | |
|
| **Ready for real work** | 100 | **~53%** | **~262–519** | | |
|
||||||
|
|
||||||
Dimension hours add up to the full-parity total without double counting: the feature-area rows
|
Dimension hours add up to the full-parity total without double counting: the feature-area rows
|
||||||
for import/export (14), multiplayer and team libraries (17, 19), plugins (18) and AI (21) are
|
for import/export (14), multiplayer and team libraries (17, 19), plugins (18) and AI (21) are
|
||||||
|
|
@ -619,6 +619,7 @@ Carried over from the ROADMAP's "Shipped so far" (2026-10-10). The test count th
|
||||||
|
|
||||||
| Date | Change | Summary |
|
| Date | Change | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Localization 36% → 39%, 31–54 h → 29–51 h; ready for real work unchanged (~53%); full parity ~267–527 h → ~265–524 h |
|
||||||
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Localization 35% → 36%, 32–55 h → 31–54 h; ready for real work unchanged (~53%); full parity ~268–528 h → ~267–527 h |
|
| 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Localization 35% → 36%, 32–55 h → 31–54 h; ready for real work unchanged (~53%); full parity ~268–528 h → ~267–527 h |
|
||||||
| 2026-10-12 | minor | Prototyping tail (gap 15, the part that doesn't need video): key/gamepad triggers take controller buttons (`Gamepad A` … `Gamepad Home`, the standard layout's 17 buttons, from a Controller menu beside the key field; the REST import maps controller triggers instead of skipping them); the player reads controllers on Windows (Windows.Gaming.Input through the `windows` crate the app already used; no new dependency) and in the browser (the Gamepad API), polling only while the prototype has such a trigger and the window has the focus, and the control channel presses buttons (`ui.input {gamepad}`); macOS, Linux and FreeBSD read no controllers yet. Widgets react to clicks in a presented prototype (the canvas's callback and sandbox limits, on the prototype's copy: the file and its history don't change, restarting starts over). `proto.evaluate` computes an expression where a layer's interaction would run (MCP); the mode lookup and set-variable-mode rules are cases in a behaviour suite (they already held). Prototyping breadth 90% → 92%, ready unchanged at 74%, 3–6 h → 2–5 h; area hours 120–260 → 119–259 (core 33–75 → 32–74), Features (depth) 29–68 h → 28–67 h; percentages unchanged at this rounding; full parity ~276–538 h → ~275–537 h |
|
| 2026-10-12 | minor | Prototyping tail (gap 15, the part that doesn't need video): key/gamepad triggers take controller buttons (`Gamepad A` … `Gamepad Home`, the standard layout's 17 buttons, from a Controller menu beside the key field; the REST import maps controller triggers instead of skipping them); the player reads controllers on Windows (Windows.Gaming.Input through the `windows` crate the app already used; no new dependency) and in the browser (the Gamepad API), polling only while the prototype has such a trigger and the window has the focus, and the control channel presses buttons (`ui.input {gamepad}`); macOS, Linux and FreeBSD read no controllers yet. Widgets react to clicks in a presented prototype (the canvas's callback and sandbox limits, on the prototype's copy: the file and its history don't change, restarting starts over). `proto.evaluate` computes an expression where a layer's interaction would run (MCP); the mode lookup and set-variable-mode rules are cases in a behaviour suite (they already held). Prototyping breadth 90% → 92%, ready unchanged at 74%, 3–6 h → 2–5 h; area hours 120–260 → 119–259 (core 33–75 → 32–74), Features (depth) 29–68 h → 28–67 h; percentages unchanged at this rounding; full parity ~276–538 h → ~275–537 h |
|
||||||
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (gap 6): the server keeps plugin bundles (manifest, module, up to 32 assets; an uncompressed length-prefixed format named by its SHA-256) published with the server key (`figcraft-cli plugin publish`, `plugin.directory.publish`), checked in full before they are kept (size cap, manifest and plugin API version, a version newer than the one kept, the module compiled but never run, plain asset names) and capped (`max_plugins`, `max_plugin_mb`, `max_plugins_mb`); anyone who reaches the server lists, searches and fetches them (hash-checked again on read, a shared download budget); the operator moderates with `figcraft-server plugins [remove]`. The app's Plugins ▸ Manage plugins gains a Browse tab: a server's plugins with Install / Update / Remove, an install confirmation that shows the permissions asked for, the source and the SHA-256, the download checked against the listed hash before anything is read, plugins kept per user in hash-named folders; `plugin.directory.list` / `install` (needs `confirm: true`) / `publish` / `unpublish` for scripts and MCP. Not in the browser build yet; no public catalogue, publisher accounts, signatures, ratings or background update checks. Plugins & widgets 55%/32% → 60%/35%, 10–30 h → 8–26 h; Ecosystem dimension 34% → 38%; weighted totals unchanged at this rounding; full parity ~281–548 h → ~279–544 h |
|
| 2026-10-12 | minor | Plugin directory on `figcraft-server` (gap 6): the server keeps plugin bundles (manifest, module, up to 32 assets; an uncompressed length-prefixed format named by its SHA-256) published with the server key (`figcraft-cli plugin publish`, `plugin.directory.publish`), checked in full before they are kept (size cap, manifest and plugin API version, a version newer than the one kept, the module compiled but never run, plain asset names) and capped (`max_plugins`, `max_plugin_mb`, `max_plugins_mb`); anyone who reaches the server lists, searches and fetches them (hash-checked again on read, a shared download budget); the operator moderates with `figcraft-server plugins [remove]`. The app's Plugins ▸ Manage plugins gains a Browse tab: a server's plugins with Install / Update / Remove, an install confirmation that shows the permissions asked for, the source and the SHA-256, the download checked against the listed hash before anything is read, plugins kept per user in hash-named folders; `plugin.directory.list` / `install` (needs `confirm: true`) / `publish` / `unpublish` for scripts and MCP. Not in the browser build yet; no public catalogue, publisher accounts, signatures, ratings or background update checks. Plugins & widgets 55%/32% → 60%/35%, 10–30 h → 8–26 h; Ecosystem dimension 34% → 38%; weighted totals unchanged at this rounding; full parity ~281–548 h → ~279–544 h |
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,14 @@ and the interface switches at once. System takes the first of the OS's preferred
|
||||||
a catalog (the browser's language on the web): a regional tag reads its language's catalog
|
a catalog (the browser's language on the web): a regional tag reads its language's catalog
|
||||||
(`fr-CA` → `fr`), a language with only a regional catalog reads that one (`pt-PT` → `pt-BR`), and
|
(`fr-CA` → `fr`), a language with only a regional catalog reads that one (`pt-PT` → `pt-BR`), and
|
||||||
Latin American and US Spanish (`es-MX`, `es-AR`, `es-CO`, `es-US`, `es-419`…) read `es-419`.
|
Latin American and US Spanish (`es-MX`, `es-AR`, `es-CO`, `es-US`, `es-419`…) read `es-419`.
|
||||||
|
Chinese (`zh`, `zh-CN`, `zh-SG`, `zh-Hans`) reads `zh-Hans`; Traditional Chinese (`zh-Hant`,
|
||||||
|
`zh-TW`, `zh-HK`, `zh-MO`) has no catalog and reads English (or the next OS language), not
|
||||||
|
Simplified.
|
||||||
`pref.language {language}` does the same from the CLI, MCP and the control channel.
|
`pref.language {language}` does the same from the CLI, MCP and the control channel.
|
||||||
|
|
||||||
The catalogs: English (`en`, the source), and Spanish (`es`, with the Latin American overlay
|
The catalogs: English (`en`, the source), and Spanish (`es`, with the Latin American overlay
|
||||||
`es-419`), French (`fr`), German (`de`), Brazilian Portuguese (`pt-BR`), Japanese (`ja`) and Korean
|
`es-419`), French (`fr`), German (`de`), Brazilian Portuguese (`pt-BR`), Japanese (`ja`), Korean (`ko`)
|
||||||
(`ko`), machine-drafted and waiting for native speakers' review.
|
and Simplified Chinese (`zh-Hans`), machine-drafted and waiting for native speakers' review.
|
||||||
|
|
||||||
## The catalogs
|
## The catalogs
|
||||||
|
|
||||||
|
|
@ -38,12 +41,15 @@ Portuguese uses Brazilian words (arquivo, salvar, tela, você), not European one
|
||||||
Spanish overrides Spain's wording where it differs (agregar, presionar, configuración for settings,
|
Spanish overrides Spain's wording where it differs (agregar, presionar, configuración for settings,
|
||||||
Enter for the key, control for a gamepad, resorte) and keeps Spanish's tú. Japanese is
|
Enter for the key, control for a gamepad, resorte) and keeps Spanish's tú. Japanese is
|
||||||
concise, without です/ます endings, with full-width ()「」:、。 where Japanese UI text has them;
|
concise, without です/ます endings, with full-width ()「」:、。 where Japanese UI text has them;
|
||||||
Korean labels end without 합니다 (instructions in -세요). Neither has plural forms.
|
Korean labels end without 합니다 (instructions in -세요). Simplified Chinese is concise, with
|
||||||
|
full-width ():,、。“” in Chinese text and a space between Chinese and Latin words or numbers.
|
||||||
|
None of the three has plural forms.
|
||||||
|
|
||||||
CJK text needs a CJK font, which egui and Inter lack: the desktop app adds an installed one when
|
CJK text needs a CJK font, which egui and Inter lack: the desktop app adds an installed one when
|
||||||
something on screen needs it (see `crates/ui-egui/src/cjk.rs`). The web build has no system fonts,
|
something on screen needs it (see `crates/ui-egui/src/cjk.rs`). The web build has no system fonts,
|
||||||
so Japanese and Korean appear there only in a build with craft-fonts and `FIGCRAFT_EMBED_CJK=1`
|
so Japanese, Korean and Chinese appear there only in a build with craft-fonts and
|
||||||
(Noto Sans CJK, +16 MB); otherwise they are left out of the picker. A new CJK or other non-Latin
|
`FIGCRAFT_EMBED_CJK=1` (Noto Sans CJK SC, +16 MB); otherwise they are left out of the picker. A
|
||||||
|
Chinese UI puts its Chinese face first so Han takes Simplified Chinese shapes. A new CJK or other non-Latin
|
||||||
language also needs its script in `cjk::language`.
|
language also needs its script in `cjk::language`.
|
||||||
|
|
||||||
## Adding a language
|
## Adding a language
|
||||||
|
|
@ -59,9 +65,8 @@ language also needs its script in `cjk::language`.
|
||||||
4. To require full coverage, add the code to `COMPLETE` in
|
4. To require full coverage, add the code to `COMPLETE` in
|
||||||
[`crates/ui-egui/src/i18n_tests.rs`](../crates/ui-egui/src/i18n_tests.rs). Every later PR that
|
[`crates/ui-egui/src/i18n_tests.rs`](../crates/ui-egui/src/i18n_tests.rs). Every later PR that
|
||||||
adds a string then has to translate it into each complete language, so only Spanish is there:
|
adds a string then has to translate it into each complete language, so only Spanish is there:
|
||||||
French, German, Brazilian Portuguese, Japanese and Korean are complete today but registered as
|
French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese are registered as
|
||||||
drafts, and a
|
drafts, and a string added later shows English in them until it is translated. To list what a draft lacks,
|
||||||
string added later shows English in them until it is translated. To list what a draft lacks,
|
|
||||||
add its code to `COMPLETE` locally and run the check.
|
add its code to `COMPLETE` locally and run the check.
|
||||||
5. Run the check and look at the panels in the app (longer words must fit the right panel):
|
5. Run the check and look at the panels in the app (longer words must fit the right panel):
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue