mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-02 06:46:50 -06:00
M7: localization layer (#128)
UI strings come from per-language TOML catalogs compiled in (English text as the key and fallback, named placeholders, one/other plurals, context suffixes); command labels are translated at display time while ids, CLI, MCP and control output stay English; accessibility names follow. Preferences > Language (System / English / Español, live; the system language from the OS or the browser), pref.language for scripts. A key check test guards the catalogs. The Spanish catalog is a machine draft awaiting native review; docs/translating.md explains adding a language.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# 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
|
||||
(System follows the OS, or the browser on the web) and the interface switches at once. `pref.language
|
||||
{language}` does the same from the CLI, MCP and the control channel.
|
||||
|
||||
## 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 key a catalog lacks shows its English text, so a partial catalog is safe to ship.
|
||||
|
||||
## 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`). `trn` picks `one` for a count of 1; a language with other rules (French: 0 and 1)
|
||||
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).
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
Reference in New Issue
Block a user