fix(vscode): default to the VS Code display language before a saved locale

The splash and the UI bundle both started in English on a fresh install
even when VS Code ran in a supported language, because only OpenChamber's
own saved locale was consulted. The HTML now exposes VS Code's display
language; the splash and detectInitialLocale use it until the user picks
a locale, which still wins.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
This commit is contained in:
Bohdan Triapitsyn
2026-09-05 15:08:15 +03:00
parent 319cf9f17a
commit c3a83b3181
3 changed files with 52 additions and 35 deletions
+12
View File
@@ -102,11 +102,23 @@ export function writeStoredLocale(locale: Locale): void {
}
}
declare global {
interface Window {
/** The host application's display language (VS Code sets it), used before the user picks a locale. */
__OPENCHAMBER_HOST_LANGUAGE__?: string;
}
}
export function detectInitialLocale(): Locale {
const stored = readStoredLocale();
if (stored) {
return stored;
}
const hostLanguage = globalThis.window?.__OPENCHAMBER_HOST_LANGUAGE__;
if (hostLanguage) {
return normalizeLocale(hostLanguage);
}
return DEFAULT_LOCALE;
}
+7 -3
View File
@@ -201,6 +201,10 @@ half-translated locale looks like a shipped feature. `localizationBundles.test.t
enforces that, and it is the check to run whenever a feature adds a new string.
The pre-bundle loading splash in `webviewHtml.ts` is separate: its strings are
inlined in the generated HTML and chosen from OpenChamber's own saved locale
(`openchamber.i18n.v1` in webview localStorage), not from VS Code's display
language, because the splash renders before the webview bundle loads.
inlined in the generated HTML because the splash renders before the webview
bundle loads. It picks them from OpenChamber's own saved locale
(`openchamber.i18n.v1` in webview localStorage) and, before the user has
chosen one, from VS Code's display language, which the HTML exposes as
`window.__OPENCHAMBER_HOST_LANGUAGE__`. The UI bundle reads the same value as
its default locale (`detectInitialLocale`), so a fresh install in a supported
language starts in that language on both the splash and the app.
+33 -32
View File
@@ -195,23 +195,32 @@ export function getWebviewHtml(options: WebviewHtmlOptions): string {
initialSessionId: ${initialSessionId ? `"${initialSessionId.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : 'null'},
};
window.__OPENCHAMBER_HOME__ = "${workspaceFolder.replace(/\\/g, '\\\\')}";
function getBootstrapMessages() {
var locale = 'en';
// VS Code's display language. The UI bundle uses it as the default locale
// until the user picks one; the splash below picks its strings from it too.
window.__OPENCHAMBER_HOST_LANGUAGE__ = ${JSON.stringify(vscode.env.language)};
// OpenChamber's own saved locale wins; before one exists, VS Code's display
// language decides, so a fresh install in a supported language never boots
// in English.
function resolveBootstrapLanguage() {
try {
var rawLocale = window.localStorage.getItem('openchamber.i18n.v1');
if (rawLocale) {
var parsedLocale = JSON.parse(rawLocale);
if (parsedLocale && typeof parsedLocale.locale === 'string') {
var detected = parsedLocale.locale.toLowerCase();
if (detected.indexOf('fr') === 0) {
locale = 'fr';
} else if (detected.indexOf('tr') === 0) {
locale = 'tr';
}
}
if (parsedLocale && typeof parsedLocale.locale === 'string') return parsedLocale.locale.toLowerCase();
}
} catch {}
return String(window.__OPENCHAMBER_HOST_LANGUAGE__ || '').toLowerCase();
}
function getBootstrapMessages() {
var locale = 'en';
var detected = resolveBootstrapLanguage();
if (detected.indexOf('fr') === 0) {
locale = 'fr';
} else if (detected.indexOf('tr') === 0) {
locale = 'tr';
}
if (locale === 'fr') {
return {
@@ -296,27 +305,19 @@ export function getWebviewHtml(options: WebviewHtmlOptions): string {
const statusEl = document.getElementById('loading-status');
const getDevMessages = () => {
try {
const rawLocale = window.localStorage.getItem('openchamber.i18n.v1');
if (rawLocale) {
const parsedLocale = JSON.parse(rawLocale);
if (parsedLocale && typeof parsedLocale.locale === 'string') {
const detected = parsedLocale.locale.toLowerCase();
if (detected.indexOf('fr') === 0) {
return {
startingDevServer: (host) => 'Démarrage du serveur de développement de la webview (' + host + ')...',
waitingDevServer: (host, attempt) => 'En attente du serveur de développement de la webview (' + host + ')... tentative ' + attempt,
};
}
if (detected.indexOf('tr') === 0) {
return {
startingDevServer: (host) => 'Webview dev sunucusu başlatılıyor (' + host + ')...',
waitingDevServer: (host, attempt) => 'Webview dev sunucusu bekleniyor (' + host + ')... deneme ' + attempt,
};
}
}
}
} catch {}
const detected = resolveBootstrapLanguage();
if (detected.indexOf('fr') === 0) {
return {
startingDevServer: (host) => 'Démarrage du serveur de développement de la webview (' + host + ')...',
waitingDevServer: (host, attempt) => 'En attente du serveur de développement de la webview (' + host + ')... tentative ' + attempt,
};
}
if (detected.indexOf('tr') === 0) {
return {
startingDevServer: (host) => 'Webview dev sunucusu başlatılıyor (' + host + ')...',
waitingDevServer: (host, attempt) => 'Webview dev sunucusu bekleniyor (' + host + ')... deneme ' + attempt,
};
}
return {
startingDevServer: (host) => 'Starting webview dev server (' + host + ')...',
waitingDevServer: (host, attempt) => 'Waiting for webview dev server (' + host + ')... attempt ' + attempt,