feat(i18n): tab auto titles follow the UI language

Titles are stored display text (layout templates, session snapshot,
broadcast registry), so a pane used to keep the wording of the language it
was opened in, and nextTerminalTitle could not even parse the number back
out of another language's pattern (numbering restarted, duplicates).

New pure module src/shared/terminalTitle.ts: resolution tries all four
languages' patterns, rendering uses the active one; i18n gains tFor(lang)
for off-language rendering. Workspace retitles auto-numbered local tabs in
place on language switch, snapshot restore and template apply; SSH panels
(user-typed connection names) are never touched. dockview's title-change
event propagates the new title to the broadcast registry and the snapshot
save with no extra wiring.

New test terminal-title.mjs (16 writer/reader language pairs, non-auto
title boundaries, gap filling); offline suite grows 17 -> 18.
This commit is contained in:
Bill committed 2026-10-08 00:11:20 +08:00
1 parent 9a46555fc1
commit 2653ab1820
9 files changed
+390 -36

No files matched your search

+12 -1
View File
@@ -90,7 +90,18 @@ export function onLanguageChange(cb: (lang: Language) => void): () => void {
/** Translate `key`; `{name}` placeholders are filled from `vars`. */
export function t(key: string, vars?: Record<string, string | number>): string {
const raw = lookup(DICTS[current], key) ?? lookup(DICTS[DEFAULT_LANGUAGE], key) ?? key
return tFor(current, key, vars)
}
/**
* Translate `key` as if `lang` were the active language, with the same
* fallback chain as `t`. Needed where a string has to be produced for a
* language other than the one being rendered: stored display text (terminal
* tab titles) is re-rendered on a language switch, and the language that text
* was written in has to be readable back out of it.
*/
export function tFor(lang: Language, key: string, vars?: Record<string, string | number>): string {
const raw = lookup(DICTS[lang], key) ?? lookup(DICTS[DEFAULT_LANGUAGE], key) ?? key
if (!vars) return raw
return raw.replace(/\{(\w+)\}/g, (match, name: string) =>
vars[name] === undefined ? match : String(vars[name])