@extenshi/cli 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/cli.js +125 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +102 -26
- package/dist/config.js.map +1 -1
- package/dist/cws-policies.d.ts.map +1 -1
- package/dist/cws-policies.js +1 -0
- package/dist/cws-policies.js.map +1 -1
- package/dist/evidence-attachments.d.ts +19 -0
- package/dist/evidence-attachments.d.ts.map +1 -0
- package/dist/evidence-attachments.js +57 -0
- package/dist/evidence-attachments.js.map +1 -0
- package/dist/extension-localization.d.ts +20 -0
- package/dist/extension-localization.d.ts.map +1 -0
- package/dist/extension-localization.js +94 -0
- package/dist/extension-localization.js.map +1 -0
- package/dist/html-report.d.ts.map +1 -1
- package/dist/html-report.js +2 -1
- package/dist/html-report.js.map +1 -1
- package/dist/icon-preview.js +1 -1
- package/dist/icon-preview.js.map +1 -1
- package/dist/listing-checks.d.ts +9 -1
- package/dist/listing-checks.d.ts.map +1 -1
- package/dist/listing-checks.js +89 -1
- package/dist/listing-checks.js.map +1 -1
- package/dist/localize-init.d.ts +9 -0
- package/dist/localize-init.d.ts.map +1 -0
- package/dist/localize-init.js +166 -0
- package/dist/localize-init.js.map +1 -0
- package/dist/localize.d.ts +27 -0
- package/dist/localize.d.ts.map +1 -0
- package/dist/localize.js +292 -0
- package/dist/localize.js.map +1 -0
- package/dist/pay-contract.d.ts +214 -0
- package/dist/pay-contract.d.ts.map +1 -0
- package/dist/pay-contract.js +202 -0
- package/dist/pay-contract.js.map +1 -0
- package/dist/pay.d.ts +18 -0
- package/dist/pay.d.ts.map +1 -0
- package/dist/pay.js +125 -0
- package/dist/pay.js.map +1 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +69 -8
- package/dist/project.js.map +1 -1
- package/package.json +7 -6
- package/dist/project-workspace.d.ts +0 -1976
- package/dist/project-workspace.d.ts.map +0 -1
- package/dist/project-workspace.js +0 -350
- package/dist/project-workspace.js.map +0 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const MAX_BYTES = 100 * 1024 * 1024;
|
|
4
|
+
const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
5
|
+
const MAX_ENTRIES = 10_000;
|
|
6
|
+
const RESERVED = ['_locales', 'localization.json', 'LOCALIZATION-REPORT.md', '.extenshi-localization.json'];
|
|
7
|
+
function exists(file) {
|
|
8
|
+
try {
|
|
9
|
+
fs.lstatSync(file);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === 'ENOENT')
|
|
14
|
+
return false;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function object(value) {
|
|
19
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
/** Initialize an English manifest in a new copy. HTML/JS extraction requires a separate reviewed step. */
|
|
22
|
+
export function initializeLocalization(inputDirectory, outputDirectory) {
|
|
23
|
+
const input = path.resolve(inputDirectory);
|
|
24
|
+
if (fs.lstatSync(input).isSymbolicLink() || !fs.statSync(input).isDirectory())
|
|
25
|
+
throw new Error('Input must be a directory, not a symlink');
|
|
26
|
+
const source = fs.realpathSync(input);
|
|
27
|
+
const requestedOutput = path.resolve(outputDirectory);
|
|
28
|
+
if (exists(requestedOutput))
|
|
29
|
+
throw new Error('Output directory already exists; choose a new directory');
|
|
30
|
+
// Require the parent to exist, so ownership of the one directory we create is unambiguous.
|
|
31
|
+
const destination = path.join(fs.realpathSync(path.dirname(requestedOutput)), path.basename(requestedOutput));
|
|
32
|
+
if (destination === source || destination.startsWith(`${source}${path.sep}`))
|
|
33
|
+
throw new Error('Output must be outside the source directory');
|
|
34
|
+
for (const relative of RESERVED) {
|
|
35
|
+
if (exists(path.join(source, relative)))
|
|
36
|
+
throw new Error(`Existing ${relative}; use localize prepare for a localized project or choose a clean source`);
|
|
37
|
+
}
|
|
38
|
+
const files = [];
|
|
39
|
+
const directories = [];
|
|
40
|
+
let bytes = 0;
|
|
41
|
+
let entries = 0;
|
|
42
|
+
function walk(relative, depth) {
|
|
43
|
+
if (depth > 64)
|
|
44
|
+
throw new Error('Source exceeds maximum directory depth (64)');
|
|
45
|
+
for (const entry of fs.readdirSync(path.join(source, relative), { withFileTypes: true })) {
|
|
46
|
+
if (++entries > MAX_ENTRIES)
|
|
47
|
+
throw new Error('Source exceeds 10,000 entries');
|
|
48
|
+
const child = path.join(relative, entry.name);
|
|
49
|
+
const stat = fs.lstatSync(path.join(source, child));
|
|
50
|
+
if (stat.isSymbolicLink())
|
|
51
|
+
throw new Error(`Symlinks are not supported: ${child}`);
|
|
52
|
+
if (entry.name === 'node_modules' || entry.name === '.git')
|
|
53
|
+
continue;
|
|
54
|
+
if (stat.isDirectory()) {
|
|
55
|
+
directories.push(child);
|
|
56
|
+
walk(child, depth + 1);
|
|
57
|
+
}
|
|
58
|
+
else if (stat.isFile()) {
|
|
59
|
+
bytes += stat.size;
|
|
60
|
+
if (stat.size > MAX_FILE_BYTES || bytes > MAX_BYTES)
|
|
61
|
+
throw new Error('Source exceeds copy limits (25 MiB per file, 100 MiB total)');
|
|
62
|
+
files.push({ relative: child, size: stat.size });
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
throw new Error(`Unsupported filesystem entry: ${child}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
walk('', 0);
|
|
69
|
+
function readSource(relative) {
|
|
70
|
+
const planned = files.find((file) => file.relative === relative);
|
|
71
|
+
if (!planned)
|
|
72
|
+
throw new Error(`Missing source file: ${relative}`);
|
|
73
|
+
const fd = fs.openSync(path.join(source, relative), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
74
|
+
try {
|
|
75
|
+
const stat = fs.fstatSync(fd);
|
|
76
|
+
if (!stat.isFile() || stat.size !== planned.size)
|
|
77
|
+
throw new Error(`Source changed during copy: ${relative}`);
|
|
78
|
+
return fs.readFileSync(fd);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
fs.closeSync(fd);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if ((files.find((file) => file.relative === 'manifest.json')?.size ?? 0) > 1024 * 1024)
|
|
85
|
+
throw new Error('manifest.json exceeds 1 MiB');
|
|
86
|
+
const manifest = JSON.parse(readSource('manifest.json').toString('utf8'));
|
|
87
|
+
if (!object(manifest))
|
|
88
|
+
throw new Error('manifest.json must be an object');
|
|
89
|
+
if ('default_locale' in manifest)
|
|
90
|
+
throw new Error('Existing default_locale; use localize prepare');
|
|
91
|
+
if (typeof manifest.name !== 'string' || !manifest.name.trim())
|
|
92
|
+
throw new Error('Manifest name must be a non-empty literal string');
|
|
93
|
+
const originalName = manifest.name;
|
|
94
|
+
const messages = {};
|
|
95
|
+
const extractedFields = [];
|
|
96
|
+
function extract(target, field, key, description, fieldPath = field) {
|
|
97
|
+
if (!(field in target))
|
|
98
|
+
return;
|
|
99
|
+
const text = target[field];
|
|
100
|
+
if (typeof text !== 'string' || !text.trim() || /__MSG_/i.test(text))
|
|
101
|
+
throw new Error(`${fieldPath} must be a non-empty literal string`);
|
|
102
|
+
messages[key] = { message: text.replaceAll('$', () => '$$'), description };
|
|
103
|
+
target[field] = `__MSG_${key}__`;
|
|
104
|
+
extractedFields.push(fieldPath);
|
|
105
|
+
}
|
|
106
|
+
extract(manifest, 'name', 'extensionName', 'Extension display name; preserve brand spelling.');
|
|
107
|
+
extract(manifest, 'description', 'extensionDescription', 'Manifest description of the actual extension features.');
|
|
108
|
+
extract(manifest, 'short_name', 'extensionShortName', 'Short extension display name for constrained UI.');
|
|
109
|
+
for (const [field, key] of [
|
|
110
|
+
['action', 'extensionActionTitle'],
|
|
111
|
+
['browser_action', 'extensionBrowserActionTitle'],
|
|
112
|
+
['page_action', 'extensionPageActionTitle'],
|
|
113
|
+
['sidebar_action', 'extensionSidebarActionTitle'],
|
|
114
|
+
]) {
|
|
115
|
+
if (!(field in manifest))
|
|
116
|
+
continue;
|
|
117
|
+
const action = manifest[field];
|
|
118
|
+
if (!object(action))
|
|
119
|
+
throw new Error(`Manifest ${field} must be an object`);
|
|
120
|
+
extract(action, 'default_title', key, `${field} tooltip or sidebar title.`, `${field}.default_title`);
|
|
121
|
+
}
|
|
122
|
+
manifest.default_locale = 'en';
|
|
123
|
+
const untouchedFiles = files
|
|
124
|
+
.filter(({ relative }) => /\.(?:html?|[cm]?jsx?|tsx?|vue|svelte)$/i.test(relative))
|
|
125
|
+
.map(({ relative }) => relative);
|
|
126
|
+
const report = [
|
|
127
|
+
'# Localization initialization report',
|
|
128
|
+
'',
|
|
129
|
+
'Scope: manifest-only initialization. Source copy is assumed to be English; verify this before translation.',
|
|
130
|
+
'HTML, JavaScript and framework templates remain byte-for-byte unchanged. Safe parser-based extraction and runtime wiring require a separate reviewed implementation.',
|
|
131
|
+
'',
|
|
132
|
+
'## Extracted manifest fields',
|
|
133
|
+
...extractedFields.map((field) => `- ${field}`),
|
|
134
|
+
'',
|
|
135
|
+
'## Source files requiring manual inspection',
|
|
136
|
+
...(untouchedFiles.length
|
|
137
|
+
? untouchedFiles.map((file) => `- ${JSON.stringify(file)}: extraction unsupported; review visible strings and runtime i18n wiring.`)
|
|
138
|
+
: ['No HTML/JS/template files were identified; inspect other source formats manually.']),
|
|
139
|
+
'',
|
|
140
|
+
'## Next steps',
|
|
141
|
+
'- Review the copied manifest and English messages. Configure targetLocales in localization.json or pass --lang to localize prepare.',
|
|
142
|
+
'- Review protectedTerms: the original extension name is protected by default.',
|
|
143
|
+
'- Translate and validate with localize prepare/apply/check. Review language quality, listing risk and RTL layout in each packaged browser build.',
|
|
144
|
+
'- node_modules and .git directories are excluded from this copy. Reinstall build dependencies when needed.',
|
|
145
|
+
'',
|
|
146
|
+
].join('\n');
|
|
147
|
+
fs.mkdirSync(destination);
|
|
148
|
+
try {
|
|
149
|
+
for (const directory of directories)
|
|
150
|
+
fs.mkdirSync(path.join(destination, directory), { recursive: true });
|
|
151
|
+
for (const { relative } of files)
|
|
152
|
+
fs.writeFileSync(path.join(destination, relative), readSource(relative), { flag: 'wx' });
|
|
153
|
+
fs.mkdirSync(path.join(destination, '_locales', 'en'), { recursive: true });
|
|
154
|
+
const writeJson = (relative, value) => fs.writeFileSync(path.join(destination, relative), `${JSON.stringify(value, null, '\t')}\n`);
|
|
155
|
+
writeJson('manifest.json', manifest);
|
|
156
|
+
writeJson('_locales/en/messages.json', messages);
|
|
157
|
+
writeJson('localization.json', { sourceLocale: 'en', targetLocales: [], protectedTerms: [originalName] });
|
|
158
|
+
fs.writeFileSync(path.join(destination, 'LOCALIZATION-REPORT.md'), report, { flag: 'wx' });
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return { outputDirectory: destination, extractedFields, untouchedFiles, copiedFiles: files.length };
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=localize-init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localize-init.js","sourceRoot":"","sources":["../src/localize-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type MessageCatalog } from './extension-localization.js';
|
|
2
|
+
interface LocaleTranslation {
|
|
3
|
+
removeKeys?: string[];
|
|
4
|
+
reviewed?: boolean;
|
|
5
|
+
sourceHashes: Record<string, string>;
|
|
6
|
+
messages: MessageCatalog;
|
|
7
|
+
}
|
|
8
|
+
export interface LocalizationBundle {
|
|
9
|
+
version: 1;
|
|
10
|
+
sourceLocale: string;
|
|
11
|
+
locales: Record<string, LocaleTranslation>;
|
|
12
|
+
}
|
|
13
|
+
export declare function prepareLocalization(directory: string, options: {
|
|
14
|
+
lang?: string;
|
|
15
|
+
output: string;
|
|
16
|
+
protect?: string;
|
|
17
|
+
}): LocalizationBundle;
|
|
18
|
+
export declare function applyLocalization(directory: string, translations: unknown, protect?: string): {
|
|
19
|
+
locales: string[];
|
|
20
|
+
keys: number;
|
|
21
|
+
};
|
|
22
|
+
export declare function checkLocalization(directory: string, protect?: string): {
|
|
23
|
+
locales: string[];
|
|
24
|
+
errors: string[];
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=localize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localize.d.ts","sourceRoot":"","sources":["../src/localize.ts"],"names":[],"mappings":"AAGA,OAAO,EAGN,KAAK,cAAc,EAEnB,MAAM,6BAA6B,CAAA;AAIpC,UAAU,iBAAiB;IAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,QAAQ,EAAE,cAAc,CAAA;CACxB;AACD,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,CAAC,CAAA;IACV,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CAC1C;AAmHD,wBAAgB,mBAAmB,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,kBAAkB,CAsCpB;AA6ED,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA4DrC;AACD,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA0BzC"}
|
package/dist/localize.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { canonicalSerialize, EXTENSION_LOCALES, validateMessageCatalog, } from './extension-localization.js';
|
|
5
|
+
import { listingFromArtifact } from './listing.js';
|
|
6
|
+
import { runListingOnlyChecks } from './review-checks.js';
|
|
7
|
+
const STATE = '.extenshi-localization.json';
|
|
8
|
+
function locale(value) {
|
|
9
|
+
if (typeof value !== 'string' || !EXTENSION_LOCALES.includes(value))
|
|
10
|
+
throw new Error(`Unsupported extension locale: ${String(value)}`);
|
|
11
|
+
}
|
|
12
|
+
function object(value) {
|
|
13
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
/** Reject symlink components, including dangling links, before reading or replacing workspace files. */
|
|
16
|
+
function safePath(root, relative) {
|
|
17
|
+
const result = path.resolve(root, relative);
|
|
18
|
+
if (!result.startsWith(`${root}${path.sep}`))
|
|
19
|
+
throw new Error('Path must stay inside extension directory');
|
|
20
|
+
let current = root;
|
|
21
|
+
for (const part of path.relative(root, result).split(path.sep)) {
|
|
22
|
+
current = path.join(current, part);
|
|
23
|
+
try {
|
|
24
|
+
if (fs.lstatSync(current).isSymbolicLink())
|
|
25
|
+
throw new Error(`Symlinks are not supported: ${current}`);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code !== 'ENOENT')
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function read(root, relative, fallback) {
|
|
35
|
+
const file = safePath(root, relative);
|
|
36
|
+
if (!fs.existsSync(file) && fallback !== undefined)
|
|
37
|
+
return fallback;
|
|
38
|
+
if (fs.statSync(file).size > 10 * 1024 * 1024)
|
|
39
|
+
throw new Error(`Localization file exceeds 10 MB: ${relative}`);
|
|
40
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
function catalogPath(code) {
|
|
43
|
+
locale(code);
|
|
44
|
+
return `_locales/${code}/messages.json`;
|
|
45
|
+
}
|
|
46
|
+
function hash(value) {
|
|
47
|
+
return createHash('sha256').update(canonicalSerialize(value)).digest('hex');
|
|
48
|
+
}
|
|
49
|
+
function assertValid(errors) {
|
|
50
|
+
if (errors.length)
|
|
51
|
+
throw new Error(errors.join('\n'));
|
|
52
|
+
}
|
|
53
|
+
function context(directory, protect) {
|
|
54
|
+
const root = fs.realpathSync(directory);
|
|
55
|
+
const manifest = read(root, 'manifest.json');
|
|
56
|
+
locale(manifest.default_locale);
|
|
57
|
+
const sourceLocale = manifest.default_locale;
|
|
58
|
+
const source = read(root, catalogPath(sourceLocale));
|
|
59
|
+
assertValid(validateMessageCatalog(source, source));
|
|
60
|
+
const config = read(root, 'localization.json', {});
|
|
61
|
+
if (config.sourceLocale && config.sourceLocale !== sourceLocale)
|
|
62
|
+
throw new Error('localization.json sourceLocale differs from manifest default_locale');
|
|
63
|
+
if (config.targetLocales &&
|
|
64
|
+
(!Array.isArray(config.targetLocales) || config.targetLocales.some((value) => typeof value !== 'string')))
|
|
65
|
+
throw new Error('Invalid targetLocales configuration');
|
|
66
|
+
if (config.protectedTerms &&
|
|
67
|
+
(!Array.isArray(config.protectedTerms) ||
|
|
68
|
+
config.protectedTerms.some((value) => typeof value !== 'string')))
|
|
69
|
+
throw new Error('Invalid protectedTerms configuration');
|
|
70
|
+
const protectedTerms = [...(config.protectedTerms ?? []), ...(protect?.split(',').filter(Boolean) ?? [])];
|
|
71
|
+
const state = read(root, STATE, { version: 1, locales: {} });
|
|
72
|
+
if (!object(state) || state.version !== 1 || !object(state.locales))
|
|
73
|
+
throw new Error('Invalid localization state');
|
|
74
|
+
if (state.targetHashes !== undefined && !object(state.targetHashes))
|
|
75
|
+
throw new Error('Invalid localization target hashes');
|
|
76
|
+
for (const [code, hashes] of [
|
|
77
|
+
...Object.entries(state.locales),
|
|
78
|
+
...Object.entries(state.targetHashes ?? {}),
|
|
79
|
+
]) {
|
|
80
|
+
locale(code);
|
|
81
|
+
if (!object(hashes) || Object.values(hashes).some((value) => typeof value !== 'string'))
|
|
82
|
+
throw new Error('Invalid localization state hashes');
|
|
83
|
+
}
|
|
84
|
+
return { root, manifest, sourceLocale, source, config, protectedTerms, state };
|
|
85
|
+
}
|
|
86
|
+
/** Stage every file first and restore originals if a rename fails. */
|
|
87
|
+
function writeBatch(root, files) {
|
|
88
|
+
const staged = [];
|
|
89
|
+
const written = [];
|
|
90
|
+
try {
|
|
91
|
+
for (const [relative, value] of Object.entries(files)) {
|
|
92
|
+
const file = safePath(root, relative);
|
|
93
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
94
|
+
const temp = `${file}.${randomUUID()}.tmp`;
|
|
95
|
+
const original = fs.existsSync(file) ? fs.readFileSync(file) : null;
|
|
96
|
+
staged.push({ file, temp, original });
|
|
97
|
+
fs.writeFileSync(temp, `${JSON.stringify(value, null, '\t')}\n`, { flag: 'wx' });
|
|
98
|
+
}
|
|
99
|
+
for (const item of staged) {
|
|
100
|
+
safePath(root, path.relative(root, item.file));
|
|
101
|
+
fs.renameSync(item.temp, item.file);
|
|
102
|
+
written.push(item);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
for (const item of written.reverse()) {
|
|
107
|
+
if (item.original)
|
|
108
|
+
fs.writeFileSync(item.file, item.original);
|
|
109
|
+
else
|
|
110
|
+
fs.rmSync(item.file, { force: true });
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
for (const item of staged)
|
|
116
|
+
fs.rmSync(item.temp, { force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function prepareLocalization(directory, options) {
|
|
120
|
+
const ctx = context(directory, options.protect);
|
|
121
|
+
const targets = [
|
|
122
|
+
...new Set(options.lang ? options.lang.split(',').map((code) => code.trim()) : (ctx.config.targetLocales ?? [])),
|
|
123
|
+
];
|
|
124
|
+
if (!targets.length)
|
|
125
|
+
throw new Error('Specify --lang fr,de,es or targetLocales in localization.json');
|
|
126
|
+
const bundle = { version: 1, sourceLocale: ctx.sourceLocale, locales: {} };
|
|
127
|
+
for (const code of targets) {
|
|
128
|
+
locale(code);
|
|
129
|
+
if (code === ctx.sourceLocale)
|
|
130
|
+
throw new Error('Target locale cannot be the default locale');
|
|
131
|
+
const existing = read(ctx.root, catalogPath(code), {});
|
|
132
|
+
assertValid(validateMessageCatalog(existing, existing));
|
|
133
|
+
const pending = Object.keys(ctx.source).filter((key) => !existing[key] ||
|
|
134
|
+
ctx.state.locales[code]?.[key] !== hash(ctx.source[key]) ||
|
|
135
|
+
ctx.state.targetHashes?.[code]?.[key] !== hash(existing[key]));
|
|
136
|
+
const removeKeys = Object.keys(existing).filter((key) => !Object.hasOwn(ctx.source, key));
|
|
137
|
+
if (pending.length || removeKeys.length)
|
|
138
|
+
bundle.locales[code] = {
|
|
139
|
+
removeKeys,
|
|
140
|
+
sourceHashes: Object.fromEntries(pending.map((key) => [key, hash(ctx.source[key])])),
|
|
141
|
+
messages: Object.fromEntries(pending.map((key) => [key, ctx.source[key]])),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
fs.mkdirSync(options.output, { recursive: true });
|
|
145
|
+
const output = fs.realpathSync(options.output);
|
|
146
|
+
const file = safePath(output, 'localization-request.json');
|
|
147
|
+
// Request files must never overwrite work a translator already started.
|
|
148
|
+
fs.writeFileSync(file, `${JSON.stringify({ ...bundle, instructions: 'REQUEST ONLY: translate messages for each locale; preserve sourceHashes, placeholders, dollar escapes and protected terms. Review every requested key, then add reviewed: true to each locale before apply.', protectedTerms: ctx.protectedTerms }, null, '\t')}\n`, { flag: 'wx' });
|
|
149
|
+
return bundle;
|
|
150
|
+
}
|
|
151
|
+
function listingGate(manifest, sourceLocale, source, target) {
|
|
152
|
+
const errors = [];
|
|
153
|
+
const resolvedManifest = (messages) => {
|
|
154
|
+
const resolved = { ...manifest };
|
|
155
|
+
for (const field of ['name', 'description']) {
|
|
156
|
+
const raw = manifest[field];
|
|
157
|
+
if (typeof raw !== 'string')
|
|
158
|
+
continue;
|
|
159
|
+
const match = /^__MSG_([A-Za-z0-9_]+)__$/.exec(raw);
|
|
160
|
+
if (!match)
|
|
161
|
+
continue;
|
|
162
|
+
const key = Object.keys(messages).find((item) => item.toLowerCase() === match[1].toLowerCase());
|
|
163
|
+
if (!key) {
|
|
164
|
+
errors.push(`Manifest ${field}: missing message ${match[1]}`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const entry = messages[key];
|
|
168
|
+
const expandContent = (content) => content.replace(/\${2,}|\$[1-9]/g, (token) => {
|
|
169
|
+
if (token.startsWith('$$'))
|
|
170
|
+
return token.slice(1);
|
|
171
|
+
errors.push(`Manifest ${field}: positional substitutions require runtime arguments; use static text`);
|
|
172
|
+
return '';
|
|
173
|
+
});
|
|
174
|
+
resolved[field] = entry.message.replace(/\${2,}|\$[A-Za-z0-9_]+\$|\$[1-9]/g, (token) => {
|
|
175
|
+
if (token.startsWith('$$'))
|
|
176
|
+
return token.slice(1);
|
|
177
|
+
if (/^\$[1-9]$/.test(token))
|
|
178
|
+
return expandContent(token);
|
|
179
|
+
const name = Object.keys(entry.placeholders ?? {}).find((item) => item.toLowerCase() === token.slice(1, -1).toLowerCase());
|
|
180
|
+
return name ? expandContent(entry.placeholders?.[name]?.content ?? '') : token;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return resolved;
|
|
184
|
+
};
|
|
185
|
+
const originalManifest = resolvedManifest(source);
|
|
186
|
+
const translatedManifest = resolvedManifest(target);
|
|
187
|
+
const findings = (messages) => runListingOnlyChecks(listingFromArtifact(messages === source ? originalManifest : translatedManifest, new Map([[catalogPath(sourceLocale), JSON.stringify(messages)]])));
|
|
188
|
+
// Preserve matched claim/pattern evidence too: a baseline ranking claim must
|
|
189
|
+
// never hide a new guarantee or a different field's finding.
|
|
190
|
+
const identity = (finding) => {
|
|
191
|
+
const evidence = (finding.evidence ?? '').replace(/^\[[^\]]+\]\s*/, '');
|
|
192
|
+
const field = evidence.split(':')[0].trim();
|
|
193
|
+
const pattern = evidence.includes(' — ') ? evidence.split(' — ').slice(1).join(' — ') : '';
|
|
194
|
+
return `${finding.check}:${finding.title}:${field}:${pattern}`;
|
|
195
|
+
};
|
|
196
|
+
const baseline = new Map();
|
|
197
|
+
for (const finding of findings(source))
|
|
198
|
+
baseline.set(identity(finding), (baseline.get(identity(finding)) ?? 0) + 1);
|
|
199
|
+
// Store limits apply to visible text after placeholder and escape expansion.
|
|
200
|
+
// https://developer.chrome.com/docs/extensions/reference/manifest
|
|
201
|
+
for (const [field, limit] of [
|
|
202
|
+
['name', 75],
|
|
203
|
+
['description', 132],
|
|
204
|
+
]) {
|
|
205
|
+
const message = translatedManifest[field];
|
|
206
|
+
if (typeof message === 'string' && message.length > limit)
|
|
207
|
+
errors.push(`Manifest ${field}: translation exceeds ${limit} characters`);
|
|
208
|
+
}
|
|
209
|
+
for (const finding of findings(target)) {
|
|
210
|
+
const remaining = baseline.get(identity(finding)) ?? 0;
|
|
211
|
+
if (remaining)
|
|
212
|
+
baseline.set(identity(finding), remaining - 1);
|
|
213
|
+
else
|
|
214
|
+
errors.push(`Translation introduces listing risk ${finding.check}: ${finding.title}`);
|
|
215
|
+
}
|
|
216
|
+
return errors;
|
|
217
|
+
}
|
|
218
|
+
export function applyLocalization(directory, translations, protect) {
|
|
219
|
+
const ctx = context(directory, protect);
|
|
220
|
+
if (!object(translations) ||
|
|
221
|
+
translations.version !== 1 ||
|
|
222
|
+
translations.sourceLocale !== ctx.sourceLocale ||
|
|
223
|
+
!object(translations.locales))
|
|
224
|
+
throw new Error('Invalid translation bundle or source locale');
|
|
225
|
+
const files = {};
|
|
226
|
+
let keys = 0;
|
|
227
|
+
for (const [code, value] of Object.entries(translations.locales)) {
|
|
228
|
+
locale(code);
|
|
229
|
+
if (code === ctx.sourceLocale)
|
|
230
|
+
throw new Error('Cannot replace the default locale');
|
|
231
|
+
if (!object(value) || value.reviewed !== true || !object(value.sourceHashes) || !object(value.messages))
|
|
232
|
+
throw new Error(`Invalid translation bundle for ${code}; set reviewed: true after reviewing each requested translation`);
|
|
233
|
+
assertValid(validateMessageCatalog(ctx.source, value.messages, {
|
|
234
|
+
allowMissing: true,
|
|
235
|
+
protectedTerms: ctx.protectedTerms,
|
|
236
|
+
}));
|
|
237
|
+
if (Object.keys(value.sourceHashes).length !== Object.keys(value.messages).length)
|
|
238
|
+
throw new Error(`${code}: sourceHashes must match translated keys`);
|
|
239
|
+
for (const key of Object.keys(value.messages))
|
|
240
|
+
if (value.sourceHashes[key] !== hash(ctx.source[key]))
|
|
241
|
+
throw new Error(`${code}.${key}: source changed; prepare translations again`);
|
|
242
|
+
const existing = read(ctx.root, catalogPath(code), {});
|
|
243
|
+
const removeKeys = value.removeKeys ?? [];
|
|
244
|
+
if (!Array.isArray(removeKeys) ||
|
|
245
|
+
removeKeys.some((key) => typeof key !== 'string' || Object.hasOwn(ctx.source, key) || !Object.hasOwn(existing, key)))
|
|
246
|
+
throw new Error(`${code}: invalid or stale removed keys; prepare translations again`);
|
|
247
|
+
const merged = { ...existing, ...value.messages };
|
|
248
|
+
for (const key of removeKeys)
|
|
249
|
+
delete merged[key];
|
|
250
|
+
assertValid(validateMessageCatalog(ctx.source, merged, { protectedTerms: ctx.protectedTerms }));
|
|
251
|
+
assertValid(listingGate(ctx.manifest, ctx.sourceLocale, ctx.source, merged));
|
|
252
|
+
files[catalogPath(code)] = merged;
|
|
253
|
+
ctx.state.locales[code] = { ...ctx.state.locales[code], ...value.sourceHashes };
|
|
254
|
+
ctx.state.targetHashes ??= {};
|
|
255
|
+
ctx.state.targetHashes[code] = {
|
|
256
|
+
...ctx.state.targetHashes[code],
|
|
257
|
+
...Object.fromEntries(Object.entries(value.messages).map(([key, message]) => [key, hash(message)])),
|
|
258
|
+
};
|
|
259
|
+
for (const key of removeKeys) {
|
|
260
|
+
delete ctx.state.locales[code][key];
|
|
261
|
+
delete ctx.state.targetHashes[code][key];
|
|
262
|
+
}
|
|
263
|
+
keys += Object.keys(value.messages).length;
|
|
264
|
+
}
|
|
265
|
+
if (Object.keys(files).length) {
|
|
266
|
+
files[STATE] = ctx.state;
|
|
267
|
+
writeBatch(ctx.root, files);
|
|
268
|
+
}
|
|
269
|
+
return { locales: Object.keys(translations.locales), keys };
|
|
270
|
+
}
|
|
271
|
+
export function checkLocalization(directory, protect) {
|
|
272
|
+
const ctx = context(directory, protect);
|
|
273
|
+
const locales = [
|
|
274
|
+
...new Set([...fs.readdirSync(safePath(ctx.root, '_locales')), ...(ctx.config.targetLocales ?? [])]),
|
|
275
|
+
].filter((code) => code !== ctx.sourceLocale);
|
|
276
|
+
const errors = [];
|
|
277
|
+
for (const code of locales) {
|
|
278
|
+
locale(code);
|
|
279
|
+
const target = read(ctx.root, catalogPath(code), {});
|
|
280
|
+
const invalid = validateMessageCatalog(ctx.source, target, { protectedTerms: ctx.protectedTerms });
|
|
281
|
+
errors.push(...invalid.map((error) => `${code}: ${error}`));
|
|
282
|
+
if (!invalid.length)
|
|
283
|
+
errors.push(...listingGate(ctx.manifest, ctx.sourceLocale, ctx.source, target).map((error) => `${code}: ${error}`));
|
|
284
|
+
for (const key of Object.keys(ctx.source))
|
|
285
|
+
if (ctx.state.locales[code]?.[key] !== hash(ctx.source[key]) ||
|
|
286
|
+
!target[key] ||
|
|
287
|
+
ctx.state.targetHashes?.[code]?.[key] !== hash(target[key]))
|
|
288
|
+
errors.push(`${code}.${key}: unreviewed or stale translation; run localize prepare`);
|
|
289
|
+
}
|
|
290
|
+
return { locales, errors };
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=localize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localize.js","sourceRoot":"","sources":["../src/localize.ts"],"names":[],"mappings":"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"}
|