diff --git a/packages/vscode/l10n/bundle.l10n.tr.json b/packages/vscode/l10n/bundle.l10n.tr.json index 99518c7f..ca5f9134 100644 --- a/packages/vscode/l10n/bundle.l10n.tr.json +++ b/packages/vscode/l10n/bundle.l10n.tr.json @@ -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", diff --git a/packages/vscode/package.nls.tr.json b/packages/vscode/package.nls.tr.json index a59693a6..2a61279c 100644 --- a/packages/vscode/package.nls.tr.json +++ b/packages/vscode/package.nls.tr.json @@ -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", diff --git a/packages/vscode/src/DOCUMENTATION.md b/packages/vscode/src/DOCUMENTATION.md index 79d843ff..8cabb2b6 100644 --- a/packages/vscode/src/DOCUMENTATION.md +++ b/packages/vscode/src/DOCUMENTATION.md @@ -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. diff --git a/packages/vscode/src/localizationBundles.test.ts b/packages/vscode/src/localizationBundles.test.ts new file mode 100644 index 00000000..45a31c07 --- /dev/null +++ b/packages/vscode/src/localizationBundles.test.ts @@ -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 => + 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}`); + } + } + }); +});