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 { export function detectInitialLocale(): Locale {
const stored = readStoredLocale(); const stored = readStoredLocale();
if (stored) { if (stored) {
return stored; return stored;
} }
const hostLanguage = globalThis.window?.__OPENCHAMBER_HOST_LANGUAGE__;
if (hostLanguage) {
return normalizeLocale(hostLanguage);
}
return DEFAULT_LOCALE; 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. 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 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 inlined in the generated HTML because the splash renders before the webview
(`openchamber.i18n.v1` in webview localStorage), not from VS Code's display bundle loads. It picks them from OpenChamber's own saved locale
language, because the splash renders before the webview bundle loads. (`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'}, initialSessionId: ${initialSessionId ? `"${initialSessionId.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : 'null'},
}; };
window.__OPENCHAMBER_HOME__ = "${workspaceFolder.replace(/\\/g, '\\\\')}"; window.__OPENCHAMBER_HOME__ = "${workspaceFolder.replace(/\\/g, '\\\\')}";
// VS Code's display language. The UI bundle uses it as the default locale
function getBootstrapMessages() { // until the user picks one; the splash below picks its strings from it too.
var locale = 'en'; 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 { try {
var rawLocale = window.localStorage.getItem('openchamber.i18n.v1'); var rawLocale = window.localStorage.getItem('openchamber.i18n.v1');
if (rawLocale) { if (rawLocale) {
var parsedLocale = JSON.parse(rawLocale); var parsedLocale = JSON.parse(rawLocale);
if (parsedLocale && typeof parsedLocale.locale === 'string') { if (parsedLocale && typeof parsedLocale.locale === 'string') return parsedLocale.locale.toLowerCase();
var detected = parsedLocale.locale.toLowerCase();
if (detected.indexOf('fr') === 0) {
locale = 'fr';
} else if (detected.indexOf('tr') === 0) {
locale = 'tr';
}
}
} }
} catch {} } 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') { if (locale === 'fr') {
return { return {
@@ -296,27 +305,19 @@ export function getWebviewHtml(options: WebviewHtmlOptions): string {
const statusEl = document.getElementById('loading-status'); const statusEl = document.getElementById('loading-status');
const getDevMessages = () => { const getDevMessages = () => {
try { const detected = resolveBootstrapLanguage();
const rawLocale = window.localStorage.getItem('openchamber.i18n.v1'); if (detected.indexOf('fr') === 0) {
if (rawLocale) { return {
const parsedLocale = JSON.parse(rawLocale); startingDevServer: (host) => 'Démarrage du serveur de développement de la webview (' + host + ')...',
if (parsedLocale && typeof parsedLocale.locale === 'string') { waitingDevServer: (host, attempt) => 'En attente du serveur de développement de la webview (' + host + ')... tentative ' + attempt,
const detected = parsedLocale.locale.toLowerCase(); };
if (detected.indexOf('fr') === 0) { }
return { if (detected.indexOf('tr') === 0) {
startingDevServer: (host) => 'Démarrage du serveur de développement de la webview (' + host + ')...', return {
waitingDevServer: (host, attempt) => 'En attente du serveur de développement de la webview (' + host + ')... tentative ' + attempt, startingDevServer: (host) => 'Webview dev sunucusu başlatılıyor (' + host + ')...',
}; waitingDevServer: (host, attempt) => 'Webview dev sunucusu bekleniyor (' + host + ')... deneme ' + 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 {}
return { return {
startingDevServer: (host) => 'Starting webview dev server (' + host + ')...', startingDevServer: (host) => 'Starting webview dev server (' + host + ')...',
waitingDevServer: (host, attempt) => 'Waiting for webview dev server (' + host + ')... attempt ' + attempt, waitingDevServer: (host, attempt) => 'Waiting for webview dev server (' + host + ')... attempt ' + attempt,