browser-extension-manager 1.3.28 → 1.3.30
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/commands/clean.js
CHANGED
|
@@ -3,12 +3,13 @@ const Manager = new (require('../build.js'));
|
|
|
3
3
|
const logger = Manager.logger('clean');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
5
|
const jetpack = require('fs-jetpack');
|
|
6
|
+
const path = require('path');
|
|
6
7
|
|
|
7
8
|
// Load package
|
|
8
9
|
const package = Manager.getPackage('main');
|
|
9
10
|
const project = Manager.getPackage('project');
|
|
10
11
|
|
|
11
|
-
//
|
|
12
|
+
// Dirs to clean
|
|
12
13
|
const dirs = [
|
|
13
14
|
'.temp',
|
|
14
15
|
'dist',
|
|
@@ -16,11 +17,29 @@ const dirs = [
|
|
|
16
17
|
// 'src/assets/themes',
|
|
17
18
|
]
|
|
18
19
|
|
|
20
|
+
// Dirs to preserve inside cleaned dirs
|
|
21
|
+
const preserve = [
|
|
22
|
+
'packaged/translations',
|
|
23
|
+
]
|
|
24
|
+
|
|
19
25
|
module.exports = async function (options) {
|
|
20
26
|
// Log
|
|
21
27
|
logger.log(`Cleaning up .temp, dist, and packaged directories...`);
|
|
22
28
|
|
|
23
29
|
try {
|
|
30
|
+
// Back up preserved dirs
|
|
31
|
+
const backups = {};
|
|
32
|
+
for (const dir of preserve) {
|
|
33
|
+
if (!jetpack.exists(dir)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const backupPath = path.join('.temp', '_preserve', dir);
|
|
38
|
+
jetpack.dir(path.dirname(backupPath));
|
|
39
|
+
jetpack.move(dir, backupPath, { overwrite: true });
|
|
40
|
+
backups[dir] = backupPath;
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
// Loop through dirs
|
|
25
44
|
dirs.forEach((dir) => {
|
|
26
45
|
// Remove (use rm -rf on Unix for speed, fallback to jetpack on Windows)
|
|
@@ -33,6 +52,16 @@ module.exports = async function (options) {
|
|
|
33
52
|
// Create empty dir
|
|
34
53
|
jetpack.dir(dir);
|
|
35
54
|
});
|
|
55
|
+
|
|
56
|
+
// Restore preserved dirs
|
|
57
|
+
for (const [dir, backupPath] of Object.entries(backups)) {
|
|
58
|
+
jetpack.dir(path.dirname(dir));
|
|
59
|
+
jetpack.move(backupPath, dir, { overwrite: true });
|
|
60
|
+
logger.log(`Preserved: ${dir}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Clean up temp preserve dir
|
|
64
|
+
jetpack.remove(path.join('.temp', '_preserve'));
|
|
36
65
|
} catch (e) {
|
|
37
66
|
logger.error(`Error clearing directories: ${e}`);
|
|
38
67
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
🚀 Enhance Your Browsing Experience Instantly
|
|
2
|
+
🧠 Smart Features That Work in the Background
|
|
3
|
+
⚡ One-Click Setup — No Configuration Needed
|
|
4
|
+
🎯 Customizable Settings — Your Browser, Your Rules
|
|
5
|
+
💡 Lightweight, Fast, Zero Bloat
|
|
6
|
+
|
|
7
|
+
{{ brand.name }} is your all-in-one browser companion that makes the web work better for you. No more frustrating browsing experiences. No more wasting time on repetitive tasks. No more settling for a browser that doesn't do enough.
|
|
8
|
+
If you want a smarter, faster, and more enjoyable browsing experience — {{ brand.name }} was made for you.
|
|
9
|
+
|
|
10
|
+
🔍 How it works
|
|
11
|
+
{{ brand.name }} runs quietly in the background, enhancing your browsing experience automatically.
|
|
12
|
+
|
|
13
|
+
🧠 Smart Detection: Knows when to activate and when to stay out of the way
|
|
14
|
+
🔧 Powerful Tools: Access everything you need from a simple popup
|
|
15
|
+
⚙️ Customizable: Tailor the extension to fit your browsing habits
|
|
16
|
+
🌐 Works Everywhere: Compatible with all your favorite websites
|
|
17
|
+
|
|
18
|
+
Click the icon, adjust your settings, and browse better. Everything else just works.
|
|
19
|
+
No complicated setup. No learning curve. Just a better browsing experience, instantly.
|
|
20
|
+
|
|
21
|
+
✨ Why {{ brand.name }} is a game changer
|
|
22
|
+
|
|
23
|
+
Save time: Automate repetitive tasks and get more done with less effort.
|
|
24
|
+
Stay in control: Customize every aspect of the extension to match your preferences.
|
|
25
|
+
Browse smarter: Intelligent features that adapt to how you use the web.
|
|
26
|
+
Clean and fast: Minimal footprint — no slowdowns, no clutter, no unnecessary bloat.
|
|
27
|
+
Private and secure: No cloud accounts required. No tracking. Everything runs locally and respects your privacy.
|
|
28
|
+
|
|
29
|
+
🧠 The {{ brand.name }} Difference
|
|
30
|
+
Most people either:
|
|
31
|
+
|
|
32
|
+
Settle for a bare-bones browser with no enhancements
|
|
33
|
+
Install a dozen separate extensions that slow everything down
|
|
34
|
+
|
|
35
|
+
{{ brand.name }} gives you a better option: one powerful extension that does it all — cleanly, efficiently, and without getting in your way.
|
|
36
|
+
Think of it as a supercharger for your browser ⚡
|
|
37
|
+
Whether you're working, researching, shopping, or just casually browsing — {{ brand.name }} makes every session better.
|
|
38
|
+
|
|
39
|
+
🔥 Install {{ brand.name }} now and upgrade the way you browse.
|
|
40
|
+
Stop settling for a basic browser. Start browsing smarter.
|
|
41
|
+
|
|
42
|
+
💸 Bonus:
|
|
43
|
+
While you're browsing, the extension also finds and applies shopping deals from top partners like Amazon, Capital One, and NordVPN. Get discounts and bonuses without lifting a finger. When you buy through links on our extension, we may earn an affiliate commission.
|
|
44
|
+
|
|
45
|
+
🔐 Your privacy is respected — we do not sell or misuse your data. By using our extension, you agree to our terms of service and privacy policy.
|
|
46
|
+
When you buy through links on our extension, we may earn an affiliate commission.
|
|
File without changes
|
|
@@ -66,11 +66,17 @@ const FILE_MAP = {
|
|
|
66
66
|
mergeLines: true,
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
-
// Config
|
|
69
|
+
// Config files
|
|
70
70
|
'config/browser-extension-manager.json': {
|
|
71
71
|
overwrite: true,
|
|
72
72
|
merge: true,
|
|
73
73
|
},
|
|
74
|
+
'config/messages.json': {
|
|
75
|
+
overwrite: false,
|
|
76
|
+
},
|
|
77
|
+
'config/description.md': {
|
|
78
|
+
overwrite: false,
|
|
79
|
+
},
|
|
74
80
|
|
|
75
81
|
// Files to run templating on
|
|
76
82
|
'.nvmrc': {
|
|
@@ -1,153 +1,188 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('translate');
|
|
4
|
+
const { query } = require('@anthropic-ai/claude-agent-sdk');
|
|
4
5
|
const { series } = require('gulp');
|
|
5
6
|
const jetpack = require('fs-jetpack');
|
|
6
7
|
const path = require('path');
|
|
7
|
-
const
|
|
8
|
+
const crypto = require('crypto');
|
|
8
9
|
const JSON5 = require('json5');
|
|
9
10
|
|
|
10
11
|
// Locale config (shared with audit.js)
|
|
11
12
|
const { limits: LOCALE_LIMITS, languages: LANGUAGES } = require('../config/locales.js');
|
|
12
13
|
|
|
13
14
|
// Paths
|
|
15
|
+
const configMessagesPath = path.join(process.cwd(), 'config', 'messages.json');
|
|
16
|
+
const configDescriptionPath = path.join(process.cwd(), 'config', 'description.md');
|
|
14
17
|
const localesDir = path.join(process.cwd(), 'src', '_locales');
|
|
15
18
|
const enMessagesPath = path.join(localesDir, 'en', 'messages.json');
|
|
19
|
+
const translationsDir = path.join(process.cwd(), 'packaged', 'translations');
|
|
20
|
+
const descriptionDir = path.join(translationsDir, 'description');
|
|
21
|
+
const cacheDir = path.join(process.cwd(), '.cache');
|
|
22
|
+
const cachePath = path.join(cacheDir, 'translate.json');
|
|
23
|
+
|
|
24
|
+
// Helper: Compute hash of a string
|
|
25
|
+
function hash(content) {
|
|
26
|
+
return crypto.createHash('md5').update(content).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Helper: Read cache
|
|
30
|
+
function readCache() {
|
|
31
|
+
if (!jetpack.exists(cachePath)) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
16
34
|
|
|
17
|
-
// Check if Claude CLI is installed
|
|
18
|
-
async function isClaudeInstalled() {
|
|
19
35
|
try {
|
|
20
|
-
|
|
21
|
-
return true;
|
|
36
|
+
return JSON.parse(jetpack.read(cachePath));
|
|
22
37
|
} catch (e) {
|
|
23
|
-
return
|
|
38
|
+
return {};
|
|
24
39
|
}
|
|
25
40
|
}
|
|
26
41
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return complete();
|
|
33
|
-
}
|
|
42
|
+
// Helper: Write cache
|
|
43
|
+
function writeCache(cache) {
|
|
44
|
+
jetpack.dir(cacheDir);
|
|
45
|
+
jetpack.write(cachePath, JSON.stringify(cache, null, 2));
|
|
46
|
+
}
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
48
|
+
// Helper: Check if source has changed since last translation
|
|
49
|
+
function hasSourceChanged(cacheKey, content) {
|
|
50
|
+
const cache = readCache();
|
|
51
|
+
const currentHash = hash(content);
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
return cache[cacheKey] !== currentHash;
|
|
54
|
+
}
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
// Helper: Update cache hash for a source
|
|
57
|
+
function updateCacheHash(cacheKey, content) {
|
|
58
|
+
const cache = readCache();
|
|
59
|
+
|
|
60
|
+
cache[cacheKey] = hash(content);
|
|
61
|
+
|
|
62
|
+
writeCache(cache);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Helper: Read and parse config/messages.json
|
|
66
|
+
function readConfigMessages() {
|
|
67
|
+
if (!jetpack.exists(configMessagesPath)) {
|
|
68
|
+
return null;
|
|
48
69
|
}
|
|
49
70
|
|
|
50
|
-
// Read English messages
|
|
51
|
-
let enMessages;
|
|
52
71
|
try {
|
|
53
|
-
|
|
72
|
+
return JSON5.parse(jetpack.read(configMessagesPath));
|
|
54
73
|
} catch (e) {
|
|
55
|
-
logger.error(`Failed to parse
|
|
56
|
-
return
|
|
74
|
+
logger.error(`Failed to parse config/messages.json: ${e.message}`);
|
|
75
|
+
return null;
|
|
57
76
|
}
|
|
77
|
+
}
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Check for missing keys
|
|
79
|
-
try {
|
|
80
|
-
const existingMessages = JSON5.parse(jetpack.read(langMessagesPath));
|
|
81
|
-
const missingKeys = enKeys.filter(key => !existingMessages[key]);
|
|
82
|
-
|
|
83
|
-
if (missingKeys.length > 0) {
|
|
84
|
-
logger.log(`[${lang}] Found ${missingKeys.length} missing keys`);
|
|
85
|
-
languagesToTranslate.push({ lang, missingKeys, existingMessages });
|
|
86
|
-
} else {
|
|
87
|
-
logger.log(`[${lang}] Up to date, skipping`);
|
|
79
|
+
// Helper: Call Claude Agent SDK with a prompt and parse JSON from the response
|
|
80
|
+
async function callClaude(prompt) {
|
|
81
|
+
let result = '';
|
|
82
|
+
|
|
83
|
+
for await (const message of query({
|
|
84
|
+
prompt,
|
|
85
|
+
options: {
|
|
86
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
87
|
+
maxTurns: 1,
|
|
88
|
+
allowedTools: [],
|
|
89
|
+
thinking: { type: 'disabled' },
|
|
90
|
+
},
|
|
91
|
+
})) {
|
|
92
|
+
// Collect assistant text
|
|
93
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
94
|
+
for (const block of message.message.content) {
|
|
95
|
+
if (block.type === 'text') {
|
|
96
|
+
result += block.text;
|
|
97
|
+
}
|
|
88
98
|
}
|
|
89
|
-
} catch (e) {
|
|
90
|
-
logger.warn(`[${lang}] Failed to parse existing messages, will retranslate: ${e.message}`);
|
|
91
|
-
languagesToTranslate.push({ lang, missingKeys: enKeys, existingMessages: {} });
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
logger.log(`Claude responded (${result.length} chars)`);
|
|
103
|
+
|
|
104
|
+
// Parse JSON from response
|
|
105
|
+
const jsonMatch = result.match(/\{[\s\S]*\}/);
|
|
106
|
+
if (!jsonMatch) {
|
|
107
|
+
throw new Error('No JSON found in Claude response');
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
return JSON.parse(jsonMatch[0]);
|
|
111
|
+
}
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
// Helper: Split an object into chunks of a given size
|
|
114
|
+
function chunkObject(obj, size) {
|
|
115
|
+
const entries = Object.entries(obj);
|
|
116
|
+
const chunks = [];
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const langMessagesPath = path.join(langDir, 'messages.json');
|
|
118
|
+
for (let i = 0; i < entries.length; i += size) {
|
|
119
|
+
chunks.push(Object.fromEntries(entries.slice(i, i + size)));
|
|
120
|
+
}
|
|
111
121
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const finalMessages = { ...existingMessages, ...translations[lang] };
|
|
122
|
+
return chunks;
|
|
123
|
+
}
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
// Copy config files to src/ locations
|
|
126
|
+
// Always runs (dev + build) so distribute task has the latest files
|
|
127
|
+
async function copyConfigFiles(complete) {
|
|
128
|
+
// Copy config/messages.json → src/_locales/en/messages.json
|
|
129
|
+
const enMessages = readConfigMessages();
|
|
130
|
+
if (enMessages) {
|
|
131
|
+
jetpack.dir(path.join(localesDir, 'en'));
|
|
132
|
+
jetpack.write(enMessagesPath, JSON.stringify(enMessages, null, 2));
|
|
133
|
+
logger.log('Copied config/messages.json → src/_locales/en/messages.json');
|
|
134
|
+
} else {
|
|
135
|
+
logger.warn('config/messages.json not found or invalid, skipping');
|
|
136
|
+
}
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
// Complete
|
|
139
|
+
return complete();
|
|
140
|
+
}
|
|
121
141
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
142
|
+
// Translate messages.json into all configured languages
|
|
143
|
+
// Only runs in build mode
|
|
144
|
+
async function translateMessages(complete) {
|
|
145
|
+
// Only run in build mode
|
|
146
|
+
if (!Manager.isBuildMode()) {
|
|
147
|
+
logger.log('Skipping messages translation (not in build mode)');
|
|
148
|
+
return complete();
|
|
129
149
|
}
|
|
130
150
|
|
|
131
151
|
// Log
|
|
132
|
-
logger.log('
|
|
152
|
+
logger.log('Starting messages translation...');
|
|
133
153
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
// Read config/messages.json
|
|
155
|
+
const enMessages = readConfigMessages();
|
|
156
|
+
if (!enMessages) {
|
|
157
|
+
logger.warn('config/messages.json not found or invalid, skipping');
|
|
158
|
+
return complete();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check if source has changed
|
|
162
|
+
const sourceContent = jetpack.read(configMessagesPath);
|
|
163
|
+
if (!hasSourceChanged('messages', sourceContent)) {
|
|
164
|
+
logger.log('config/messages.json unchanged since last translation, skipping');
|
|
165
|
+
return complete();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Get English keys
|
|
169
|
+
const enKeys = Object.keys(enMessages);
|
|
170
|
+
logger.log(`Found ${enKeys.length} keys in config/messages.json`);
|
|
137
171
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`- "${lang}": ${LANGUAGES[lang]}`
|
|
143
|
-
).join('\n');
|
|
172
|
+
// Build language list
|
|
173
|
+
const languageList = Object.entries(LANGUAGES)
|
|
174
|
+
.map(([code, name]) => `- "${code}": ${name}`)
|
|
175
|
+
.join('\n');
|
|
144
176
|
|
|
145
177
|
// Build character limits info
|
|
146
178
|
const limitsInfo = Object.entries(LOCALE_LIMITS)
|
|
147
179
|
.map(([field, limit]) => `- ${field}: max ${limit} characters`)
|
|
148
180
|
.join('\n');
|
|
149
181
|
|
|
150
|
-
|
|
182
|
+
logger.log(`Translating messages into ${Object.keys(LANGUAGES).length} languages...`);
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const translations = await callClaude(`Translate the following Chrome extension messages.json content from English to multiple languages.
|
|
151
186
|
|
|
152
187
|
TARGET LANGUAGES:
|
|
153
188
|
${languageList}
|
|
@@ -174,48 +209,135 @@ OUTPUT FORMAT:
|
|
|
174
209
|
...
|
|
175
210
|
}
|
|
176
211
|
|
|
177
|
-
Output the translated JSON
|
|
212
|
+
Output the translated JSON:`);
|
|
213
|
+
|
|
214
|
+
// Write each translation
|
|
215
|
+
for (const lang of Object.keys(LANGUAGES)) {
|
|
216
|
+
if (!translations[lang]) {
|
|
217
|
+
logger.warn(`[${lang}] No translation returned`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const langDir = path.join(localesDir, lang);
|
|
222
|
+
const langMessagesPath = path.join(langDir, 'messages.json');
|
|
223
|
+
|
|
224
|
+
jetpack.dir(langDir);
|
|
225
|
+
jetpack.write(langMessagesPath, JSON.stringify(translations[lang], null, 2));
|
|
226
|
+
logger.log(`[${lang}] Messages translation saved`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Update cache
|
|
230
|
+
updateCacheHash('messages', sourceContent);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
logger.error(`Messages translation failed: ${e.message}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Log
|
|
236
|
+
logger.log('Messages translation finished!');
|
|
237
|
+
|
|
238
|
+
// Complete
|
|
239
|
+
return complete();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Translate description.md into all configured languages
|
|
243
|
+
// Only runs in build mode
|
|
244
|
+
async function translateDescription(complete) {
|
|
245
|
+
// Only run in build mode
|
|
246
|
+
if (!Manager.isBuildMode()) {
|
|
247
|
+
logger.log('Skipping description translation (not in build mode)');
|
|
248
|
+
return complete();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Log
|
|
252
|
+
logger.log('Starting description translation...');
|
|
253
|
+
|
|
254
|
+
// Check if English description exists
|
|
255
|
+
if (!jetpack.exists(configDescriptionPath)) {
|
|
256
|
+
logger.log('config/description.md not found, skipping');
|
|
257
|
+
return complete();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Read English description
|
|
261
|
+
const enDescription = jetpack.read(configDescriptionPath);
|
|
262
|
+
if (!enDescription || !enDescription.trim()) {
|
|
263
|
+
logger.warn('config/description.md is empty, skipping');
|
|
264
|
+
return complete();
|
|
265
|
+
}
|
|
178
266
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
267
|
+
// Check if source has changed
|
|
268
|
+
if (!hasSourceChanged('description', enDescription)) {
|
|
269
|
+
logger.log('config/description.md unchanged since last translation, skipping');
|
|
270
|
+
return complete();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
logger.log(`Found config/description.md (${enDescription.length} chars)`);
|
|
274
|
+
|
|
275
|
+
// Batch languages into chunks to avoid output size limits
|
|
276
|
+
const BATCH_SIZE = 4;
|
|
277
|
+
const batches = chunkObject(LANGUAGES, BATCH_SIZE);
|
|
278
|
+
|
|
279
|
+
logger.log(`Translating description into ${Object.keys(LANGUAGES).length} languages (${batches.length} batches of ${BATCH_SIZE})...`);
|
|
280
|
+
|
|
281
|
+
// Ensure description directory exists
|
|
282
|
+
jetpack.dir(descriptionDir);
|
|
182
283
|
|
|
183
284
|
try {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
285
|
+
for (let i = 0; i < batches.length; i++) {
|
|
286
|
+
const batch = batches[i];
|
|
287
|
+
const languageList = Object.entries(batch)
|
|
288
|
+
.map(([code, name]) => `- "${code}": ${name}`)
|
|
289
|
+
.join('\n');
|
|
187
290
|
|
|
188
|
-
|
|
189
|
-
const command = `cat "${tempFile}" | claude -p -`;
|
|
291
|
+
logger.log(`Batch ${i + 1}/${batches.length}: translating ${Object.keys(batch).join(', ')}...`);
|
|
190
292
|
|
|
191
|
-
|
|
192
|
-
logger.log('Calling Claude CLI...');
|
|
293
|
+
const translations = await callClaude(`Translate the following Chrome extension store description from English to multiple languages.
|
|
193
294
|
|
|
194
|
-
|
|
195
|
-
|
|
295
|
+
TARGET LANGUAGES:
|
|
296
|
+
${languageList}
|
|
196
297
|
|
|
197
|
-
|
|
198
|
-
|
|
298
|
+
IMPORTANT RULES:
|
|
299
|
+
1. Translate ALL text content including emoji labels and descriptions
|
|
300
|
+
2. Keep all emojis exactly as they are
|
|
301
|
+
3. Preserve the markdown formatting (headers, bold, bullet points, etc.)
|
|
302
|
+
4. Keep brand names, product names, and company names in English (e.g., Amazon, Capital One, NordVPN)
|
|
303
|
+
5. Maintain the same tone — enthusiastic, conversational, and persuasive
|
|
304
|
+
6. Return ONLY valid JSON, no markdown code fences, no explanation
|
|
305
|
+
7. Return a JSON object where each key is the language code and the value is the full translated description as a string
|
|
199
306
|
|
|
200
|
-
|
|
201
|
-
|
|
307
|
+
INPUT (English):
|
|
308
|
+
${enDescription}
|
|
202
309
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
310
|
+
OUTPUT FORMAT:
|
|
311
|
+
{
|
|
312
|
+
${Object.keys(batch).map((code) => ` "${code}": "full translated description here..."`).join(',\n')}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
Output the translated JSON:`);
|
|
316
|
+
|
|
317
|
+
// Write each translation in this batch
|
|
318
|
+
for (const lang of Object.keys(batch)) {
|
|
319
|
+
if (!translations[lang]) {
|
|
320
|
+
logger.warn(`[${lang}] No description translation returned`);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
208
323
|
|
|
209
|
-
|
|
210
|
-
|
|
324
|
+
jetpack.write(path.join(descriptionDir, `${lang}.md`), translations[lang]);
|
|
325
|
+
logger.log(`[${lang}] Description translation saved`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
211
328
|
|
|
212
|
-
|
|
329
|
+
// Update cache
|
|
330
|
+
updateCacheHash('description', enDescription);
|
|
213
331
|
} catch (e) {
|
|
214
|
-
|
|
215
|
-
jetpack.remove(tempFile);
|
|
216
|
-
throw new Error(`Claude CLI failed: ${e.message}`);
|
|
332
|
+
logger.error(`Description translation failed: ${e.message}`);
|
|
217
333
|
}
|
|
334
|
+
|
|
335
|
+
// Log
|
|
336
|
+
logger.log('Description translation finished!');
|
|
337
|
+
|
|
338
|
+
// Complete
|
|
339
|
+
return complete();
|
|
218
340
|
}
|
|
219
341
|
|
|
220
342
|
// Export task
|
|
221
|
-
module.exports = series(
|
|
343
|
+
module.exports = series(copyConfigFiles, translateMessages, translateDescription);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-extension-manager",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.30",
|
|
4
4
|
"description": "Browser Extension Manager dependency manager",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -66,14 +66,15 @@
|
|
|
66
66
|
},
|
|
67
67
|
"homepage": "https://template.itwcreativeworks.com",
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@
|
|
70
|
-
"@babel/
|
|
69
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.20",
|
|
70
|
+
"@babel/core": "^7.29.0",
|
|
71
|
+
"@babel/preset-env": "^7.29.0",
|
|
71
72
|
"@popperjs/core": "^2.11.8",
|
|
72
73
|
"babel-loader": "^10.0.0",
|
|
73
74
|
"chalk": "^5.6.2",
|
|
74
|
-
"dotenv": "^17.2.
|
|
75
|
+
"dotenv": "^17.2.4",
|
|
75
76
|
"fs-jetpack": "^5.1.0",
|
|
76
|
-
"glob": "^13.0.
|
|
77
|
+
"glob": "^13.0.1",
|
|
77
78
|
"gulp-clean-css": "^4.3.0",
|
|
78
79
|
"gulp-filter": "^9.0.1",
|
|
79
80
|
"gulp-rename": "^2.1.0",
|
|
@@ -82,13 +83,13 @@
|
|
|
82
83
|
"itwcw-package-analytics": "^1.0.8",
|
|
83
84
|
"json5": "^2.2.3",
|
|
84
85
|
"lodash": "^4.17.23",
|
|
85
|
-
"minimatch": "^10.1.
|
|
86
|
+
"minimatch": "^10.1.2",
|
|
86
87
|
"node-powertools": "^2.3.2",
|
|
87
88
|
"npm-api": "^1.0.1",
|
|
88
89
|
"sass": "^1.97.3",
|
|
89
90
|
"through2": "^4.0.2",
|
|
90
91
|
"web-manager": "^4.1.6",
|
|
91
|
-
"webpack": "^5.
|
|
92
|
+
"webpack": "^5.105.0",
|
|
92
93
|
"wonderful-fetch": "^1.3.4",
|
|
93
94
|
"wonderful-version": "^1.3.2",
|
|
94
95
|
"ws": "^8.19.0",
|