addonova 1.0.0 → 1.0.1
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 +93 -0
- package/bin/index.js +42 -3
- package/package.json +24 -2
- package/src/commands/init.js +31 -31
- package/src/commands/update.js +67 -0
- package/src/index.js +1 -0
- package/template/config/chrome.js +13 -5
- package/template/config/edge.js +14 -6
- package/template/config/firefox.js +2 -2
- package/template/config/naver.js +14 -6
- package/template/config/opera.js +14 -6
- package/template/config/thunderbird.js +2 -2
- package/template/package.json.tpl +4 -24
- package/template/platform/chrome/platform.js +13 -0
- package/template/platform/edge/platform.js +14 -0
- package/template/platform/naver/platform.js +28 -0
- package/template/platform/opera/platform.js +14 -0
- package/template/src/_locales/en.i18n +3 -2
- package/template/src/assets/icons/128.png +0 -0
- package/template/src/assets/icons/32.png +0 -0
- package/template/src/assets/icons/48.png +0 -0
- package/template/src/assets/icons/64.png +0 -0
- package/template/src/js/background.js +6 -1
- package/template/src/js/lib/browser.js +263 -0
- package/template/src/js/lib/common.js +14 -0
- package/template/src/js/lib/config.js +21 -0
- package/template/src/js/lib/runtime.js +83 -0
- package/template/src/manifest/manifest-firefox.json +10 -7
- package/template/src/manifest/manifest.json +10 -7
- package/{template → workspace}/tasks/build.js +18 -10
- package/{template → workspace}/tasks/bundle-css.js +27 -34
- package/{template → workspace}/tasks/bundle-js.js +0 -1
- package/{template → workspace}/tasks/cli.js +12 -6
- package/{template → workspace}/tasks/copy.js +1 -2
- package/{template → workspace}/tasks/paths.js +2 -3
- package/{template → workspace}/tasks/zip.js +1 -1
- package/{template → workspace}/tools/json2i18n.js +7 -3
- package/template/tasks/translate.js +0 -255
- /package/template/src/{scss/popup.scss → css/popup.css} +0 -0
- /package/{template → workspace}/tasks/bundle-html.js +0 -0
- /package/{template → workspace}/tasks/bundle-locales.js +0 -0
- /package/{template → workspace}/tasks/bundle-manifest.js +0 -0
- /package/{template → workspace}/tasks/folder.js +0 -0
- /package/{template → workspace}/tasks/task.js +0 -0
- /package/{template → workspace}/tasks/utils.js +0 -0
- /package/{template → workspace}/tasks/watch.js +0 -0
- /package/{template → workspace}/tools/translate.js +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { build } from 'esbuild';
|
|
3
|
-
import { sassPlugin } from 'esbuild-sass-plugin';
|
|
4
3
|
import {getDestDir} from './paths.js';
|
|
5
4
|
import {readFile, writeFile, getConfig, getAllFiles, log, fileExistsInConfig} from './utils.js';
|
|
6
5
|
import {createTask} from './task.js';
|
|
7
6
|
|
|
8
|
-
const
|
|
7
|
+
const srcCSSDir = 'src/css';
|
|
9
8
|
|
|
10
9
|
async function removeTopSourceComment(filePath) {
|
|
11
10
|
const code = await readFile(filePath, 'utf8');
|
|
@@ -14,24 +13,17 @@ async function removeTopSourceComment(filePath) {
|
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
async function esbuildCSS(config, isDebug, platform) {
|
|
17
|
-
let buildResult;
|
|
18
|
-
let outputs;
|
|
16
|
+
let buildResult, outputs;
|
|
19
17
|
const dir = getDestDir({isDebug, platform});
|
|
20
|
-
|
|
18
|
+
|
|
21
19
|
for (const [dest, src] of Object.entries(config.entry)) {
|
|
22
20
|
buildResult = await build({
|
|
23
21
|
entryPoints: src,
|
|
24
22
|
outdir: path.join(dir, dest),
|
|
25
23
|
entryNames: config.filename,
|
|
26
24
|
loader: {
|
|
27
|
-
'.
|
|
25
|
+
'.css': 'css'
|
|
28
26
|
},
|
|
29
|
-
plugins: [
|
|
30
|
-
sassPlugin({
|
|
31
|
-
type: 'css',
|
|
32
|
-
sourceMap: config.sourcemap,
|
|
33
|
-
})
|
|
34
|
-
],
|
|
35
27
|
minify: config.minify,
|
|
36
28
|
sourcemap: config.sourcemap,
|
|
37
29
|
metafile: true,
|
|
@@ -49,60 +41,61 @@ async function esbuildCSS(config, isDebug, platform) {
|
|
|
49
41
|
}
|
|
50
42
|
}
|
|
51
43
|
|
|
52
|
-
|
|
44
|
+
function createBundleCSSTask(srcCSSDir) {
|
|
53
45
|
let currentWatchFiles;
|
|
46
|
+
|
|
54
47
|
const bundleCSS = async ({platforms, isDebug, logInfo, logWarn}) => {
|
|
55
|
-
for(const platform of platforms){
|
|
56
|
-
const config =
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
await esbuildCSS(
|
|
48
|
+
for (const platform of platforms) {
|
|
49
|
+
const config = await getConfig(platform);
|
|
50
|
+
const cssConfig = config.css;
|
|
51
|
+
if (cssConfig) {
|
|
52
|
+
await esbuildCSS(cssConfig, isDebug, platform);
|
|
60
53
|
if (logInfo) log.ok(`Bundling CSS for ${platform}...`);
|
|
61
54
|
} else {
|
|
62
|
-
if(logWarn) log.warn(`No
|
|
63
|
-
}
|
|
55
|
+
if (logWarn) log.warn(`No CSS config found for ${platform}, skipping CSS bundling.`);
|
|
56
|
+
}
|
|
64
57
|
}
|
|
65
58
|
}
|
|
66
59
|
|
|
67
60
|
const onChange = async (changedFiles, watcher, platforms, isDebug) => {
|
|
68
|
-
for(const platform of platforms){
|
|
61
|
+
for (const platform of platforms) {
|
|
69
62
|
const config = await getConfig(platform);
|
|
70
|
-
if (config.
|
|
63
|
+
if (config.css) {
|
|
71
64
|
const exists = await fileExistsInConfig(
|
|
72
|
-
config.
|
|
65
|
+
config.css.entry,
|
|
73
66
|
changedFiles[0]
|
|
74
67
|
);
|
|
75
|
-
if (exists){
|
|
68
|
+
if (exists) {
|
|
76
69
|
const newConfig = {
|
|
77
|
-
|
|
70
|
+
css: {
|
|
78
71
|
entry: exists,
|
|
79
|
-
filename: config.
|
|
80
|
-
minify: config.
|
|
81
|
-
sourcemap: config.
|
|
72
|
+
filename: config.css.filename,
|
|
73
|
+
minify: config.css.minify,
|
|
74
|
+
sourcemap: config.css.sourcemap
|
|
82
75
|
}
|
|
83
76
|
}
|
|
84
|
-
await esbuildCSS(newConfig.
|
|
77
|
+
await esbuildCSS(newConfig.css, isDebug, platform);
|
|
85
78
|
}
|
|
86
|
-
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
81
|
|
|
90
|
-
const newWatchFiles = await getAllFiles(
|
|
82
|
+
const newWatchFiles = await getAllFiles(srcCSSDir);
|
|
91
83
|
watcher.unwatch(
|
|
92
84
|
currentWatchFiles.filter((oldFile) => !newWatchFiles.includes(oldFile))
|
|
93
85
|
);
|
|
94
86
|
watcher.add(
|
|
95
|
-
newWatchFiles.filter((newFile) => currentWatchFiles.includes(newFile))
|
|
87
|
+
newWatchFiles.filter((newFile) => !currentWatchFiles.includes(newFile))
|
|
96
88
|
);
|
|
97
89
|
}
|
|
90
|
+
|
|
98
91
|
return createTask(
|
|
99
92
|
'bundle CSS',
|
|
100
93
|
bundleCSS,
|
|
101
94
|
).addWatcher(
|
|
102
95
|
async () => {
|
|
103
|
-
currentWatchFiles = await getAllFiles(
|
|
96
|
+
currentWatchFiles = await getAllFiles(srcCSSDir);
|
|
104
97
|
return currentWatchFiles;
|
|
105
98
|
}, onChange);
|
|
106
99
|
}
|
|
107
100
|
|
|
108
|
-
export default createBundleCSSTask(
|
|
101
|
+
export default createBundleCSSTask(srcCSSDir);
|
|
@@ -13,7 +13,6 @@ async function removeTopSourceComment(filePath) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async function esbuildJS(config, isDebug, platform){
|
|
16
|
-
// console.log(`Starting JS build for ${config}...`);
|
|
17
16
|
let buildResult, outputs;
|
|
18
17
|
const dir = getDestDir({isDebug, platform});
|
|
19
18
|
for (const [dest, src] of Object.entries(config.entry)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {join} from 'node:path';
|
|
1
|
+
import {join, dirname} from 'node:path';
|
|
2
2
|
import {fileURLToPath} from 'node:url';
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import {fork} from 'node:child_process';
|
|
@@ -6,7 +6,7 @@ import {log} from './utils.js';
|
|
|
6
6
|
|
|
7
7
|
function printHelp() {
|
|
8
8
|
console.log([
|
|
9
|
-
'
|
|
9
|
+
'WebExtension build utility',
|
|
10
10
|
'Usage: npm run build -- [options]',
|
|
11
11
|
'',
|
|
12
12
|
'To narrow down the list of build targets (for efficiency):',
|
|
@@ -30,7 +30,7 @@ function printHelp() {
|
|
|
30
30
|
].join('\n'));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const __filename = join(fileURLToPath(import.meta.url), '
|
|
33
|
+
const __filename = join(dirname(fileURLToPath(import.meta.url)), 'build.js');
|
|
34
34
|
|
|
35
35
|
async function executeChildProcess(args) {
|
|
36
36
|
const child = fork(__filename, args);
|
|
@@ -61,14 +61,20 @@ function validateArguments(args) {
|
|
|
61
61
|
'--help',
|
|
62
62
|
'-h'
|
|
63
63
|
];
|
|
64
|
-
const invalidFlags = args.filter((flag) => !validFlags.includes(flag));
|
|
64
|
+
const invalidFlags = args.filter((flag) => !validFlags.includes(flag) && !flag.startsWith('--version='));
|
|
65
65
|
invalidFlags.forEach((flag) => validationErrors.push(`Invalid flag ${flag}`));
|
|
66
66
|
return validationErrors;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
async function run() {
|
|
70
|
-
const
|
|
71
|
-
|
|
70
|
+
const subcommand = process.argv[2];
|
|
71
|
+
let args = process.argv.slice(3);
|
|
72
|
+
|
|
73
|
+
if (subcommand === 'zip') {
|
|
74
|
+
args = ['--all', '--release'];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const shouldPrintHelp = args.length === 0 || (subcommand !== 'build' && subcommand !== 'zip') || args.includes('-h') || args.includes('--help');
|
|
72
78
|
if (shouldPrintHelp) {
|
|
73
79
|
printHelp();
|
|
74
80
|
process.exit(0);
|
|
@@ -6,7 +6,6 @@ import path from 'node:path';
|
|
|
6
6
|
import { mkdir } from 'node:fs/promises';
|
|
7
7
|
|
|
8
8
|
export function createCopyTask() {
|
|
9
|
-
const paths = [];
|
|
10
9
|
const assetsCopy = async ({platforms, isDebug, logInfo, logWarn}) => {
|
|
11
10
|
for(const platform of platforms){
|
|
12
11
|
const config = await getConfig(platform);
|
|
@@ -40,7 +39,7 @@ export function createCopyTask() {
|
|
|
40
39
|
'assets copy',
|
|
41
40
|
assetsCopy,
|
|
42
41
|
).addWatcher(
|
|
43
|
-
|
|
42
|
+
[],
|
|
44
43
|
onChange,
|
|
45
44
|
);
|
|
46
45
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {createRequire} from 'node:module';
|
|
2
1
|
import {dirname, join} from 'node:path';
|
|
3
2
|
|
|
4
|
-
let rootDir =
|
|
3
|
+
let rootDir = process.cwd();
|
|
5
4
|
|
|
6
5
|
export const absolutePath = (path) => {
|
|
7
6
|
return join(rootDir, path);
|
|
@@ -10,7 +9,7 @@ export function setRootDir(dir) {
|
|
|
10
9
|
rootDir = dir;
|
|
11
10
|
}
|
|
12
11
|
export function getDestDir({isDebug, platform}) {
|
|
13
|
-
const buildTypeDir =
|
|
12
|
+
const buildTypeDir = `.output/${isDebug ? 'debug':'release'}`;
|
|
14
13
|
return `${buildTypeDir}/${platform}`;
|
|
15
14
|
}
|
|
16
15
|
export default {
|
|
@@ -41,7 +41,7 @@ async function zip({platforms, isDebug, version}) {
|
|
|
41
41
|
throw new Error('zip task does not support debug builds');
|
|
42
42
|
}
|
|
43
43
|
version = version ? `-${version}` : '';
|
|
44
|
-
const releaseDir = '
|
|
44
|
+
const releaseDir = '.output/release';
|
|
45
45
|
const promises = [];
|
|
46
46
|
const date = await getLastCommitTime();
|
|
47
47
|
const xpiPlatforms = ["firefox", "thunderbird"];
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { mkdirSync, readdirSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const localesDir = join(__dirname, "../", "_locales");
|
|
9
|
+
const outputDir = join(__dirname, "../", "output", "_locales");
|
|
6
10
|
|
|
7
11
|
mkdirSync(outputDir, { recursive: true });
|
|
8
12
|
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import * as readline from 'node:readline/promises';
|
|
3
|
-
import { stdin as input, stdout as output } from 'node:process';
|
|
4
|
-
import {readFile, writeFile, httpsRequest, timeout} from './utils.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const LOCALES_ROOT = 'src/_locales';
|
|
8
|
-
|
|
9
|
-
function toMessageId(message) {
|
|
10
|
-
if (typeof message !== 'string') return '';
|
|
11
|
-
|
|
12
|
-
return message
|
|
13
|
-
.trim()
|
|
14
|
-
.split(/\s+/) // split by any whitespace
|
|
15
|
-
.slice(0, 3) // only first 3 words
|
|
16
|
-
.map(word =>
|
|
17
|
-
word
|
|
18
|
-
.replace(/[^\w]/g, '') // remove special characters
|
|
19
|
-
.toLowerCase()
|
|
20
|
-
)
|
|
21
|
-
.filter(Boolean) // remove empty results
|
|
22
|
-
.join('_');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function getSupportedLocales() {
|
|
26
|
-
const fileList = await fs.readdir(LOCALES_ROOT);
|
|
27
|
-
|
|
28
|
-
const locales = [];
|
|
29
|
-
|
|
30
|
-
for (const file of fileList) {
|
|
31
|
-
if (file.endsWith('.i18n')) {
|
|
32
|
-
const locale = file.substring(0, file.lastIndexOf('.i18n'));
|
|
33
|
-
locales.push(locale);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return locales;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function stringifyLocale(messages) {
|
|
41
|
-
const lines = [];
|
|
42
|
-
messages.forEach((message, id) => {
|
|
43
|
-
lines.push(`@${id}`);
|
|
44
|
-
const hasDoubleNewLines = /\n\n/.test(message);
|
|
45
|
-
message.split('\n')
|
|
46
|
-
.filter((line) => line.trim())
|
|
47
|
-
.forEach((line, index, filtered) => {
|
|
48
|
-
lines.push(line);
|
|
49
|
-
if (hasDoubleNewLines && index < filtered.length - 1) {
|
|
50
|
-
lines.push('');
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
lines.push('');
|
|
54
|
-
});
|
|
55
|
-
return lines.join('\n');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function parseLocale(content) {
|
|
59
|
-
const messages = new Map();
|
|
60
|
-
const lines = content.split('\n');
|
|
61
|
-
let id = '';
|
|
62
|
-
for (let i = 0; i < lines.length; i++) {
|
|
63
|
-
const line = lines[i];
|
|
64
|
-
if (line.startsWith('@')) {
|
|
65
|
-
id = line.substring(1);
|
|
66
|
-
} else if (line.startsWith('#')) {
|
|
67
|
-
// Ignore
|
|
68
|
-
} else if (messages.has(id)) {
|
|
69
|
-
const message = messages.get(id);
|
|
70
|
-
messages.set(id, `${message}\n${line}`);
|
|
71
|
-
} else {
|
|
72
|
-
messages.set(id, line);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
messages.forEach((value, id) => {
|
|
76
|
-
messages.set(id, value.trim());
|
|
77
|
-
});
|
|
78
|
-
return messages;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function translate(text, lang) {
|
|
82
|
-
const url = new URL('https://translate.googleapis.com/translate_a/single');
|
|
83
|
-
url.search = (new URLSearchParams({
|
|
84
|
-
client: 'gtx',
|
|
85
|
-
sl: 'en-US',
|
|
86
|
-
tl: lang,
|
|
87
|
-
dt: 't',
|
|
88
|
-
dj: '1',
|
|
89
|
-
q: text,
|
|
90
|
-
})).toString();
|
|
91
|
-
const response = await httpsRequest(url.toString());
|
|
92
|
-
const translation = JSON.parse(response.text());
|
|
93
|
-
return translation.sentences.map((s) => s.trans).join('\n').replaceAll(/\n+/g, '\n');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function deleteMessage(messageId) {
|
|
97
|
-
const supportedLocales = await getSupportedLocales();
|
|
98
|
-
|
|
99
|
-
if (!messageId || !messageId.trim()) {
|
|
100
|
-
console.log('⚠ Message key cannot be empty.');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const id = messageId.trim();
|
|
105
|
-
let foundAnywhere = false;
|
|
106
|
-
|
|
107
|
-
for (const locale of supportedLocales) {
|
|
108
|
-
const locFile = `${LOCALES_ROOT}/${locale}.i18n`;
|
|
109
|
-
const locContent = await readFile(locFile);
|
|
110
|
-
const locMessages = parseLocale(locContent);
|
|
111
|
-
|
|
112
|
-
if (!locMessages.has(id)) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
locMessages.delete(id);
|
|
117
|
-
const output = stringifyLocale(locMessages);
|
|
118
|
-
await writeFile(locFile, output);
|
|
119
|
-
|
|
120
|
-
console.log(`✔ Removed from ${locale}.i18n`);
|
|
121
|
-
foundAnywhere = true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!foundAnywhere) {
|
|
125
|
-
console.log('⚠ Message key not found in any locale.');
|
|
126
|
-
} else {
|
|
127
|
-
console.log('🗑 Deletion completed.');
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
async function translateEnMessage(message, customId) {
|
|
133
|
-
console.log(`Translating message: ${message}`);
|
|
134
|
-
|
|
135
|
-
const supportedLocales = await getSupportedLocales();
|
|
136
|
-
const messageId = customId && customId.trim()
|
|
137
|
-
? customId.trim()
|
|
138
|
-
: toMessageId(message);
|
|
139
|
-
|
|
140
|
-
// 1️⃣ Update English first
|
|
141
|
-
const enFile = `${LOCALES_ROOT}/en.i18n`;
|
|
142
|
-
const enContent = await readFile(enFile);
|
|
143
|
-
const enMessages = parseLocale(enContent);
|
|
144
|
-
|
|
145
|
-
if (!enMessages.has(messageId)) {
|
|
146
|
-
enMessages.set(messageId, message);
|
|
147
|
-
const output = stringifyLocale(enMessages);
|
|
148
|
-
await writeFile(enFile, output);
|
|
149
|
-
console.log(`en: ${message}`);
|
|
150
|
-
} else {
|
|
151
|
-
console.log('Message already exists in en.i18n');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 2️⃣ Translate other locales
|
|
155
|
-
for (const locale of supportedLocales) {
|
|
156
|
-
if (locale === 'en') continue;
|
|
157
|
-
|
|
158
|
-
await timeout(1000);
|
|
159
|
-
|
|
160
|
-
const locFile = `${LOCALES_ROOT}/${locale}.i18n`;
|
|
161
|
-
const locContent = await readFile(locFile);
|
|
162
|
-
const locMessages = parseLocale(locContent);
|
|
163
|
-
|
|
164
|
-
if (locMessages.has(messageId)) {
|
|
165
|
-
console.log(`Already exists in: ${locFile}`);
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const translated = await translate(message, locale);
|
|
170
|
-
locMessages.set(messageId, translated);
|
|
171
|
-
|
|
172
|
-
const output = stringifyLocale(locMessages);
|
|
173
|
-
await writeFile(locFile, output);
|
|
174
|
-
|
|
175
|
-
console.log(`${locale}: ${translated}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function main() {
|
|
180
|
-
const rl = readline.createInterface({ input, output });
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
while (true) {
|
|
184
|
-
console.log('\n=== Message Manager ===');
|
|
185
|
-
console.log('[1] Add new message');
|
|
186
|
-
console.log('[2] Delete message');
|
|
187
|
-
console.log('[0] Exit');
|
|
188
|
-
|
|
189
|
-
const choice = (await rl.question('Select option: '))
|
|
190
|
-
.trim()
|
|
191
|
-
.toLowerCase();
|
|
192
|
-
|
|
193
|
-
switch (choice) {
|
|
194
|
-
case '1': {
|
|
195
|
-
const newMessage = (await rl.question('Enter new message: '))
|
|
196
|
-
.trim();
|
|
197
|
-
|
|
198
|
-
if (!newMessage) {
|
|
199
|
-
console.log('⚠ Message cannot be empty.');
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const newMessageId = (await rl.question(
|
|
204
|
-
'Enter new message key (leave empty for auto-generation): '
|
|
205
|
-
)).trim();
|
|
206
|
-
|
|
207
|
-
await translateEnMessage(
|
|
208
|
-
newMessage,
|
|
209
|
-
newMessageId || undefined
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
console.log('✔ Message processed successfully.');
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
case '2': {
|
|
217
|
-
const messageId = (await rl.question(
|
|
218
|
-
'Enter message key to delete: '
|
|
219
|
-
)).trim();
|
|
220
|
-
|
|
221
|
-
if (!messageId) {
|
|
222
|
-
console.log('⚠ Message key cannot be empty.');
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const confirm = (await rl.question(
|
|
227
|
-
`Are you sure you want to delete "${messageId}" from all locales? (y/n): `
|
|
228
|
-
))
|
|
229
|
-
.trim()
|
|
230
|
-
.toLowerCase();
|
|
231
|
-
|
|
232
|
-
if (confirm !== 'y') {
|
|
233
|
-
console.log('Deletion cancelled.');
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
await deleteMessage(messageId);
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
case '0':
|
|
242
|
-
case 'exit': {
|
|
243
|
-
console.log('Exiting...');
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
default: {
|
|
248
|
-
console.log('Invalid selection. Please try again.');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} finally {
|
|
253
|
-
rl.close();
|
|
254
|
-
}
|
|
255
|
-
}main();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|