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:
@@ -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}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user