mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-31 13:56:54 -06:00
Merge main into #1657
This commit is contained in:
@@ -244,7 +244,7 @@ The desktop app also offers a loopback-only control channel (`figcraft --control
|
||||
- **Its own renderer:** tiny-skia on every core for the canvas and exports, with layer blending, effects and masks; booleans from linesweeper, geometry from kurbo, text from skrifa and HarfRust.
|
||||
- **Runs in the browser:** the whole engine and UI compile to WebAssembly.
|
||||
- **Accessible:** screen readers get names, roles, values and states for the tools, layer rows, fields, dropdowns, tabs and menus (AccessKit on desktop), F6 moves the keyboard between the toolbar, layers, canvas and properties (Tab within them, Escape back to the canvas), the system's reduce-motion setting is honoured, and panel text meets WCAG AA contrast in both themes.
|
||||
- **Languages:** English, Russian and Turkish (reviewed by native speakers), and Spanish, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese, Czech, Indonesian, Georgian and Vietnamese drafts, picked in Preferences or taken from the system or browser and switched live; adding a language is one catalog file ([`docs/translating.md`](docs/translating.md)).
|
||||
- **Languages:** English, Russian and Turkish (reviewed by native speakers), and Spanish, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese, Czech, Indonesian, Georgian, Vietnamese and Bosnian drafts, picked in Preferences or taken from the system or browser and switched live; adding a language is one catalog file ([`docs/translating.md`](docs/translating.md)).
|
||||
- **Plugins:** sandboxed WebAssembly plugins (a metered, pure-Rust interpreter with a memory cap and no file or network access, in the browser too) that act through the same commands, each run one undo step, within the permissions their manifest asks for. A Rust SDK and four example plugins are in [`plugins/`](plugins); see [`docs/plugins.md`](docs/plugins.md).
|
||||
- **Live sessions (preview):** share the open file from the app (Share ▸ Start sharing: online through a server, or on your local network), give the others the short join code it shows (or the invite link), and edit it together: everyone's changes sync live, with cursors, selections, avatars and follow, and undo takes back only your own changes. Others join from the desktop app (invite links open it) or from the browser build. See [`docs/collab-protocol.md`](docs/collab-protocol.md).
|
||||
- **A server of your own (preview):** `figcraft-server` keeps a team's files, publishes them from the app (File ▸ Publish to server…, or Share ▸ Online with your server in Preferences) and hands out share links and join codes that let people edit or only view; files survive restarts and apps reconnect where they were. No accounts yet: links are the access. Releases ship it for Linux, and the desktop app joins over `wss://`; [hosting it on a rented Linux server](docs/server.md#deploy-on-a-linux-server) takes a systemd unit and Caddy or nginx (or Docker). See [`docs/server.md`](docs/server.md).
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
@@ -442,7 +442,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
spec!(
|
||||
"pref.language",
|
||||
"Interface language",
|
||||
"{language: system|en|cs|ru|es|es-419|fr|de|pt-BR|ja|ka|ko|vi|zh-Hans|…; else English}",
|
||||
"{language: system|en|bs|cs|ru|es|es-419|fr|de|pt-BR|ja|ka|ko|vi|zh-Hans|…; else English}",
|
||||
false,
|
||||
|s, p| {
|
||||
let lang = req_string("pref.language", p, "language")?;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -179,7 +179,7 @@ mod tests {
|
||||
assert_eq!(language("zh-Hans"), HAN);
|
||||
assert_eq!(language("bn"), BENGALI);
|
||||
assert_eq!(language("fr"), 0);
|
||||
assert!(["en", "es", "es-419", "fr", "de", "pt-BR", "tr", "id", "vi"].iter().all(|c| can_show(c)));
|
||||
assert!(["en", "es", "es-419", "fr", "de", "pt-BR", "tr", "id", "vi", "bs"].iter().all(|c| can_show(c)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -179,6 +179,9 @@ fn plural(code: &str, n: usize) -> Plural {
|
||||
"ru" if n % 10 == 1 && n % 100 != 11 => Plural::One,
|
||||
"ru" | "pl" if (2..=4).contains(&(n % 10)) && !(12..=14).contains(&(n % 100)) => Plural::Few,
|
||||
"ru" | "pl" => Plural::Many,
|
||||
"bs" if n % 10 == 1 && n % 100 != 11 => Plural::One,
|
||||
"bs" if (2..=4).contains(&(n % 10)) && !(12..=14).contains(&(n % 100)) => Plural::Few,
|
||||
"bs" => Plural::Other,
|
||||
"ja" | "ko" | "th" | "zh-Hans" | "vi" => Plural::Other,
|
||||
"fr" | "pt-BR" if n <= 1 => Plural::One,
|
||||
_ if n == 1 => Plural::One,
|
||||
@@ -187,7 +190,8 @@ fn plural(code: &str, n: usize) -> Plural {
|
||||
}
|
||||
|
||||
// CLDR's million-count "many" in French, Spanish and Portuguese still reads their existing
|
||||
// "other" wording. Czech supplies separate few forms; Russian and Polish supply few/many forms.
|
||||
// "other" wording. Czech supplies separate few forms; Russian and Polish supply few/many forms;
|
||||
// Bosnian takes Russian's one and few and reads "other" for the rest (CLDR has no "many" there).
|
||||
|
||||
/// Whether count `n` takes the "one" form: 0 and 1 in French and Brazilian Portuguese, never in
|
||||
/// Japanese, Korean, Thai, Chinese and Vietnamese (no plural forms: always "other"), 1 elsewhere (CLDR's integer rules).
|
||||
|
||||
@@ -431,6 +431,36 @@ fn swedish_is_picked_for_swedish_locales() {
|
||||
assert!(!plural_one(sv, 0) && plural_one(sv, 1) && !plural_one(sv, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bosnian_tags_and_integer_plurals() {
|
||||
let code = |pref: &str, system: Option<&str>| LANGS[resolve(pref, system)].code;
|
||||
for tag in ["bs", "bs-BA", "bs-Latn-BA", "bs_BA.UTF-8"] {
|
||||
assert_eq!(code("system", Some(tag)), "bs", "{tag}");
|
||||
}
|
||||
let bs = LANGS.iter().position(|l| l.code == "bs").unwrap();
|
||||
assert_eq!(LANGS[bs].name, "Bosanski");
|
||||
assert!(LANGS[bs].draft);
|
||||
for n in [1, 21, 31, 101, 1001] {
|
||||
assert_eq!(plural("bs", n), Plural::One, "{n}");
|
||||
}
|
||||
for n in [2, 3, 4, 22, 24, 102] {
|
||||
assert_eq!(plural("bs", n), Plural::Few, "{n}");
|
||||
}
|
||||
for n in [0, 5, 11, 12, 14, 20, 111, 112] {
|
||||
assert_eq!(plural("bs", n), Plural::Other, "{n}");
|
||||
}
|
||||
// "one" covers 21 and 31 too, so every form keeps the count.
|
||||
for (key, text) in LANGS[bs].ui {
|
||||
if let Text::Plural(Forms { one, few, other, .. }) = text {
|
||||
for form in [*one, *few, Some(*other)].into_iter().flatten() {
|
||||
assert!(form.contains("{n}"), "{key}: {form}");
|
||||
}
|
||||
}
|
||||
}
|
||||
let layers = [1, 3, 5, 21].map(|n| fill(counted(bs, "{n} layers", n), &[("n", &n)]));
|
||||
assert_eq!(layers, ["1 sloj", "3 sloja", "5 slojeva", "21 sloj"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latin_american_spanish_overlays_spanish() {
|
||||
let code = |pref: &str, system: Option<&str>| LANGS[resolve(pref, system)].code;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft; Arabic draft catalog, count-neutral plurals and web font gate; Thai draft catalog and Thai UI fonts; a missing translation fails no check: `cargo xtask i18n-report` lists them; Vietnamese draft catalog; string counts re-measured: 2,352 entries; Russian and Turkish catalogs reviewed by native speakers, Russian one/few/many plurals; Czech and Indonesian drafts; the Japanese draft completed; counts re-measured; Windows UI language from the first preferred language; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog; macOS finds its downloadable CJK system fonts (#406); string counts re-measured: 2,272 entries, the drafts at 95–99%; Swedish command labels and menu wordings; Russian, Swedish, Turkish, Czech and Indonesian listed; a duplicated paragraph removed) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (Bosnian draft catalog, one/few/other plurals; German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft; Arabic draft catalog, count-neutral plurals and web font gate; Thai draft catalog and Thai UI fonts; a missing translation fails no check: `cargo xtask i18n-report` lists them; Vietnamese draft catalog; string counts re-measured: 2,352 entries; Russian and Turkish catalogs reviewed by native speakers, Russian one/few/many plurals; Czech and Indonesian drafts; the Japanese draft completed; counts re-measured; Windows UI language from the first preferred language; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog; macOS finds its downloadable CJK system fonts (#406); string counts re-measured: 2,272 entries, the drafts at 95–99%; Swedish command labels and menu wordings; Russian, Swedish, Turkish, Czech and Indonesian listed; a duplicated paragraph removed) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
|
||||
FigCraft's interface has a localization layer with an English catalog, a Russian catalog reviewed
|
||||
by a native speaker, and Spanish (with a Latin American overlay), French, German, Brazilian
|
||||
Portuguese, Japanese, Korean, Simplified Chinese, Indonesian, Vietnamese and Czech drafts (machine-drafted,
|
||||
Portuguese, Japanese, Korean, Simplified Chinese, Indonesian, Vietnamese, Bosnian and Czech drafts (machine-drafted,
|
||||
waiting for native speakers' review), plus Swedish and Turkish catalogs. This page lists the twelve key
|
||||
languages of the craftrules standard, then the other languages Figma ships. How to add a
|
||||
language: [`translating.md`](translating.md).
|
||||
@@ -31,7 +31,7 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||
Spanish catalog's command and menu tables). Command ids, the CLI, MCP and the control
|
||||
channel stay English. Accessibility names come from the same strings.
|
||||
- **Language preference:** Preferences ▸ Language (System, then each catalog by its own name:
|
||||
English, Čeština, Deutsch, Español, Español (Latinoamérica), Français, Bahasa Indonesia, 日本語,
|
||||
English, Bosanski, Čeština, Deutsch, Español, Español (Latinoamérica), Français, Bahasa Indonesia, 日本語,
|
||||
한국어, Português (Brasil), Русский, Svenska, Türkçe, Tiếng Việt, 简体中文), applied at once without a
|
||||
restart; System takes the first of the OS's preferred languages that has a catalog (on Windows
|
||||
only the first preferred language counts, since that list also holds the languages added for a
|
||||
@@ -53,7 +53,7 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v
|
||||
2026-10-12; by 2026-10-17 the strings added since left them at 95–99% (table below).
|
||||
- **Not yet localized:** messages the engine writes (errors, a few toasts, Dev Mode property values
|
||||
such as "inside"), number formatting (decimal points stay `.`), and decimal plural rules
|
||||
(`trn` takes integers: Czech uses one/few/other and Russian one/few/many; French and Brazilian
|
||||
(`trn` takes integers: Czech and Bosnian use one/few/other and Russian one/few/many; French and Brazilian
|
||||
Portuguese take the singular for 0 as well; Japanese, Korean, Chinese and Vietnamese have one form;
|
||||
CLDR's "many" for millions in French, Spanish and Portuguese reads as the plural).
|
||||
- **Script support in documents:** Unicode bidi with Arabic shaping and mirrored brackets in text
|
||||
@@ -105,7 +105,9 @@ Other languages (measured 2026-10-22): Russian (`ru`) 2,352 / 2,352 (100%), revi
|
||||
by VanTan, one/few/many integer plurals; Swedish (`sv`) 2,314 / 2,352 (98.4%), its command labels and menu
|
||||
wordings added 2026-10-17 (#599), native review welcome; Turkish (`tr`) 2,291 / 2,352 (97.4%), whose header
|
||||
says it was machine-drafted and reviewed by a native speaker; Czech (`cs`) 2,292 / 2,352 (97.4%), a draft
|
||||
with one/few/other plurals. Figma ships none beyond the eight above.
|
||||
with one/few/other plurals; Bosnian (`bs`, Bosanski, added 2026-10-30) 2,561 / 2,561 (100%: 1,901 UI
|
||||
strings, 629 command labels, 31 menu wordings), a machine draft awaiting native review, with
|
||||
one/few/other plurals (21 takes one). Figma ships none beyond the eight above.
|
||||
|
||||
The layer is done. Per-language hours above are agent hours for a catalog of ~1,800 entries
|
||||
(a machine draft, then a pass over the panels for overflow), plus each language's plural rule where
|
||||
@@ -123,6 +125,7 @@ Hindi and Arabic. Traditional Chinese is not planned (no catalog; its users see
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | Bosnian (`bs`, Bosanski) draft catalog: every UI string (1,901), command label (629) and menu wording (31), machine-drafted in Latin script with ijekavian forms and marked as a draft awaiting native review. Bosnian plurals: one for 1, 21, 31… (not 11), few for 2–4, 22–24… (not 12–14), other for the rest. Grade and hours unchanged (Bosnian is not one of the twelve key languages) |
|
||||
| 2026-10-29 | minor | German terminology corrections (M10): gradient stops, OpenType stylistic sets and the Home-screen preference; partial review, catalog remains a draft. Nine existing translations corrected; keys, placeholders, coverage, grade and estimates unchanged |
|
||||
| 2026-10-28 | minor | Arabic draft: 1,857 UI entries, 620 command labels and 31 menu wordings, with count-neutral plurals. AI-assisted, native review pending. Desktop uses the existing Arabic fallback and bidi; web requires an embedded Arabic face. No panel mirroring or parity-grade increase claimed |
|
||||
| 2026-10-24 | minor | Thai (`th`) draft catalog: `th.toml` translates every UI string (1,791), 589 command labels and the 31 menu wordings, machine-drafted and marked as a draft awaiting native review (9 newer command labels read English, as in the other drafts). The desktop UI now adds an installed Thai face (Noto Sans Thai, Leelawadee UI, Tahoma, Sukhumvit Set, Thonburi, Ayuthaya, Sarabun, Loma, Garuda or Waree) when a layer or page name or the UI language needs it, so Thai names no longer show as tofu squares. No plural forms for Thai. Grade and hours unchanged |
|
||||
|
||||
+1
-1
@@ -656,7 +656,7 @@ on; `key` resolves keys the way the canvas does. Commands behind keys:
|
||||
- `pref.startOnHome {on?}`: the desktop app starts on the Home screen instead of in a new file
|
||||
(off by default; files named on the command line still open, and `--control` starts in a new
|
||||
file anyway, so control callers have a document).
|
||||
- `pref.language {language: system|en|cs|ru|es|es-419|fr|de|pt-BR|ja|ka|ko|sv|vi|zh-Hans|…}`: the app's interface language, applied live ("system":
|
||||
- `pref.language {language: system|en|bs|cs|ru|es|es-419|fr|de|pt-BR|ja|ka|ko|sv|vi|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
|
||||
every result stay English.
|
||||
|
||||
|
||||
+10
-2
@@ -17,7 +17,7 @@ Simplified.
|
||||
The catalogs: English (`en`, the source), and Spanish (`es`, with the Latin American overlay
|
||||
`es-419`), French (`fr`), German (`de`), Dutch (`nl`), Brazilian Portuguese (`pt-BR`), Japanese
|
||||
(`ja`), Korean (`ko`), Simplified Chinese (`zh-Hans`), Czech (`cs`), Indonesian (`id`), Finnish
|
||||
(`fi`), Bengali (`bn`), Italian (`it`), Vietnamese (`vi`) and Thai (`th`), machine-drafted and waiting for native speakers' review. Polish (`pl`,
|
||||
(`fi`), Bengali (`bn`), Italian (`it`), Vietnamese (`vi`), Thai (`th`) and Bosnian (`bs`), machine-drafted and waiting for native speakers' review. Polish (`pl`,
|
||||
Polski) was reviewed and approved by Lucas (@lukasz-sklad). Georgian (`ka`) was written with AI
|
||||
assistance and reviewed by a native speaker, and stays a draft until a maintainer changes its status.
|
||||
Russian (`ru`, Русский) was reviewed and approved by VanTan; its catalog keeps Frame, Dev Mode and
|
||||
@@ -67,7 +67,12 @@ plural entries give only `other`. Thai is concise, with bare imperatives for but
|
||||
menu items (บันทึก, ลบ), no ครับ/ค่ะ endings, no spaces between Thai words (a space only separates
|
||||
clauses, and Thai from Latin words or numbers), and design terms transliterated as Thai designers say
|
||||
them (เฟรม, เลเยอร์, คอมโพเนนต์, อินสแตนซ์, ออโต้เลย์เอาต์). It has no plural forms: plural entries give
|
||||
only `other`, and dates read `{day} {month} {year}` with Thai month abbreviations.
|
||||
only `other`, and dates read `{day} {month} {year}` with Thai month abbreviations. Bosnian is
|
||||
written in Latin script with ijekavian forms and Bosnian words (historija, hiljada, sačuvati,
|
||||
sedmica), uses bare imperatives for buttons and menu items (Sačuvaj, Izbriši) and the formal Vi for
|
||||
instructions (Odaberite, Kliknite), „ “ quotes, and consistent design terms (okvir, sloj,
|
||||
komponenta, instanca, varijanta, automatski raspored, ograničenja, ispuna, obrub, efekat, varijabla,
|
||||
stil, način for a variable mode, dodatak for a plugin).
|
||||
|
||||
CJK and Thai text need their own fonts, which egui and Inter lack: the desktop app adds an installed
|
||||
one when something on screen needs it, so layer and page names in those scripts show whatever the
|
||||
@@ -116,6 +121,9 @@ right-to-left interface. Native review and representative in-app checks remain r
|
||||
three forms. Czech's CLDR `many` applies only to decimals; `trn` takes integer counts.
|
||||
Polish uses `one` for 1 only, `few` like Russian (2–4, 22–24… except 12–14) and `many` for
|
||||
the other integers, including 0 and 21 (e.g. `{n} warstwa`, `{n} warstwy`, `{n} warstw`).
|
||||
Bosnian uses `one` for 1, 21, 31… except 11, `few` for 2–4, 22–24… except 12–14, and `other`
|
||||
for the rest, including 0 (e.g. `{n} sloj`, `{n} sloja`, `{n} slojeva`); since `one` covers 21,
|
||||
keep `{n}` in every form.
|
||||
4. A string added later shows English in every language until someone translates it; a missing
|
||||
translation never fails a check or a build. `cargo xtask i18n-report` lists what each language
|
||||
lacks (UI strings, command labels, menu wordings), and `cargo xtask ci` prints the same list.
|
||||
|
||||
Reference in New Issue
Block a user