mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-11-01 07:36:42 +01:00
docs(i18n): describe per-process languages, previews and override limits
docs/I18N.md still said the configurator was untranslated, that the
language was set once at startup, and that a translation could reorder
the {} placeholders; the guard rules and the steps for adding a language
were incomplete. It now covers which processes draw translated text and
when each picks up a change, the configurator preview, how auto resolves
in a systemd user service, the fixed placeholder order, the guard's call
forms and its derived-label audit, the limits on local catalog overrides,
search under translation, and every place a new language has to be added.
CONFIG.md, config.example.toml and the catalogs README state the same
timing, override and placeholder rules.
This commit is contained in:
1 parent
89ffe56e81
commit
98b6f54c15
4 files changed
+126
-33
No files matched your search
+5
-2
@@ -415,10 +415,13 @@ theme = "auto"
|
||||
# currently behaves like "off" (full motion).
|
||||
reduced_motion = "auto"
|
||||
|
||||
# UI language for overlay chrome: help, toolbars, menus, toasts, and settings.
|
||||
# UI language: overlay chrome (help, toolbars, menus, toasts, settings), the
|
||||
# About window, background mode's tray and notices, and the configurator.
|
||||
# Options: "auto", "en", "zh-CN". "auto" follows the desktop locale
|
||||
# (WAYSCRIBER_LANGUAGE, then LANGUAGE/LC_ALL/LC_MESSAGES/LANG) and falls back
|
||||
# to English when that locale has no translation.
|
||||
# to English when that locale has no translation. Each process reads this when
|
||||
# it starts: the overlay and About window on their next open, background mode
|
||||
# after a restart.
|
||||
language = "auto"
|
||||
|
||||
# Show the status bar and its configured contents
|
||||
|
||||
+4
-3
@@ -958,9 +958,10 @@ theme = "auto"
|
||||
# Options: "auto", "on", "off"
|
||||
reduced_motion = "auto"
|
||||
|
||||
# UI language for overlay chrome
|
||||
# UI language: overlay chrome, About window, background mode, configurator
|
||||
# Options: "auto", "en", "zh-CN" ("auto" follows the desktop locale and falls
|
||||
# back to English when that locale has no translation)
|
||||
# back to English when that locale has no translation). Each process reads it
|
||||
# when it starts; background mode needs a restart.
|
||||
language = "auto"
|
||||
|
||||
# Show the status bar and its configured contents
|
||||
@@ -1136,7 +1137,7 @@ to save it.
|
||||
**Theme, Motion & Language:**
|
||||
- **Theme**: `theme` selects the overlay chrome theme — `"auto"` (default), `"dark"`, or `"light"`. `"auto"` currently resolves to dark chrome. The command palette supports `"light"` throughout its search, results, shortcut editing, and tooltips. Other popups retain their complete dark palette until migrated.
|
||||
- **Reduced motion**: `reduced_motion = "on"` disables overlay chrome animations (toast and flash fades render instantly; coverage extends to more surfaces as they adopt the shared animation envelopes). `"off"` keeps full motion. `"auto"` (default) is reserved for a future desktop-portal query of the system reduce-motion preference and currently behaves like `"off"` (full motion).
|
||||
- **Language**: `language` selects the language of the overlay chrome — `"auto"` (default) follows the desktop locale, `"en"` forces English, and `"zh-CN"` forces Simplified Chinese. A locale this build has no catalog for falls back to English, so `"auto"` is always safe; `WAYSCRIBER_LANGUAGE` overrides the setting for one run. Canvas content you type or name stays exactly as you wrote it. See `docs/I18N.md` for the catalog format and the surfaces that are not translated yet.
|
||||
- **Language**: `language` selects the interface language — overlay chrome, the About window, background mode's tray and notices, and the configurator. `"auto"` (default) follows the desktop locale, `"en"` forces English, and `"zh-CN"` forces Simplified Chinese. A locale this build has no catalog for falls back to English, so `"auto"` is always safe; `WAYSCRIBER_LANGUAGE` overrides the setting for one run. Picking a language in the configurator previews it there at once and is written on Save; the overlay and the About window use it the next time they open, and background mode after it restarts. `"auto"` follows each process's own locale, and a systemd user service can see a different one than your terminal. Canvas content you type or name stays exactly as you wrote it. See `docs/I18N.md` for the catalog format, local catalog overrides, and the surfaces that are not translated yet.
|
||||
|
||||
**UI Styling:**
|
||||
- **Font sizes**: Customize text size for status bar and help overlay
|
||||
|
||||
+111
-26
@@ -1,8 +1,10 @@
|
||||
# Internationalization
|
||||
|
||||
wayscriber draws its overlay chrome in the language named by `[ui] language`.
|
||||
English is the source language: UI strings are written in English in the code and
|
||||
looked up in a per-language catalog. A string with no entry draws in English.
|
||||
wayscriber draws its interface in the language named by `[ui] language`: the
|
||||
overlay and its toolbars, the About window, the background daemon's tray menu,
|
||||
shortcut descriptions and update notices, and the configurator. English is the
|
||||
source language: UI strings are written in English in the code and looked up in
|
||||
a per-language catalog. A string with no entry draws in English.
|
||||
|
||||
## Choosing a language
|
||||
|
||||
@@ -17,17 +19,43 @@ language = "auto"
|
||||
today — leaves the slot empty and the search continues; English is the final
|
||||
fallback, so `auto` never fails.
|
||||
|
||||
The configurator's Language row lists each language by its own name
|
||||
("English", "简体中文"), so a language you cannot read can always be switched
|
||||
back. Picking one previews it in the configurator window at once; nothing is
|
||||
written until Save. Each other process reads the saved setting when it starts:
|
||||
|
||||
- the overlay, the next time it opens;
|
||||
- the About window, the next time it opens;
|
||||
- background mode (the daemon: tray menu, portal shortcut descriptions and
|
||||
update notices), after it restarts.
|
||||
|
||||
`auto` resolves in each process from that process's own environment. Background
|
||||
mode usually runs as a systemd user service, whose environment can differ from
|
||||
a terminal's: `systemctl --user show-environment` shows what it sees, and a
|
||||
locale set with `systemctl --user set-environment LANG=…` or in
|
||||
`~/.config/environment.d/` applies after the service restarts.
|
||||
|
||||
Two environment variables help while testing a translation:
|
||||
|
||||
- `WAYSCRIBER_LANGUAGE=zh-CN` overrides `[ui] language` entirely, so a
|
||||
translation can be checked without editing `config.toml`.
|
||||
- `WAYSCRIBER_LANGUAGE=zh-CN` overrides `[ui] language` entirely, in every
|
||||
process that sees it, so a translation can be checked without editing
|
||||
`config.toml`.
|
||||
- `WAYSCRIBER_LOCALE_DIR=…` names a directory of catalog files to merge over
|
||||
the embedded ones, so a translation can be corrected without a rebuild.
|
||||
|
||||
Without that variable, a file in `$XDG_DATA_HOME/wayscriber/locales/<code>.toml`
|
||||
(such as `~/.local/share/wayscriber/locales/zh-CN.toml`) merges over the
|
||||
embedded catalog of the same code. Entries in the file win over the embedded
|
||||
ones; the file may hold a subset.
|
||||
ones; the file may hold a subset. A process reads the file once, so an edit
|
||||
applies to processes started afterwards. The file is ignored, with one log
|
||||
message, and the embedded catalog used instead, when it:
|
||||
|
||||
- is a symbolic link, a FIFO, or anything else that is not a regular file;
|
||||
- is larger than 1 MiB (the embedded Chinese catalog is about 140 KB);
|
||||
- is not UTF-8, or not valid TOML.
|
||||
|
||||
A single entry whose `{}` placeholders do not match its key is skipped, and
|
||||
the embedded entry kept.
|
||||
|
||||
## Adding a string
|
||||
|
||||
@@ -40,6 +68,22 @@ title: tr("Boards"),
|
||||
let caption = trf("{} of {} selected", &[&selected, &total]);
|
||||
```
|
||||
|
||||
`trf` fills anonymous `{}` placeholders with its arguments in order: the first
|
||||
`{}` in the translated text always receives the first argument. A translation
|
||||
can place its placeholders anywhere in the sentence, but it cannot swap two
|
||||
values, so phrase a template so that its values read in the same order in
|
||||
every language. `{name}` and `{0}` are not placeholders; they stay literal, and
|
||||
the guard rejects them in a template.
|
||||
|
||||
Write the string literal at the call: plain or raw, on one line or several.
|
||||
The guard reads only that, so a constant or an expression as the argument
|
||||
fails it. Code that builds a table for one language, rather than for the active
|
||||
one, binds the lookup under the same name so each literal stays readable:
|
||||
|
||||
```rust
|
||||
let tr = |source: &'static str| crate::i18n::lookup(language, source);
|
||||
```
|
||||
|
||||
Then run the coverage guard:
|
||||
|
||||
```sh
|
||||
@@ -59,27 +103,34 @@ are the English strings and only values are translated.
|
||||
|
||||
Rules the guard enforces:
|
||||
|
||||
- every `tr("…")`/`trf("…")` literal in `src/` has an entry in every catalog;
|
||||
- every action's `label`, `short_label`, and `description` in
|
||||
`src/config/action_meta/` has an entry too (the guard iterates
|
||||
`action_meta_iter()`, so derived names such as the compact display labels are
|
||||
covered by translating the underlying source strings);
|
||||
- every `tr(…)`/`trf(…)` call in `src/` and `configurator/src/` spells a
|
||||
string literal, and that literal has an entry in every catalog. The action
|
||||
accessors in `src/config/action_meta/` are the one exemption, because they
|
||||
translate metadata fields centrally;
|
||||
- every action's `label`, `short_label` and `description`, and the compact
|
||||
display label `action_display_label` derives from them (`Whiteboard` for
|
||||
"Toggle Whiteboard Mode"), has an entry too. The guard reads the metadata
|
||||
table and calls the production function, so a new action or a new derivation
|
||||
is covered without a literal to point at;
|
||||
- a `trf` template spells only `{}`, and its argument slice holds as many
|
||||
values as the template has placeholders;
|
||||
- a value is not identical to its key — an entry that repeats the key is dead
|
||||
weight and fails;
|
||||
- a value has the same number of `{}` placeholders as its key, so `trf` never
|
||||
drops or misplaces a value. Order may differ, which is the point.
|
||||
- a value has the same number of `{}` placeholders as its key.
|
||||
|
||||
Only literals can be marked. A string built at runtime — a binding label, a user
|
||||
name, a file name — is never translated. Canvas content (typed text, sticky
|
||||
notes, board and page names) is user data and stays as the user wrote it.
|
||||
|
||||
Displayed text is never an identifier. Widget IDs, stack page names, CSS names,
|
||||
config keys and action IDs are fixed English spellings derived from typed
|
||||
values, never from a label, and no code compares a label with translated text
|
||||
to decide what to do.
|
||||
|
||||
### What is not translated
|
||||
|
||||
- `--help` and `--version`, which print before the configuration is read;
|
||||
- log messages and developer diagnostics;
|
||||
- `wayscriber-configurator`, the separate GTK4 crate, whose own interface is
|
||||
still English. It does expose `[ui] language` under its UI settings, so the
|
||||
overlay language can be picked there instead of editing the file by hand;
|
||||
- keyboard key names. `Ctrl+Shift+1`, `Enter`, and `Not bound` are printed as
|
||||
they appear on the keyboard; only the prose around them is translated.
|
||||
- capture and export error detail — the `Display` text of `CaptureError` — which
|
||||
@@ -90,14 +141,31 @@ notes, board and page names) is user data and stays as the user wrote it.
|
||||
- overlay chrome that is not wrapped yet. The guard only sees strings that are
|
||||
already marked, so an unwrapped surface stays English; the config diagnostics
|
||||
in the about window's debug view are one such surface.
|
||||
- in the configurator, a message already on the status line, and the result
|
||||
of a session or background-setup action, keep the language they were shown
|
||||
in until the next message replaces them. Captions, option labels and other
|
||||
persistent text switch at once.
|
||||
|
||||
### Searching the configurator
|
||||
|
||||
The configurator's search matches its English terms in every language, so a
|
||||
search that works in English keeps working after a switch. It also matches tab,
|
||||
sub-tab and section titles, and keybinding action names, as they are displayed.
|
||||
A row's translated caption is not searchable unless it is one of those.
|
||||
|
||||
## Adding a language
|
||||
|
||||
1. `src/domain/language.rs`: add a variant, its `code()`, its `catalog_code()`,
|
||||
and the spellings `language_from_code`/`language_from_locale` accept.
|
||||
2. `locales/<code>.toml`: translate `locales/zh-CN.toml`, keeping the keys.
|
||||
3. `src/i18n.rs`: add the new file to `EMBEDDED_CATALOGS`.
|
||||
4. `cargo test --test repository_guards translations` must pass, which proves
|
||||
1. `src/domain/language.rs`: add a variant, its `code()`, its
|
||||
`catalog_code()`, and its serde name and aliases.
|
||||
2. `src/i18n.rs`: the spellings `language_from_code` and
|
||||
`language_from_locale` accept, the variant's `encode`/`decode` atom, and the
|
||||
new file in `EMBEDDED_CATALOGS`.
|
||||
3. `src/config/types/toolbar/items/definitions.rs`: a table for the language in
|
||||
`toolbar_item_definitions_in`, which keeps one per language.
|
||||
4. `configurator/src/models/fields/theme.rs`: a `LanguageOption`, labeled with
|
||||
the language's own name.
|
||||
5. `locales/<code>.toml`: translate `locales/zh-CN.toml`, keeping the keys.
|
||||
6. `cargo test --test repository_guards translations` must pass, which proves
|
||||
the new catalog covers every marked string.
|
||||
|
||||
## Design notes
|
||||
@@ -107,11 +175,28 @@ notes, board and page names) is user data and stays as the user wrote it.
|
||||
translated costs nothing and a catalog can grow one surface at a time.
|
||||
- Catalogs are embedded with `include_str!`, so a release binary carries its
|
||||
translations, and the optional user file merges over them.
|
||||
- The selected language is process-global and set once at startup, from
|
||||
`[ui] language` in `src/backend/wayland/backend/state_init/config.rs`. UI
|
||||
chrome text has no owner object able to carry a language, so renderers read
|
||||
the selection as ambient state; this is the one documented exception to the
|
||||
explicit-parameter rule in `src/ui/AGENTS.md`. Canvas text never reads it.
|
||||
- The selected language is process-global. UI chrome text has no owner object
|
||||
able to carry a language, so renderers read the selection as ambient state;
|
||||
this is the one documented exception to the explicit-parameter rule in
|
||||
`src/ui/AGENTS.md`. Canvas text never reads it.
|
||||
- Each process selects its language before it builds any chrome, with
|
||||
`i18n::initialize`, which also loads the catalog so the first frame does no
|
||||
file I/O: the overlay in `src/backend/wayland/backend/state_init/config.rs`,
|
||||
the daemon in `src/daemon/language.rs` before its tray, shortcut and
|
||||
update-notice workers start, and the About window in
|
||||
`src/about_window/mod.rs`. An overlay is a child process of the daemon and
|
||||
cannot change the daemon's language, so there is no live switching between
|
||||
processes.
|
||||
- The configurator opens in English, loads every catalog from its blocking
|
||||
config-load job, and applies the draft's language whenever the draft is
|
||||
replaced (a load, Defaults, a finished save) or the Language row changes it.
|
||||
The shell then rebuilds its pages from the model
|
||||
(`configurator/src/app/component/rebuild.rs`) and retitles the persistent
|
||||
chrome in place. The model owns everything typed or selected, so nothing the
|
||||
user entered is lost; focus, the caret and the visible page's scroll position
|
||||
are carried across. Text the model stores for display — catalog dates, the
|
||||
shortcut recorder's prompt, conflict claimants, background-setup feedback —
|
||||
is stored as what it reports and rendered when drawn.
|
||||
- Layout: the help overlay measures its labels, so a translation changes the
|
||||
column widths and the row count; a language whose words are much longer than
|
||||
English may need layout tuning in fixed-width chrome.
|
||||
+6
-2
@@ -10,7 +10,9 @@ language shows instead.
|
||||
```
|
||||
|
||||
- Only values are translated. Never change a key; it is the lookup key.
|
||||
- Keep the number of `{}` placeholders equal to the key's. Order may differ.
|
||||
- Keep the number of `{}` placeholders equal to the key's. A placeholder may
|
||||
move within the sentence, but values fill them in order: the first `{}` in
|
||||
the value always receives the first value, so two cannot be swapped.
|
||||
- An entry whose value equals its key is dead weight and fails the guard.
|
||||
- The file is ordered by key, case-insensitively.
|
||||
|
||||
@@ -24,4 +26,6 @@ See `docs/I18N.md` for the whole workflow, and `src/i18n.rs` for how a catalog
|
||||
is loaded. A copy of a catalog placed in
|
||||
`$XDG_DATA_HOME/wayscriber/locales/<code>.toml` (or the directory
|
||||
`WAYSCRIBER_LOCALE_DIR` names) merges over the embedded one, which is how a
|
||||
translation is corrected without rebuilding.
|
||||
translation is corrected without rebuilding. It must be a regular file of at
|
||||
most 1 MiB, not a symbolic link; otherwise it is ignored and the embedded
|
||||
catalog is used.
|
||||
Reference in new issue
Block a user