mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-25 10:56:48 -06:00
- `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.
93 lines
6.4 KiB
Markdown
93 lines
6.4 KiB
Markdown
# Translating FigCraft
|
|
|
|
FigCraft's interface strings live in one TOML catalog per language in
|
|
[`crates/ui-egui/locales/`](../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`.
|
|
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.
|
|
|
|
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`), Korean (`ko`)
|
|
and Simplified Chinese (`zh-Hans`), machine-drafted and waiting for native speakers' review.
|
|
|
|
## The catalogs
|
|
|
|
- **`en.toml`** lists 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.toml`** and 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] name` is the language's own name (the picker shows it untranslated); `[meta] status` marks
|
|
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 -세요). 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
|
|
something on screen needs it (see `crates/ui-egui/src/cjk.rs`). The web build has no system fonts,
|
|
so Japanese, Korean and Chinese appear there only in a build with craft-fonts and
|
|
`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`.
|
|
|
|
## Adding a language
|
|
|
|
1. Copy `crates/ui-egui/locales/es.toml` to `<code>.toml` (a BCP 47 code: `fr`, `pt-BR`, `ja`) and
|
|
set `[meta] name` and `status = "draft — needs native review"`.
|
|
2. 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`.
|
|
3. Plurals: give `one` only if the language has a singular form (Japanese, Korean and Chinese give
|
|
only `other`, and `plural_one` returns false for them). `trn` picks `one` for a count of 1, and
|
|
also for 0 in French and Brazilian Portuguese (keep `{n}` in their `one` forms); a language with other rules needs its rule in
|
|
`plural_one` in [`crates/ui-egui/src/i18n.rs`](../crates/ui-egui/src/i18n.rs).
|
|
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
|
|
adds a string then has to translate it into each complete language, so only Spanish is there:
|
|
French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese are 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 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):
|
|
|
|
cargo test -p figcraft-ui-egui i18n
|
|
|
|
Dropdowns, 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`.
|