- `crates/ui-egui/locales/es-419.toml`: Latin American Spanish as an overlay on `es.toml`. It lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage: agregar for anadir, presionar for pulsar, configuracion for settings, Enter for the Intro key, control for a gamepad, resorte for a spring. Everything else reads `es.toml`, then English. Same tu register as `es.toml`. Marked as a draft awaiting native review (not in `COMPLETE`). - Fallback chain: a regional catalog whose language has a catalog overlays it. `build.rs` bakes a `parent` index into each `Lang` (es-419 -> es; others -> English), and `i18n::chain` walks overlay -> base -> English for UI strings, command labels and menu wordings. Catalogs are now sorted by code, so the picker lists Espanol before Espanol (Latinoamerica). - System detection: es-419, es-MX, es-AR, es-CO, es-US and the other Latin American tags (also `es_CO.UTF-8`, `es-Latn-MX`) resolve to es-419; es, es-ES and other regions stay es. - Preferences > Language shows "Espanol (Latinoamerica)" (in Spanish spelling) with the draft note. - `pref.language` params doc and `docs/mcp.md` list es-419. - Docs: localization-parity (overlay section, language preference, Spanish row, total), translating (overlays, detection, wording), target-app-parity, ROADMAP, gaps (gap 5), roadmap (focus item 4). Localization 35% -> 36%, 32-55 h -> 31-54 h; ready for real work unchanged (~53%); full parity ~268-528 h -> ~267-527 h.
5.9 KiB
Translating FigCraft
FigCraft's interface strings live in one TOML catalog per language in
crates/ui-egui/locales/. The build bakes every catalog into the
binary (desktop and web); nothing is read at run time. Preferences ▸ Language picks the language
and the interface switches at once. System takes the first of the OS's preferred languages that has
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
Latin American and US Spanish (es-MX, es-AR, es-CO, es-US, es-419…) read es-419.
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
es-419), French (fr), German (de), Brazilian Portuguese (pt-BR), Japanese (ja) and Korean
(ko), machine-drafted and waiting for native speakers' review.
The catalogs
en.tomllists every key. A key is the English text as the code writes it, so the code stays readable:tr("Fill"),trf("Delete {name}?", &[("name", &name)]),trn("{n} layers", count, &[]). Values equal their keys except plurals, which give both forms:"{n} layers" = { one = "{n} layer", other = "{n} layers" }.es.tomland other languages translate the same keys under[ui], plus command labels by command id under[commands](the Actions palette, the shortcuts panel and the menus) and, under[menu], the few menu items the main menu words differently from the command (the Select matching submenu's "Fill" for "Select same fill"). Command ids, the CLI, MCP and control-channel output stay English.[meta] nameis the language's own name (the picker shows it untranslated);[meta] statusmarks a draft (the Preferences window says so).- A regional overlay (
es-419.toml: a code with a region whose language has a catalog) lists only the entries worded differently there; everything else reads the language's catalog, then English. The check fails on an overlay entry that reads as in the catalog under it.
A key a catalog lacks shows its English text, so a partial catalog is safe to ship.
Typography follows each language: French puts a no-break space (\u00A0 in the TOML) before
: ; ? !, inside « » and before %; German uses „ “ and formal address (Sie); Brazilian
Portuguese uses Brazilian words (arquivo, salvar, tela, você), not European ones. Latin American
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
concise, without です/ます endings, with full-width ()「」:、。 where Japanese UI text has them;
Korean labels end without 합니다 (instructions in -세요). Neither has plural forms.
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,
so Japanese and Korean appear there only in a build with craft-fonts and FIGCRAFT_EMBED_CJK=1
(Noto Sans CJK, +16 MB); otherwise they are left out of the picker. A new CJK or other non-Latin
language also needs its script in cjk::language.
Adding a language
-
Copy
crates/ui-egui/locales/es.tomlto<code>.toml(a BCP 47 code:fr,pt-BR,ja) and set[meta] nameandstatus = "draft — needs native review". -
Translate every value; keep the keys. Keep
{placeholders}exactly, "…" where English has it, brand names and keyboard glyphs. English labels of commands:figcraft-cli commands. -
Plurals: give
oneonly if the language has a singular form (Japanese, Korean and Chinese give onlyother, andplural_onereturns false for them).trnpicksonefor a count of 1, and also for 0 in French and Brazilian Portuguese (keep{n}in theironeforms); a language with other rules needs its rule inplural_oneincrates/ui-egui/src/i18n.rs. -
To require full coverage, add the code to
COMPLETEincrates/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: French, German, Brazilian Portuguese, Japanese and Korean are complete today but registered as drafts, and a string added later shows English in them until it is translated. To list what a draft lacks, add its code toCOMPLETElocally and run the check. -
Run the check and look at the panels in the app (longer words must fit the right panel):
cargo test -p figcraft-ui-egui i18nDropdowns, the right panel's two-column labels and the home cards cut a label that doesn't fit with "…" and show it in full on hover; elsewhere, look for clipped or wrapped text.
The check fails on keys the code uses that en.toml lacks (it prints the lines to paste), keys no
code uses, unknown command ids, placeholders that don't match English, and, for complete languages,
anything untranslated. Machine-drafted catalogs stay marked as drafts until a native speaker has
reviewed them.
Adding strings in code
Wrap every string a user sees or a screen reader hears: tr for plain text, trf for {named}
placeholders, trn for counts, tr_noop for literals in const tables that are looked up later
with tr(label). The key must be a string literal as the first argument (the check reads the
source). Don't wrap ids, command ids, JSON, log messages, file extensions or document content. Then
add the key to en.toml and to each complete language (Spanish); the drafts may follow later.
Labels from the engine and document crates (tool names, blend modes, easing curves…) are looked up
at display with tr(x.label()); they are listed in dynamic_labels in i18n_tests.rs.