Repository navigation
Add multi-language UI support (zh-CN first) 「using deepseek-V4.1-flash」 - #411
Conversation
|
Thanks for the contribution. I noticed that the commit lists me as both author and committer, although you submitted it from your fork. Could you explain how that happened and correct both fields to your own identity? |
Wrap every user-visible string in the overlay and the configurator with tr()/trf(), make the UI language selectable at runtime, and ship a complete Simplified Chinese catalog. Core: - src/i18n.rs: process-global language state, tr()/trf() lookup, and locale resolution (WAYSCRIBER_LANGUAGE > [ui] language > desktop locale > English) - src/domain/language.rs: Language enum with serde spellings - locales/zh-CN.toml: 2247 entries, embedded via include_str! - config: [ui] language = "auto" | "en" | "zh-CN" Coverage: - overlay: help overlay, context menus, board picker, status bar, toasts, command palette chrome, properties panel, about window, onboarding hints, top-toolbar menus (wayland + GTK), input HUD, arrow-style labels, navigation hints, all action labels - wayscriber-configurator: shell, sidebar tabs, every settings page, dialogs, and status messages; it loads the language at startup and again when the Language row changes, so its chrome follows the overlay Guards and tests: - tests/repository_guards/translations.rs: every tr()/trf() literal in src/ and configurator/src/ must have a catalog entry (call sites are reported); a value may not repeat its key; placeholder counts must match - tests/i18n.rs: catalog loading, override, and detection - configurator suite: 519 pass; full lib suite unchanged apart from pre-existing/environment failures Deliberately untranslated: --help/--version, log messages, keyboard key names, TOML config keys shown in validation errors, gsettings/systemctl command arguments, CSS class names, and product names.
|
I am very sorry for the inconvenience caused. I have corrected and resubmitted the pull request. What happened: the machine I committed from had no git identity configured at all (git could only auto-detect |
|
Thanks! I was just very confused. I am looking into the changes btw. |
The language-support commit left 108 files unformatted, so the format check failed, and strict Clippy stopped on a collapsible if in the toolbar definitions, needless lifetimes on the catalog lookup, and two needless borrows in the shortcut recorder prompts. This is rustfmt's output plus those four mechanical fixes; behavior is unchanged.
The catalogs load once per process, with whatever override file the runner has installed, and WAYSCRIBER_LANGUAGE beats every configured language, so a test that asserts Chinese text, or one that selects English, depended on the machine running it. Core tests that assert embedded Chinese now run in a fresh copy of the test binary with the override, the locale variables and the override directory cleared. The i18n integration test points the override directory at an empty one. with_temp_config_home clears the override for the config it loads and restores the process language afterwards, since loading a config at startup selects its language. The configurator, which links the core without its test-only locale neutrality, clears those variables once per test process, before any test takes the environment lock, and gains the same child runner for tests that select a language other than English.
Several places used displayed text as an identifier or read it to decide
what to do, so they broke once that text was translated:
- The configurator named its GtkStack pages after their translated tab
titles. The names were registered in the startup language and looked up
in the current one, so after a language change the sidebar and the UI
sub-tabs stopped switching pages. Tabs now have fixed stack names.
- The arrow-style menu built its widget IDs from the translated style
label, and the layout menu from the translated preset label, in both
toolbars ("top.arrow-style.pointy" became "top.arrow-style.尖头"). Both
now derive the ID from the typed value.
- The session catalog cleared its loading notice only when the status
contained the English "Loading sessions", which never matched in
Chinese. The notice is now a typed status that completion recognizes in
any language, and that re-renders when the language changes.
- The background-setup panel chose its failure styling by searching the
feedback for "failed" or "error", so translated failures looked like
successes. The feedback is now typed and carries its outcome, including
a light-control setup whose Hyprland reload failed after the controls
were written.
- The eraser tooltip compared the slider label with a translation; it now
reads a typed flag.
…er language
A local catalog override was read with read_to_string on the first
translated lookup: a FIFO named zh-CN.toml blocked that lookup until a
writer appeared, a large file was read whole, and a symlink was followed.
Overrides are now opened without following symlinks and without
blocking, checked on the opened descriptor to be a regular file, and read
up to 1 MiB. A file that fails those checks, or is not UTF-8 or TOML, is
ignored with one log message and the embedded catalog is kept; an entry
whose {} placeholders differ from its key is skipped. prepare_catalogs()
loads the catalogs ahead of the first lookup for processes that want that
file work done early.
The toolbar definitions cached only the last language and leaked a new
table with Box::leak on every switch. Each language now has one table,
built once from that language through i18n::lookup, so a table never
mixes two languages and switching back reuses it.
… window The About window is its own process, started with --about, and read the config only for its theme, so its text stayed English whatever [ui] language said. The daemon never selected a language either, so its tray menu, portal shortcut descriptions and update notices stayed English; an overlay it starts is a separate process and cannot set the daemon's language. i18n::initialize() selects a language and loads its catalog before the first surface is built. The overlay calls it where it already selected its language, the About window calls it with the language from the config read it makes for its theme, and the daemon reads the saved language once, before its tray, shortcut and update-notice workers start, whichever are enabled. A config the daemon cannot read keeps the automatic default. Background mode picks up a change when it restarts.
The catalog guard read only a string literal that followed tr( or trf( on
the same line, so the calls rustfmt wraps onto the next line were never
checked, and a call whose argument was a constant or an expression was
skipped without a word. Three strings had no catalog entry as a result:
"Light Mode drawing", "Light Mode passthrough" and "Path copied". The
guard compared only the {} counts in the catalog, so a template spelling
{min} and {max} passed while trf left those words in the message, as the
whole-number range error did.
The guard now reads plain and raw literals wherever they open the call,
across lines, and trf(tr("…"), &[…]). Any other argument fails, except in
the action accessors, whose metadata the guard audits directly, now
including the compact labels action_display_label derives. A trf template
must spell only {} and pass as many values as it has placeholders.
The three calls now spell their literals and have Chinese entries, the
range message uses two {} slots, and the trf documentation states that
values fill placeholders in order, so a translation can move them but not
swap two.
Picking a language set the process language, but every label already on screen kept its old text, so only dialogs opened afterwards changed. A reload, Defaults or a finished save replaced the draft without applying its language, which left the window in the language of the previous draft. Startup read the config a second time, synchronously, just to pick the language before the window was built. The draft's language is now applied on every transition that replaces the draft, and by the Language row, before the status text for that transition is built. When the language changes, the shell rebuilds its pages from the model and retitles the persistent chrome in place. The model owns everything typed or selected, so nothing entered is lost; the rebuild carries focus, the caret and selection, and the visible page's scroll position across, and editor popovers closed by the rebuild no longer cancel their edits. The window opens in English and switches when the first load arrives, and that load job prepares every catalog off the GTK thread. Text the configurator stored for display was translated once and kept: the "Never" of a catalog entry, the shortcut recorder's prompt, the claimants of a pending shortcut conflict. Those now store what they report and are rendered when drawn. The language list names each language in itself, so a preview the user cannot read can still be undone, and search matches the English terms in every language as well as tab, sub-tab and section titles as displayed. A GTK test runs the real window on the private headless compositor and checks a language switch with open editors, focus, caret, scroll and navigation; both GTK gates require its marker.
Much of the configurator still drew English in Chinese: row titles and hints, buttons, accessibility labels, placeholders, validation errors, status messages such as the shortcut reset and conflict-review notices, the session card's Created, Opened and Saved prefixes, and the background-setup status lines. They are now marked where they are drawn, with format! text turned into trf templates and displayed constants written as literals so the catalog guard can read them, and the Chinese catalog gains an entry for each new key. Values written into config or other programs stay as they were: default board and profile names, the GNOME shortcut name and default quick-color labels.
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.
`--language CODE` sets the interface language for one run of --active, --freeze, --daemon or --about without changing config.toml. It accepts the spellings `[ui] language` does and takes precedence over both WAYSCRIBER_LANGUAGE and the config. A daemon started with it gives it to the About windows it opens, and to its overlays as an optional environment input that an older overlay ignores. The process broker strips that input from every other helper, so a configurator opened from an overlay keeps previewing its own Language row. With --daemon-toggle or an overlay action the flag is an error, since a running daemon's overlays cannot be changed from outside. `--list-languages` prints each language's code and name, the configured language, and the language in effect with the setting that decided it. Languages now carry their native and English names, and the configurator's Language row takes its labels from them. The help text, README, CONFIG.md, I18N.md and config.example.toml describe both options.
|
Thank you again, @weimona, for this PR. Multi-language support is a big feature, and your work gave a strong base for it. I added my commits on top of your branch. I did not change your commit. I plan to merge with a merge commit, so that your commit stays in the history with your name. In my commits:
I also added 128 Chinese strings to Thank you again for your contribution! |
Wrap every user-visible string in the overlay and the configurator with tr()/trf(), make the UI language selectable at runtime, and ship a complete Simplified Chinese catalog.
Core:
Coverage:
Guards and tests:
Deliberately untranslated: --help/--version, log messages, keyboard key names, TOML config keys shown in validation errors, gsettings/systemctl command arguments, CSS class names, and product names.