fix(vscode): complete the Turkish extension bundles

The inline-comment feature added its manifest and runtime strings to English and French only, so Turkish users saw English command titles, thread labels and warnings. Adds the ten missing keys and a test that fails whenever a locale bundle drifts from the English key set or its placeholders.
This commit is contained in:
Bohdan Triapitsyn
2026-09-05 14:47:10 +03:00
parent 9eb8a92169
commit 6c3e51956a
4 changed files with 69 additions and 0 deletions
+7
View File
@@ -13,6 +13,13 @@
"OpenChamber [Improve Code]: No active editor": "OpenChamber [Kodu İyileştir]: Aktif düzenleyici yok",
"OpenChamber [Improve Code]: No text selected": "OpenChamber [Kodu İyileştir]: Metin seçilmedi",
"Improve the following Code:": "Aşağıdaki Kodu iyileştir:",
"OpenChamber [Add Comment]: No active editor": "OpenChamber [Yorum Ekle]: Aktif düzenleyici yok",
"OpenChamber [Add Comment]: File is outside the workspace": "OpenChamber [Yorum Ekle]: Dosya çalışma alanının dışında",
"OpenChamber [Add Comment]: The comment never reached the chat and was discarded": "OpenChamber [Yorum Ekle]: Yorum sohbete ulaşmadı ve atıldı",
"Comment on line {0}": "{0}. satıra yorum",
"Comment on lines {0}-{1}": "{0}-{1}. satırlara yorum",
"OpenChamber": "OpenChamber",
"Not sent yet": "Henüz gönderilmedi",
"OpenCode CLI not found. Install it and ensure it's in PATH.": "OpenCode CLI bulunamadı. Yükleyin ve PATH içinde olduğundan emin olun.",
"OpenCode CLI not found. Please install it and ensure it's in PATH.": "OpenCode CLI bulunamadı. Lütfen yükleyin ve PATH içinde olduğundan emin olun.",
"More Info": "Daha fazla bilgi",
+3
View File
@@ -10,6 +10,9 @@
"command.openNewSessionInEditor.title": "Düzenleyicide yeni oturum aç",
"command.openCurrentOrNewSessionInEditor.title": "Oturumu düzenleyicide aç",
"command.addToContext.title": "Bağlama ekle",
"command.addLineComment.title": "Yorum ekle",
"command.submitLineComment.title": "Yorum yap",
"command.removeLineComment.title": "Yorumu kaldır",
"command.explain.title": "Açıkla",
"command.improveCode.title": "Kodu iyileştir",
"command.newSession.title": "Yeni oturum",
+14
View File
@@ -190,3 +190,17 @@ resolves `$XDG_CONFIG_HOME/opencode` at extension startup, falling back to
No files are migrated. The behavior GET bridge response includes the effective
`path` for both existing and missing AGENTS.md files; shared Settings uses it
in the warning.
## Extension localization
Two bundles carry extension-host text: `package.nls*.json` for the manifest
`%token%` strings and `l10n/bundle.l10n*.json` for the `t(...)` call sites.
Every locale file must cover the full English key set with the same `{0}`
placeholders — VS Code silently falls back to English per missing key, so a
half-translated locale looks like a shipped feature. `localizationBundles.test.ts`
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.
@@ -0,0 +1,45 @@
import assert from 'node:assert/strict';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { describe, test } from 'node:test';
const packageRoot = path.resolve(__dirname, '..');
const readJson = (relativePath: string): Record<string, string> =>
JSON.parse(fs.readFileSync(path.join(packageRoot, relativePath), 'utf8'));
const placeholders = (value: string): string[] => (value.match(/\{\d+\}/g) ?? []).sort();
const localeFiles = (directory: string, prefix: string, suffix: string): string[] =>
fs
.readdirSync(path.join(packageRoot, directory))
.filter((name) => name.startsWith(prefix) && name.endsWith(suffix) && name !== `${prefix}${suffix}`)
.map((name) => path.join(directory, name));
describe('extension localization bundles', () => {
test('every runtime string bundle covers the English keys with matching placeholders', () => {
const english = readJson('l10n/bundle.l10n.json');
for (const file of localeFiles('l10n', 'bundle.l10n', '.json')) {
const translated = readJson(file);
for (const [key, source] of Object.entries(english)) {
const value = translated[key];
assert.equal(typeof value, 'string', `${file} is missing the key ${JSON.stringify(key)}`);
assert.deepEqual(
placeholders(value),
placeholders(source),
`${file} changes the placeholders of ${JSON.stringify(key)}`
);
}
}
});
test('every manifest bundle covers the English keys', () => {
const english = readJson('package.nls.json');
for (const file of localeFiles('.', 'package.nls', '.json')) {
const translated = readJson(file);
for (const key of Object.keys(english)) {
assert.equal(typeof translated[key], 'string', `${file} is missing the key ${key}`);
}
}
});
});