# Desktop Language Toggle Design

## Goal

Add a language button immediately before the theme button. Clicking it switches between English and Simplified Chinese in the current window and persists the selection to `~/.pi/agent/settings.json`.

## User Experience

- Use the existing Material Symbols `translate` icon in a compact icon button.
- Add a deterministic `translate` entry to `ICON_FALLBACKS` and a matching local SVG path so the button remains an icon when the Material Symbols font is unavailable.
- The tooltip and accessible label describe the target language: `Switch to Chinese` in English and `切换到英文` in Chinese.
- A click sends a persistence request to the extension. The visible language changes after the extension confirms the write, normally within the same interaction.
- While the request is pending, disable the button to prevent duplicate writes.
- If persistence fails, retain the current language and surface the backend error through the existing command toast.

## Architecture

The WebView sends `{ type: "set-language", language }` through the Glimpse bridge. The extension validates the requested value, merges it into the existing settings object, writes the settings file, and responds with `{ type: "language-update", success, language, error? }`.

The frontend changes `currentLanguage` from a constant to mutable state. On a successful response it updates `data.language`, reapplies static `data-i18n*` attributes, updates the language button, and rerenders all visible dynamic surfaces from the existing application state. No window reload is required.

A small browser-neutral `LanguageToggleUtils` helper owns next-language selection and message viewport snapshot/restore behavior. It is injected before `app.js` and is executable from Node tests without a browser dependency.

## Persistence

Add `saveConfiguredLanguage(settingsPath, value)` to `i18n-utils.js`.

- Accept only values in `SUPPORTED_LANGUAGES`.
- Preserve unrelated settings fields.
- Create the settings directory when the file does not exist.
- Refuse to overwrite malformed JSON or a non-object root.
- Serialize to a uniquely named temporary file in the settings directory, then atomically rename it over `settings.json`.
- Remove a leftover temporary file if writing or renaming fails, while leaving the original settings file intact.
- Write formatted JSON with a trailing newline.

## Rerender Scope

After a successful switch, refresh:

- Static labels, titles, placeholders, and accessible labels.
- Sidebar project/workspace/thread content.
- Breadcrumb and current main view.
- Stats and context usage.
- Read-only banner and input state.
- Streaming send/cancel title.

Before rerendering the thread view, capture the message container's distance from the bottom and each `<details>` element's open state, then clear the language-dependent message HTML cache. Call `renderMainContent()` once; it already refreshes the project tree and current view. Update the send/cancel button through a non-rendering helper rather than `updateStreamingUI()`. Restore the captured message state in the next animation frame, after `renderMessages()` has scheduled its normal scroll callback.

User content, paths, model/provider names, command descriptions, backend errors, input content, scroll position, expanded tool cards, and current interaction state remain unchanged.

## Testing

- Unit-test settings creation, field preservation, supported-value validation, and malformed JSON protection.
- Unit-test next-language selection and message viewport/details snapshot restoration through `LanguageToggleUtils`.
- Add structural verification for the header button, bridge message cases, mutable language state, single-pass rerender function, and icon fallback mapping.
- Keep dictionary parity, ASCII-safe Chinese source, icon fallback coverage, TypeScript, syntax, and existing runtime checks in `npm run check`.
- Manually verify both switch directions, immediate rerender, failure behavior, and persistence after reopening `/desktop`.
