browser-extension-manager 1.2.12 → 1.2.14
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/dist/config/manifest.json +1 -1
- package/dist/gulp/config/locales.js +30 -0
- package/dist/gulp/tasks/audit.js +67 -3
- package/dist/gulp/tasks/package.js +80 -44
- package/dist/gulp/tasks/publish.js +24 -12
- package/dist/gulp/tasks/translate.js +14 -46
- package/dist/gulp/templates/BUILD_INSTRUCTIONS.md +11 -4
- package/package.json +1 -1
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
// IDs
|
|
91
91
|
browser_specific_settings: {
|
|
92
92
|
gecko: {
|
|
93
|
-
id: 'my-addon@example.com',
|
|
93
|
+
// id: 'my-addon@example.com',
|
|
94
94
|
strict_min_version: '91.0',
|
|
95
95
|
// Required for new Firefox extensions as of Nov 2025
|
|
96
96
|
// https://blog.mozilla.org/addons/2025/10/23/data-collection-consent-changes-for-new-firefox-extensions/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Locale field limits (Chrome Web Store requirements)
|
|
2
|
+
// https://developer.chrome.com/docs/webstore/i18n
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Field character limits
|
|
5
|
+
limits: {
|
|
6
|
+
appName: 50,
|
|
7
|
+
appNameShort: 25,
|
|
8
|
+
appDescription: 200,
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
// Languages to translate (code: name)
|
|
12
|
+
languages: {
|
|
13
|
+
zh: 'Chinese (Simplified)',
|
|
14
|
+
es: 'Spanish',
|
|
15
|
+
hi: 'Hindi',
|
|
16
|
+
ar: 'Arabic',
|
|
17
|
+
pt: 'Portuguese',
|
|
18
|
+
ru: 'Russian',
|
|
19
|
+
ja: 'Japanese',
|
|
20
|
+
de: 'German',
|
|
21
|
+
fr: 'French',
|
|
22
|
+
ko: 'Korean',
|
|
23
|
+
ur: 'Urdu',
|
|
24
|
+
id: 'Indonesian',
|
|
25
|
+
bn: 'Bengali',
|
|
26
|
+
tl: 'Tagalog/Filipino',
|
|
27
|
+
vi: 'Vietnamese',
|
|
28
|
+
it: 'Italian',
|
|
29
|
+
},
|
|
30
|
+
};
|
package/dist/gulp/tasks/audit.js
CHANGED
|
@@ -15,6 +15,7 @@ const rootPathProject = Manager.getRootPath('project');
|
|
|
15
15
|
// Audit results tracker
|
|
16
16
|
const auditResults = {
|
|
17
17
|
externalScripts: [],
|
|
18
|
+
localeWarnings: [],
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
// Patterns to detect external script references
|
|
@@ -38,6 +39,44 @@ const EXTERNAL_SCRIPT_PATTERNS = [
|
|
|
38
39
|
/["'](https?:\/\/(?:www\.)?(?:google\.com\/recaptcha|apis\.google\.com)[^"']*)["']/gi,
|
|
39
40
|
];
|
|
40
41
|
|
|
42
|
+
// Locale config (shared with translate.js)
|
|
43
|
+
const { limits: LOCALE_LIMITS } = require('../config/locales.js');
|
|
44
|
+
|
|
45
|
+
// Check locale files for warnings
|
|
46
|
+
function checkLocaleFiles(packagedDir) {
|
|
47
|
+
const localesDir = path.join(packagedDir, '_locales');
|
|
48
|
+
|
|
49
|
+
if (!jetpack.exists(localesDir)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const localeFiles = jetpack.find(localesDir, { matching: '*/messages.json' });
|
|
54
|
+
|
|
55
|
+
localeFiles.forEach(filePath => {
|
|
56
|
+
try {
|
|
57
|
+
const content = jetpack.read(filePath);
|
|
58
|
+
const messages = JSON.parse(content);
|
|
59
|
+
|
|
60
|
+
// Check each field against its limit
|
|
61
|
+
Object.entries(LOCALE_LIMITS).forEach(([field, limit]) => {
|
|
62
|
+
const message = messages[field]?.message;
|
|
63
|
+
|
|
64
|
+
if (message && message.length > limit) {
|
|
65
|
+
auditResults.localeWarnings.push({
|
|
66
|
+
file: path.relative(rootPathProject, filePath),
|
|
67
|
+
field: field,
|
|
68
|
+
length: message.length,
|
|
69
|
+
limit: limit,
|
|
70
|
+
value: message,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
} catch (e) {
|
|
75
|
+
logger.warn(`Error parsing locale file ${filePath}: ${e.message}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
41
80
|
// Check a single file for external script references
|
|
42
81
|
function checkFileForExternalScripts(filePath) {
|
|
43
82
|
try {
|
|
@@ -86,10 +125,11 @@ async function auditFn(complete) {
|
|
|
86
125
|
|
|
87
126
|
// Reset results
|
|
88
127
|
auditResults.externalScripts = [];
|
|
128
|
+
auditResults.localeWarnings = [];
|
|
89
129
|
|
|
90
130
|
try {
|
|
91
|
-
//
|
|
92
|
-
const packagedDir = path.join(rootPathProject, 'packaged', 'raw');
|
|
131
|
+
// Audit chromium build (code is same for both targets, only manifest differs)
|
|
132
|
+
const packagedDir = path.join(rootPathProject, 'packaged', 'chromium', 'raw');
|
|
93
133
|
|
|
94
134
|
if (!jetpack.exists(packagedDir)) {
|
|
95
135
|
logger.log(chalk.yellow('⚠️ Packaged directory not found. Run package task first.'));
|
|
@@ -100,7 +140,7 @@ async function auditFn(complete) {
|
|
|
100
140
|
|
|
101
141
|
logger.log(`Auditing ${files.length} files (JS, HTML)...`);
|
|
102
142
|
|
|
103
|
-
// Check each file
|
|
143
|
+
// Check each file for external scripts
|
|
104
144
|
files.forEach(filePath => {
|
|
105
145
|
const externalScripts = checkFileForExternalScripts(filePath);
|
|
106
146
|
if (externalScripts.length > 0) {
|
|
@@ -108,6 +148,9 @@ async function auditFn(complete) {
|
|
|
108
148
|
}
|
|
109
149
|
});
|
|
110
150
|
|
|
151
|
+
// Check locale files
|
|
152
|
+
checkLocaleFiles(packagedDir);
|
|
153
|
+
|
|
111
154
|
// Display results
|
|
112
155
|
displayAuditResults();
|
|
113
156
|
|
|
@@ -142,6 +185,23 @@ function displayAuditResults() {
|
|
|
142
185
|
console.log(chalk.green('✅ No external scripts detected'));
|
|
143
186
|
}
|
|
144
187
|
|
|
188
|
+
console.log('');
|
|
189
|
+
|
|
190
|
+
// Locale Warnings
|
|
191
|
+
if (auditResults.localeWarnings.length > 0) {
|
|
192
|
+
console.log(chalk.yellow.bold('⚠️ LOCALE FIELD LENGTH WARNINGS'));
|
|
193
|
+
console.log(chalk.gray('Some locale fields exceed recommended character limits.\n'));
|
|
194
|
+
|
|
195
|
+
auditResults.localeWarnings.forEach((item, index) => {
|
|
196
|
+
console.log(chalk.yellow(` ${index + 1}. ${item.file}`));
|
|
197
|
+
console.log(chalk.gray(` Field: ${item.field} (${item.length}/${item.limit} chars)`));
|
|
198
|
+
console.log(chalk.gray(` Value: "${item.value}"`));
|
|
199
|
+
console.log('');
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
console.log(chalk.green('✅ All locale fields within limits'));
|
|
203
|
+
}
|
|
204
|
+
|
|
145
205
|
// Summary
|
|
146
206
|
console.log(chalk.bold('\n───────────────────────────────────────────────────'));
|
|
147
207
|
console.log(chalk.bold('SUMMARY'));
|
|
@@ -151,6 +211,10 @@ function displayAuditResults() {
|
|
|
151
211
|
const externalScriptColor = externalScriptCount > 0 ? chalk.red : chalk.green;
|
|
152
212
|
console.log(externalScriptColor(`External Scripts: ${externalScriptCount}`));
|
|
153
213
|
|
|
214
|
+
const localeWarningCount = auditResults.localeWarnings.length;
|
|
215
|
+
const localeWarningColor = localeWarningCount > 0 ? chalk.yellow : chalk.green;
|
|
216
|
+
console.log(localeWarningColor(`Locale Warnings: ${localeWarningCount}`));
|
|
217
|
+
|
|
154
218
|
console.log(chalk.bold('═══════════════════════════════════════════════════\n'));
|
|
155
219
|
}
|
|
156
220
|
|
|
@@ -152,8 +152,11 @@ function getPackageVersion(packageName) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// Build targets
|
|
156
|
+
const TARGETS = ['chromium', 'firefox'];
|
|
157
|
+
|
|
155
158
|
// Special Compilation Task for manifest.json with default settings
|
|
156
|
-
async function compileManifest(outputDir) {
|
|
159
|
+
async function compileManifest(outputDir, target) {
|
|
157
160
|
try {
|
|
158
161
|
const manifestPath = path.join('dist', 'manifest.json');
|
|
159
162
|
const outputPath = path.join(outputDir, 'manifest.json');
|
|
@@ -189,10 +192,27 @@ async function compileManifest(outputDir) {
|
|
|
189
192
|
// Add package version to manifest
|
|
190
193
|
manifest.version = project.version;
|
|
191
194
|
|
|
195
|
+
// Apply target-specific manifest adjustments
|
|
196
|
+
if (manifest.background) {
|
|
197
|
+
if (target === 'chromium') {
|
|
198
|
+
// Chrome/Edge uses service_worker only
|
|
199
|
+
delete manifest.background.scripts;
|
|
200
|
+
} else if (target === 'firefox') {
|
|
201
|
+
// Firefox uses scripts array only
|
|
202
|
+
if (manifest.background.service_worker) {
|
|
203
|
+
// Ensure scripts array exists (derive from service_worker if needed)
|
|
204
|
+
if (!manifest.background.scripts) {
|
|
205
|
+
manifest.background.scripts = [manifest.background.service_worker];
|
|
206
|
+
}
|
|
207
|
+
delete manifest.background.service_worker;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
192
212
|
// Save as regular JSON
|
|
193
213
|
jetpack.write(outputPath, JSON.stringify(manifest, null, 2));
|
|
194
214
|
|
|
195
|
-
logger.log(`Manifest compiled with defaults`);
|
|
215
|
+
logger.log(`[${target}] Manifest compiled with defaults`);
|
|
196
216
|
} catch (e) {
|
|
197
217
|
logger.error(`Error compiling manifest`, e);
|
|
198
218
|
}
|
|
@@ -225,76 +245,92 @@ async function compileLocales(outputDir) {
|
|
|
225
245
|
}
|
|
226
246
|
}
|
|
227
247
|
|
|
228
|
-
// Package Task for raw
|
|
248
|
+
// Package Task for raw - creates both chromium and firefox builds
|
|
229
249
|
async function packageRaw() {
|
|
230
250
|
// Log
|
|
231
251
|
logger.log(`Starting raw packaging...`);
|
|
232
252
|
|
|
233
253
|
try {
|
|
234
|
-
|
|
254
|
+
// Build for each target
|
|
255
|
+
for (const target of TARGETS) {
|
|
256
|
+
await packageRawForTarget(target);
|
|
257
|
+
}
|
|
235
258
|
|
|
236
|
-
//
|
|
237
|
-
|
|
259
|
+
// Log completion
|
|
260
|
+
logger.log(`Finished raw packaging for all targets`);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
logger.error(`Error during raw packaging`, e);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Package raw for a specific target (chromium or firefox)
|
|
267
|
+
async function packageRawForTarget(target) {
|
|
268
|
+
logger.log(`[${target}] Starting raw packaging...`);
|
|
269
|
+
|
|
270
|
+
const outputDir = `packaged/${target}/raw`;
|
|
238
271
|
|
|
239
|
-
|
|
240
|
-
|
|
272
|
+
// Ensure the directory exists (this also cleans it)
|
|
273
|
+
jetpack.dir(outputDir, { empty: true });
|
|
241
274
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const redactions = getRedactions();
|
|
275
|
+
// Copy files to raw package directory
|
|
276
|
+
await execute(`cp -r dist/* ${outputDir}`);
|
|
245
277
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
278
|
+
// Loop thru outputDir/assets/js all JS files for redactions
|
|
279
|
+
const jsFiles = jetpack.find(path.join(outputDir, 'assets', 'js'), { matching: '*.js' });
|
|
280
|
+
const redactions = getRedactions();
|
|
249
281
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const regex = new RegExp(key, 'g'); // Create a global regex for the key
|
|
254
|
-
content = content.replace(regex, value);
|
|
282
|
+
jsFiles.forEach(filePath => {
|
|
283
|
+
// Load the content
|
|
284
|
+
let content = jetpack.read(filePath);
|
|
255
285
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
286
|
+
// Replace keys with their corresponding values
|
|
287
|
+
Object.keys(redactions).forEach(key => {
|
|
288
|
+
const value = redactions[key];
|
|
289
|
+
const regex = new RegExp(key, 'g'); // Create a global regex for the key
|
|
290
|
+
content = content.replace(regex, value);
|
|
259
291
|
|
|
260
|
-
//
|
|
261
|
-
|
|
292
|
+
// Log replacement
|
|
293
|
+
logger.log(`[${target}] Redacted ${key} in ${filePath}`);
|
|
262
294
|
});
|
|
263
295
|
|
|
264
|
-
//
|
|
265
|
-
|
|
296
|
+
// Write the new content to the file
|
|
297
|
+
jetpack.write(filePath, content);
|
|
298
|
+
});
|
|
266
299
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
await compileLocales(outputDir);
|
|
300
|
+
// Generate build.js and build.json
|
|
301
|
+
await generateBuildJs(outputDir);
|
|
270
302
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
303
|
+
// Compile manifest (target-specific) and locales
|
|
304
|
+
await compileManifest(outputDir, target);
|
|
305
|
+
await compileLocales(outputDir);
|
|
306
|
+
|
|
307
|
+
logger.log(`[${target}] Finished raw packaging`);
|
|
276
308
|
}
|
|
277
309
|
|
|
278
|
-
// Create zipped version of raw
|
|
310
|
+
// Create zipped version of raw packages for each target
|
|
279
311
|
async function packageZip() {
|
|
280
312
|
// Log
|
|
281
|
-
logger.log(`Zipping raw
|
|
313
|
+
logger.log(`Zipping raw packages...`);
|
|
314
|
+
|
|
315
|
+
// Skip if not in build mode
|
|
316
|
+
if (!Manager.isBuildMode()) {
|
|
317
|
+
logger.log(`Skipping zip (not in build mode)`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
282
320
|
|
|
283
321
|
try {
|
|
284
|
-
|
|
285
|
-
const
|
|
322
|
+
// Create zip for each target
|
|
323
|
+
for (const target of TARGETS) {
|
|
324
|
+
const inputDir = `packaged/${target}/raw`;
|
|
325
|
+
const zipPath = `packaged/${target}/extension.zip`;
|
|
286
326
|
|
|
287
|
-
// Create packed extension (.zip)
|
|
288
|
-
if (Manager.isBuildMode()) {
|
|
289
327
|
// Remove existing zip if it exists
|
|
290
328
|
jetpack.remove(zipPath);
|
|
291
329
|
|
|
292
330
|
// Zip contents of raw directory (not the directory itself)
|
|
293
331
|
// This ensures manifest.json is at the root of the zip
|
|
294
|
-
await execute(`cd ${inputDir} && zip -r
|
|
295
|
-
logger.log(`Zipped package created at ${zipPath}`);
|
|
296
|
-
} else {
|
|
297
|
-
logger.log(`Skipping zip (not in build mode)`);
|
|
332
|
+
await execute(`cd ${inputDir} && zip -r ../extension.zip .`);
|
|
333
|
+
logger.log(`[${target}] Zipped package created at ${zipPath}`);
|
|
298
334
|
}
|
|
299
335
|
} catch (e) {
|
|
300
336
|
logger.error(`Error zipping package`, e);
|
|
@@ -9,8 +9,17 @@ const { execute } = require('node-powertools');
|
|
|
9
9
|
// Load package
|
|
10
10
|
const project = Manager.getPackage('project');
|
|
11
11
|
|
|
12
|
-
// Paths
|
|
13
|
-
const
|
|
12
|
+
// Paths for each target
|
|
13
|
+
const PATHS = {
|
|
14
|
+
chromium: {
|
|
15
|
+
zip: path.join(process.cwd(), 'packaged', 'chromium', 'extension.zip'),
|
|
16
|
+
raw: path.join(process.cwd(), 'packaged', 'chromium', 'raw'),
|
|
17
|
+
},
|
|
18
|
+
firefox: {
|
|
19
|
+
zip: path.join(process.cwd(), 'packaged', 'firefox', 'extension.zip'),
|
|
20
|
+
raw: path.join(process.cwd(), 'packaged', 'firefox', 'raw'),
|
|
21
|
+
},
|
|
22
|
+
};
|
|
14
23
|
|
|
15
24
|
// Helper to check if a credential is valid (not empty or placeholder)
|
|
16
25
|
function isValidCredential(value) {
|
|
@@ -56,9 +65,13 @@ async function publish(complete) {
|
|
|
56
65
|
// Log
|
|
57
66
|
logger.log('Starting publish...');
|
|
58
67
|
|
|
59
|
-
// Check if
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
// Check if zips exist for each target
|
|
69
|
+
const missingZips = Object.entries(PATHS)
|
|
70
|
+
.filter(([, paths]) => !jetpack.exists(paths.zip))
|
|
71
|
+
.map(([target]) => target);
|
|
72
|
+
|
|
73
|
+
if (missingZips.length > 0) {
|
|
74
|
+
logger.error(`Extension zips not found for: ${missingZips.join(', ')}. Run build first.`);
|
|
62
75
|
return complete();
|
|
63
76
|
}
|
|
64
77
|
|
|
@@ -158,10 +171,10 @@ async function publishToChrome() {
|
|
|
158
171
|
|
|
159
172
|
logger.log('[chrome] Uploading to Chrome Web Store...');
|
|
160
173
|
|
|
161
|
-
// Use chrome-webstore-upload-cli
|
|
174
|
+
// Use chrome-webstore-upload-cli with chromium build
|
|
162
175
|
const command = [
|
|
163
176
|
'npx chrome-webstore-upload-cli',
|
|
164
|
-
`--source "${
|
|
177
|
+
`--source "${PATHS.chromium.zip}"`,
|
|
165
178
|
`--extension-id "${extensionId}"`,
|
|
166
179
|
`--client-id "${clientId}"`,
|
|
167
180
|
`--client-secret "${clientSecret}"`,
|
|
@@ -188,11 +201,10 @@ async function publishToFirefox() {
|
|
|
188
201
|
|
|
189
202
|
logger.log('[firefox] Uploading to Firefox Add-ons...');
|
|
190
203
|
|
|
191
|
-
// Use web-ext sign
|
|
192
|
-
const rawDir = path.join(process.cwd(), 'packaged', 'raw');
|
|
204
|
+
// Use web-ext sign with firefox build
|
|
193
205
|
const command = [
|
|
194
206
|
'npx web-ext sign',
|
|
195
|
-
`--source-dir "${
|
|
207
|
+
`--source-dir "${PATHS.firefox.raw}"`,
|
|
196
208
|
`--api-key "${apiKey}"`,
|
|
197
209
|
`--api-secret "${apiSecret}"`,
|
|
198
210
|
`--channel "${channel}"`,
|
|
@@ -218,8 +230,8 @@ async function publishToEdge() {
|
|
|
218
230
|
|
|
219
231
|
logger.log('[edge] Uploading to Microsoft Edge Add-ons...');
|
|
220
232
|
|
|
221
|
-
// Read zip file
|
|
222
|
-
const zipBuffer = jetpack.read(
|
|
233
|
+
// Read chromium zip file (Edge uses same build as Chrome)
|
|
234
|
+
const zipBuffer = jetpack.read(PATHS.chromium.zip, 'buffer');
|
|
223
235
|
|
|
224
236
|
// Edge API v1.1 endpoint
|
|
225
237
|
const uploadUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions/draft/package`;
|
|
@@ -7,25 +7,8 @@ const path = require('path');
|
|
|
7
7
|
const { execute } = require('node-powertools');
|
|
8
8
|
const JSON5 = require('json5');
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
const LANGUAGES =
|
|
12
|
-
'zh',
|
|
13
|
-
'es',
|
|
14
|
-
'hi',
|
|
15
|
-
'ar',
|
|
16
|
-
'pt',
|
|
17
|
-
'ru',
|
|
18
|
-
'ja',
|
|
19
|
-
'de',
|
|
20
|
-
'fr',
|
|
21
|
-
'ko',
|
|
22
|
-
'ur',
|
|
23
|
-
'id',
|
|
24
|
-
'bn',
|
|
25
|
-
'tl',
|
|
26
|
-
'vi',
|
|
27
|
-
'it',
|
|
28
|
-
];
|
|
10
|
+
// Locale config (shared with audit.js)
|
|
11
|
+
const { limits: LOCALE_LIMITS, languages: LANGUAGES } = require('../config/locales.js');
|
|
29
12
|
|
|
30
13
|
// Paths
|
|
31
14
|
const localesDir = path.join(process.cwd(), 'src', '_locales');
|
|
@@ -80,7 +63,7 @@ async function translate(complete) {
|
|
|
80
63
|
// Check which languages need translation
|
|
81
64
|
const languagesToTranslate = [];
|
|
82
65
|
|
|
83
|
-
for (const lang of LANGUAGES) {
|
|
66
|
+
for (const lang of Object.keys(LANGUAGES)) {
|
|
84
67
|
const langDir = path.join(localesDir, lang);
|
|
85
68
|
const langMessagesPath = path.join(langDir, 'messages.json');
|
|
86
69
|
|
|
@@ -156,21 +139,30 @@ async function translate(complete) {
|
|
|
156
139
|
async function translateAllWithClaude(enMessages, languagesToTranslate) {
|
|
157
140
|
// Build language info for prompt
|
|
158
141
|
const languageList = languagesToTranslate.map(({ lang }) =>
|
|
159
|
-
`- "${lang}": ${
|
|
142
|
+
`- "${lang}": ${LANGUAGES[lang]}`
|
|
160
143
|
).join('\n');
|
|
161
144
|
|
|
145
|
+
// Build character limits info
|
|
146
|
+
const limitsInfo = Object.entries(LOCALE_LIMITS)
|
|
147
|
+
.map(([field, limit]) => `- ${field}: max ${limit} characters`)
|
|
148
|
+
.join('\n');
|
|
149
|
+
|
|
162
150
|
const prompt = `Translate the following Chrome extension messages.json content from English to multiple languages.
|
|
163
151
|
|
|
164
152
|
TARGET LANGUAGES:
|
|
165
153
|
${languageList}
|
|
166
154
|
|
|
155
|
+
CHARACTER LIMITS (Chrome Web Store requirements):
|
|
156
|
+
${limitsInfo}
|
|
157
|
+
|
|
167
158
|
IMPORTANT RULES:
|
|
168
159
|
1. Only translate the "message" field values
|
|
169
160
|
2. Keep the "description" field values in English (they are for developers)
|
|
170
161
|
3. Keep all JSON keys exactly as they are
|
|
171
162
|
4. Return ONLY valid JSON, no markdown, no explanation
|
|
172
163
|
5. Preserve any placeholders like $1, $2, etc.
|
|
173
|
-
6.
|
|
164
|
+
6. IMPORTANT: Respect the character limits above for each field
|
|
165
|
+
7. Return a JSON object where each key is the language code and the value is the translated messages object
|
|
174
166
|
|
|
175
167
|
INPUT (English):
|
|
176
168
|
${JSON.stringify(enMessages, null, 2)}
|
|
@@ -225,29 +217,5 @@ Output the translated JSON:`;
|
|
|
225
217
|
}
|
|
226
218
|
}
|
|
227
219
|
|
|
228
|
-
// Get full language name
|
|
229
|
-
function getLanguageName(code) {
|
|
230
|
-
const names = {
|
|
231
|
-
zh: 'Chinese (Simplified)',
|
|
232
|
-
es: 'Spanish',
|
|
233
|
-
hi: 'Hindi',
|
|
234
|
-
ar: 'Arabic',
|
|
235
|
-
pt: 'Portuguese',
|
|
236
|
-
ru: 'Russian',
|
|
237
|
-
ja: 'Japanese',
|
|
238
|
-
de: 'German',
|
|
239
|
-
fr: 'French',
|
|
240
|
-
ko: 'Korean',
|
|
241
|
-
ur: 'Urdu',
|
|
242
|
-
id: 'Indonesian',
|
|
243
|
-
bn: 'Bengali',
|
|
244
|
-
tl: 'Tagalog/Filipino',
|
|
245
|
-
vi: 'Vietnamese',
|
|
246
|
-
it: 'Italian',
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
return names[code] || code;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
220
|
// Export task
|
|
253
221
|
module.exports = series(translate);
|
|
@@ -25,8 +25,9 @@ npm install
|
|
|
25
25
|
```sh
|
|
26
26
|
npm run build
|
|
27
27
|
```
|
|
28
|
-
5. The built
|
|
29
|
-
|
|
28
|
+
5. The built extensions will be in `packaged/` directory:
|
|
29
|
+
- **Firefox:** `packaged/firefox/raw/` (unpacked) and `packaged/firefox/extension.zip`
|
|
30
|
+
- **Chrome/Edge:** `packaged/chromium/raw/` (unpacked) and `packaged/chromium/extension.zip`
|
|
30
31
|
|
|
31
32
|
## Loading the Extension
|
|
32
33
|
|
|
@@ -34,13 +35,19 @@ npm run build
|
|
|
34
35
|
1. Go to `about:debugging`
|
|
35
36
|
2. Click "This Firefox"
|
|
36
37
|
3. Click "Load Temporary Add-on"
|
|
37
|
-
4. Select `packaged/raw/manifest.json`
|
|
38
|
+
4. Select `packaged/firefox/raw/manifest.json`
|
|
38
39
|
|
|
39
40
|
### Chrome
|
|
40
41
|
1. Go to `chrome://extensions`
|
|
41
42
|
2. Enable "Developer mode"
|
|
42
43
|
3. Click "Load unpacked"
|
|
43
|
-
4. Select the `packaged/raw/` directory
|
|
44
|
+
4. Select the `packaged/chromium/raw/` directory
|
|
45
|
+
|
|
46
|
+
### Edge
|
|
47
|
+
1. Go to `edge://extensions`
|
|
48
|
+
2. Enable "Developer mode"
|
|
49
|
+
3. Click "Load unpacked"
|
|
50
|
+
4. Select the `packaged/chromium/raw/` directory
|
|
44
51
|
|
|
45
52
|
## Questions
|
|
46
53
|
|