Repository navigation
refactor: show the Settings window and menu bar through Fluent messages - #880
Merged
Merged
Conversation
…t messages First step of Phase 2 of docs/i18n.md. - scripts/check-i18n.py: a module marked `// i18n: migrated` (the file and the directory of the same name) may hold no string literal that reads as English outside logs, panics, attributes, Display writes, and key bindings; `// i18n: data` exempts a literal that is data. - The en-XA leak harness moves to `i18n::leak_tests` so each window keeps its own test; Settings gets one per page, ignored until the page is migrated. - Settings: the window title, section list, groups, page leads, search, footer, and every catalog row's title and description come from settings.ftl; the `[features]` flags name messages in features.ftl. Search matches the translated and the English text. Page selectors are their message IDs, the same in every language. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The layout enums under config keep their config names and gain a message ID each (layout.ftl, tab-layout.ftl, chat.ftl); `label()` now formats it in the current locale, so the View menu and Settings show them translated. Settings' segmented control takes owned labels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Every menu and item of the menu bar, the QA menu included, and the in-window bar's own labels (Linux and Windows) come from menus.ftl. A test builds the menus under en-XA and checks every name is a translated message, since the leak test cannot see macOS's native menus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The page's groups, rows, tab bar positions, and each SSH host's scrollback note come from settings-tabs.ftl, and the rows search finds from their settings.ftl messages. A host's failure keeps its error as an $error placeable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Files group, the file rows' titles, Reveal, Open, Reload, and the placeholder for an unreadable Herdr config come from messages; paths stay data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…ssages - settings.ftl gains the shared controls' text: the read-only and unavailable reasons, On/Off, notification positions, the number range, the swatch's "none", and the footer's save and reload outcomes, with errors as $error placeables. - The "Use Herdr setting" dropdown entry is one message around Herdr's value; the button mutes the words around the value with highlights on a single line instead of matching an English prefix. - Position tables name message IDs, checked by a test. - General (with Windows' Launch at login) and Notifications draw from settings-general.ftl and settings-notifications.ftl, and their en-XA leak tests, with Tabs and Configuration, now run. - Native live checks mark their fixture names and log lines as data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Remote control page's rows, notes, buttons, status lines, and errors come from settings-remote.ftl, with plurals as Fluent selectors and the port, fingerprint, and errors as placeables. Rows in the catalog reuse its titles. The pane history reload failure on SSH hosts moves there too. Form cards take a borrowed title, so a page can pass a formatted message, and the certificate files card gets a title instead of its ID. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Code page's VS Code mode, server state, license, connection test, and px0 address come from settings-code.ftl; paths, addresses, and commits are placeables, and example addresses stay data. The license text is a message here; the browser's own copy of it still uses the launcher's constant until the browser module is migrated. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Integrations page's search, its fallback without a session window, and the list the daemon's host reports (crate::integrations) come from settings-integrations.ftl and integrations.ftl; agent names, commands, hosts, and the daemon's messages stay data. The chats' environment card on the General page, about what agents start with, moves to settings-integrations.ftl too. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Plugins page's notes, empty states, switches, and value details come from settings-plugins.ftl, with the status labels count as a plural. Searching finds the status labels row by its translated or English title. Plugin tokens, the samples they report, host names, and the preview's example workspace and agent stay data. The page's en-XA leak test now runs, allowing the preview's names and their truncations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Theme page, its theme browser, the window backdrop, and terminal padding take their text from settings-theme.ftl, and rows search finds use their settings.ftl titles. Theme names stay data; FOLLOW stays the config value it is compared as, and the page shows a message for it. Load and preview errors pass the error as an argument. The page's en-XA leak test now runs; the number control's unit and range are listed as data until `ui::number` is migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Sidebar page and its host colour rows take their text from settings-sidebar.ftl, and rows search finds use their settings.ftl titles and descriptions. Host rows name their hint's message, and the swatch row takes the formatted hint. Host names stay data. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Each status icon set names its title and description by messages in settings-indicators.ftl, as do the statuses its card previews; the edge rail and Icons heading use their settings.ftl rows. Tests check every such ID is an English message. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Fonts page, its specimen, and the family picker take their text from settings-fonts.ftl, and each face its settings.ftl row title. The picker selects which face it sets inside one message rather than joining a translated face name. Specimen lines are a message each, except the terminal's, which is code. Family names stay data. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Status bar page takes its heading, choices, and note from settings-status-bar.ftl, and its rows their settings.ftl titles and descriptions where those match. The choice tables name messages, which a test checks. The page's en-XA leak test now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Number fields take a Unit (pixels, seconds, percent, or none) whose label is a message, in place of an English string; the font faces, the font search placeholder, and the missing config path's line are messages too. controls.rs is marked migrated. The en-XA leak test no longer needs to allow units and ranges as data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…nt messages The Cloud devices page's own text, its New device setup card, and each provider's Images section come from settings-cloud.ftl. The setup summary under each provider's switch, also shown by the Coder menu, moves to cloud-setup.ftl with plural line counts. Provider cards gain a `cloud_card` helper that takes a translated title, and field labels take owned text, ahead of migrating each provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Azure card's fields, sign-in status, size choices, device rows, and delete confirmation come from settings-cloud-azure.ftl; text the provider cards share (Save, Cancel, Remove from Herdr only, delete outcomes) is in settings-cloud.ftl. The fields table names message IDs, checked by a test. Whether a device is a VM or a sandbox selects within each message rather than splicing in an English noun. Size specs and prices still come formatted from herdr-remote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Boat card's fields, key status, sandbox count (now a plural), pending creation recovery, and device rows come from settings-cloud-boat.ftl, with the API key and creation text other cards share in settings-cloud.ftl. The default API URL stays the URL field's placeholder as data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
The Coder card's fields, sign-in state, client secret notes, and the workspace destroy confirmation and progress come from settings-cloud-coder.ftl. A shared `Hint` names a field's placeholder as a message or as a value shown as it is, such as an example URL, and Boat's fields use it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…ssages The Container card's engine line, fields, new container box, container rows and their states, and the unsaved list come from settings-cloud-container.ftl. Each row's state is one message with the engine as an argument; the default image, CPUs, and memory stay their fields' placeholders as values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…ages The Daytona card's fields, key status, sandbox count (now a plural selector rather than an English suffix), and new sandbox box come from settings-cloud-daytona.ftl, sharing the API key and creation text in settings-cloud.ftl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…messages The OpenSandbox card's fields, server status with a plural sandbox count, key notes, and new sandbox box come from settings-cloud-opensandbox.ftl. Its "(experimental)" title is a shared message around the provider's name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…ages The Railway card's key choices, generated key location, file dialog title, device rows, and status lines come from settings-cloud-railway.ftl. The key name rule is a message of its own, with a test that its English stays herdr-remote's NAME_RULE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
…ages The Sprites card's API URL and token fields, token notes, sprite count as a plural, and new sprite box come from settings-cloud-sprites.ftl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Cloud devices is marked migrated, and its en-XA page test runs, allowing only the provider names on the tabs. That page test opens no card, so a test of its own draws each provider's tab, with every account unconfigured, under en-XA and allows only provider names and the values fields show as placeholders. It found the Azure sandbox sizes' "GiB disk" coming from herdr-remote, now a message, and size choices cutting their lines short: they now wrap, as a translation may run longer than English. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Every Settings page now draws only messages, so the marker moves to settings_window.rs and check-i18n rejects new English literals anywhere under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Owner
Author
|
|
[Medium impact] The changes appear safe to merge; both previous findings are fixed, and no new actionable issues were found. SummaryMoves Settings and menu-bar text into Fluent messages and adds checks for untranslated text.
Reviews (2) · Last reviewed commit: "fix: show Settings search hints in the l..." · Reviewed by Greptile |
The tokenizer started a name on any `isalnum()` character but matched it with an ASCII-only pattern, so `let café = 1;` tripped an assertion. The name pattern is now Unicode-aware `\w`, matched in place rather than on a slice of the rest of the source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Settings formatted its field placeholders once, when each field was built, so changing `language` or the QA pseudo-locale left the search, theme, font, integration, plugin, px0, and cloud-provider hints in the old language until the window reopened. `SearchInput::set_placeholder_with` keeps how to format the hint and formats it again when `CurrentLocale` changes, without touching what was typed. Example paths and tokens keep `set_placeholder`, which stays as given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE
Owner
Author
|
@greptileai review |
1 task done
This was referenced Oct 30, 2026
penso
added a commit
that referenced
this pull request
Oct 31, 2026
#941) * fix(i18n): map the errors #905 added so the app builds again #905 (iOS app, one pairing for every machine) and #926 (localized errors) merged independently. #905 moved herdr-automate's binary, checksum, and platform failures into host_build::Error and added herdr_client::Error::TerminalAttach, remote control's app::Error::Enroll, and the root Error::Nearby, none of which had a Localize mapping, so main no longer compiled. Each new enum gets its own mapping and .ftl file, as #926 set out: host_build (error-host-build.ftl, which takes over the three automation messages), terminal_attach (error-client-terminal-attach.ftl), nearby (error-nearby.ftl), and enroll (in error-remote-control.ftl). Per-variant tests cover each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh5CsF9aiVsJURUwioh * style(i18n): keep the client error mapping sorted * refactor(i18n): show the window's notices and tab faces through Fluent Part of Phase 2 of docs/i18n.md, following #880. Toasts, flashes, the config diagnostic cards, the agent skill offer, and the window's command notices take their text from window-notices.ftl and notifications.ftl; the tab faces' muted hint and close button from tab-layout.ftl. - Marked migrated: notifications.rs, tab_layout.rs, and in window/ the toasts, flash, system notifications, config diagnostic, commands, monitors, zen, and Shift-select hint modules. D-Bus and registry names in the system notification backends are tagged as data. - Errors reaching these notices use localize() instead of to_string(): request failures, the agent skill install, keep-awake, checkpoint restores, and dismissing the announcement. - The skill install's place count is a Fluent selector. - notifications.rs's inline test module moves to notifications/tests.rs. - A new en-XA leak test covers each toast preview, a hint and a flash, the status line's error, the config diagnostic card, and the agent skill offer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh5CsF9aiVsJURUwioh * fix(i18n): keep preview direction marks and restore failure causes Greptile on #941: Notice::new cleans daemon text with safe_text, which also stripped the direction marks en-XB puts in the QA toast and system notification previews; previews now set their title and body from the messages afterwards. A failed checkpoint restore's flash lost the cause checkpoint::describe used to append; a failed Git script's or worker's error now follows the message. Also puts host_build.rs's keep-sorted end marker back on its own line, which main's rustfmt had folded away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFavh5CsF9aiVsJURUwioh --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First PR of Phase 2 of
docs/i18n.md(string migration), which ships as a series split by feature area. This one covers the Settings window and the menu bar, plus the tooling every later PR uses. English text is unchanged except for the two visible changes listed below.Tooling
scripts/check-i18n.py: a module marked// i18n: migrated(the file and the directory of the same name) may hold no string literal that reads as English. Logs, panics, attributes such as#[error],write!in aDisplay, key contexts and bindings are exempt. A literal that is data (a theme or font name, a config value) takes// i18n: data. Covered by new Python tests.i18n::leak_tests(under_en_xa,draw,assert_translated), so each window keeps its own leak test. Settings has one test per page, plus one per Cloud devices provider card. The menu bar has a test that builds every menu under en-XA, since macOS's native menus are not drawn by the app.i18n::takes(id)(test only) checks tables that name messages as data.Settings window (
settings_window/is marked migrated as a whole)settings.ftl. Search matches both the translated and the English text, as the palette does. Page selectors are their message IDs, the same in every language..ftlfile:settings-general,-tabs,-notifications,-configuration,-theme,-sidebar,-indicators,-fonts,-status-bar,-code,-integrations,-plugins,-remote,-cloud, and one per cloud provider. Parallel PRs therefore do not share a catalog.Unitinstead of an English string.[features]flags, sidebar layouts, tab layouts and chat layouts are named by messages (features.ftl,layout.ftl,tab-layout.ftl,chat.ftl). The few outside helpers Settings draws are migrated with it:integrations.rsandcloud_setup::summary.Menu bar: every menu and item, QA included, and the in-window bar's own labels on Linux and Windows (
menus.ftl).Visible changes
Not in this PR (next PRs in the series)
menu/, about 700 literals), then the remaining surfaces.$errorplaceables fed their EnglishDisplay.remote::device_line.Validation
just formatjust ciondba48610(macOS arm64): passed. 14,492 Rust tests, 0 failed; check-i18n reports 1017 messages in 32 files, valid and used.just lint-windows: passedjust lint-linux: passedScreenshots
No native screenshots: they were not captured in this session, and no desktop interaction was done. English rendering is meant to be unchanged apart from the two visible changes above.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BFavh55PnUHvhsWwqaMNjE