i18next-cli 1.42.6 → 1.42.7
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/cjs/cli.js +14 -14
- package/dist/cjs/config.js +4 -4
- package/dist/cjs/extractor/core/extractor.js +11 -11
- package/dist/cjs/linter.js +7 -7
- package/dist/cjs/locize.js +18 -18
- package/dist/cjs/rename-key.js +3 -3
- package/dist/cjs/status.js +25 -20
- package/dist/cjs/syncer.js +11 -11
- package/dist/cjs/types-generator.js +6 -6
- package/dist/esm/cli.js +14 -14
- package/dist/esm/config.js +4 -4
- package/dist/esm/extractor/core/extractor.js +11 -11
- package/dist/esm/linter.js +7 -7
- package/dist/esm/locize.js +18 -18
- package/dist/esm/rename-key.js +3 -3
- package/dist/esm/status.js +25 -20
- package/dist/esm/syncer.js +11 -11
- package/dist/esm/types-generator.js +6 -6
- package/package.json +2 -5
package/dist/cjs/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ var commander = require('commander');
|
|
|
5
5
|
var chokidar = require('chokidar');
|
|
6
6
|
var glob = require('glob');
|
|
7
7
|
var minimatch = require('minimatch');
|
|
8
|
-
var
|
|
8
|
+
var node_util = require('node:util');
|
|
9
9
|
var config = require('./config.js');
|
|
10
10
|
var heuristicConfig = require('./heuristic-config.js');
|
|
11
11
|
var extractor = require('./extractor/core/extractor.js');
|
|
@@ -28,7 +28,7 @@ const program = new commander.Command();
|
|
|
28
28
|
program
|
|
29
29
|
.name('i18next-cli')
|
|
30
30
|
.description('A unified, high-performance i18next CLI.')
|
|
31
|
-
.version('1.42.
|
|
31
|
+
.version('1.42.7'); // This string is replaced with the actual version at build time by rollup
|
|
32
32
|
// new: global config override option
|
|
33
33
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
34
34
|
program
|
|
@@ -100,14 +100,14 @@ program
|
|
|
100
100
|
const cfgPath = program.opts().config;
|
|
101
101
|
let config$1 = await config.loadConfig(cfgPath);
|
|
102
102
|
if (!config$1) {
|
|
103
|
-
console.log(
|
|
103
|
+
console.log(node_util.styleText('blue', 'No config file found. Attempting to detect project structure...'));
|
|
104
104
|
const detected = await heuristicConfig.detectConfig();
|
|
105
105
|
if (!detected) {
|
|
106
|
-
console.error(
|
|
107
|
-
console.log(`Please create a config file first by running: ${
|
|
106
|
+
console.error(node_util.styleText('red', 'Could not automatically detect your project structure.'));
|
|
107
|
+
console.log(`Please create a config file first by running: ${node_util.styleText('cyan', 'npx i18next-cli init')}`);
|
|
108
108
|
process.exit(1);
|
|
109
109
|
}
|
|
110
|
-
console.log(
|
|
110
|
+
console.log(node_util.styleText('green', 'Project structure detected successfully!'));
|
|
111
111
|
config$1 = detected;
|
|
112
112
|
}
|
|
113
113
|
await status.runStatus(config$1, { detail: locale, namespace: options.namespace });
|
|
@@ -164,14 +164,14 @@ program
|
|
|
164
164
|
// The existing logic for loading the config or detecting it is now inside this function
|
|
165
165
|
let config$1 = await config.loadConfig(cfgPath);
|
|
166
166
|
if (!config$1) {
|
|
167
|
-
console.log(
|
|
167
|
+
console.log(node_util.styleText('blue', 'No config file found. Attempting to detect project structure...'));
|
|
168
168
|
const detected = await heuristicConfig.detectConfig();
|
|
169
169
|
if (!detected) {
|
|
170
|
-
console.error(
|
|
171
|
-
console.log(`Please create a config file first by running: ${
|
|
170
|
+
console.error(node_util.styleText('red', 'Could not automatically detect your project structure.'));
|
|
171
|
+
console.log(`Please create a config file first by running: ${node_util.styleText('cyan', 'npx i18next-cli init')}`);
|
|
172
172
|
process.exit(1);
|
|
173
173
|
}
|
|
174
|
-
console.log(
|
|
174
|
+
console.log(node_util.styleText('green', 'Project structure detected successfully!'));
|
|
175
175
|
config$1 = detected;
|
|
176
176
|
}
|
|
177
177
|
await linter.runLinterCli(config$1, { quiet: !!options.quiet });
|
|
@@ -242,21 +242,21 @@ program
|
|
|
242
242
|
const result = await renameKey.runRenameKey(config$1, oldKey, newKey, options);
|
|
243
243
|
if (!result.success) {
|
|
244
244
|
if (result.conflicts) {
|
|
245
|
-
console.error(
|
|
245
|
+
console.error(node_util.styleText('red', '\n❌ Conflicts detected:'));
|
|
246
246
|
result.conflicts.forEach(c => console.error(` - ${c}`));
|
|
247
247
|
}
|
|
248
248
|
if (result.error) {
|
|
249
|
-
console.error(
|
|
249
|
+
console.error(node_util.styleText('red', `\n❌ ${result.error}`));
|
|
250
250
|
}
|
|
251
251
|
process.exit(1);
|
|
252
252
|
}
|
|
253
253
|
const totalChanges = result.sourceFiles.reduce((sum, f) => sum + f.changes, 0);
|
|
254
254
|
if (totalChanges === 0) {
|
|
255
|
-
console.log(
|
|
255
|
+
console.log(node_util.styleText('yellow', `\n⚠️ No usages found for "${oldKey}"`));
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
catch (error) {
|
|
259
|
-
console.error(
|
|
259
|
+
console.error(node_util.styleText('red', 'Error renaming key:'), error);
|
|
260
260
|
process.exit(1);
|
|
261
261
|
}
|
|
262
262
|
});
|
package/dist/cjs/config.js
CHANGED
|
@@ -6,7 +6,7 @@ var promises = require('node:fs/promises');
|
|
|
6
6
|
var jiti = require('jiti');
|
|
7
7
|
var jsoncParser = require('jsonc-parser');
|
|
8
8
|
var inquirer = require('inquirer');
|
|
9
|
-
var
|
|
9
|
+
var node_util = require('node:util');
|
|
10
10
|
var init = require('./init.js');
|
|
11
11
|
var logger = require('./utils/logger.js');
|
|
12
12
|
|
|
@@ -130,18 +130,18 @@ async function ensureConfig(configPath, logger$1 = new logger.ConsoleLogger()) {
|
|
|
130
130
|
const { shouldInit } = await inquirer.prompt([{
|
|
131
131
|
type: 'confirm',
|
|
132
132
|
name: 'shouldInit',
|
|
133
|
-
message:
|
|
133
|
+
message: node_util.styleText('yellow', 'Configuration file not found. Would you like to create one now?'),
|
|
134
134
|
default: true,
|
|
135
135
|
}]);
|
|
136
136
|
if (shouldInit) {
|
|
137
137
|
await init.runInit(); // Run the interactive setup wizard (keeps existing behavior)
|
|
138
|
-
logger$1.info(
|
|
138
|
+
logger$1.info(node_util.styleText('green', 'Configuration created. Resuming command...'));
|
|
139
139
|
config = await loadConfig(configPath, logger$1); // Try loading the newly created config
|
|
140
140
|
if (config) {
|
|
141
141
|
return config;
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
144
|
-
logger$1.error(
|
|
144
|
+
logger$1.error(node_util.styleText('red', 'Error: Failed to load configuration after creation. Please try running the command again.'));
|
|
145
145
|
process.exit(1);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var wrapOra = require('../../utils/wrap-ora.js');
|
|
4
|
-
var
|
|
4
|
+
var node_util = require('node:util');
|
|
5
5
|
var core = require('@swc/core');
|
|
6
6
|
var promises = require('node:fs/promises');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -71,7 +71,7 @@ async function runExtractor(config, options = {}) {
|
|
|
71
71
|
const fileContent = fileUtils.serializeTranslationFile(result.newTranslations, effectiveFormat, config.extract.indentation, rawContent);
|
|
72
72
|
await promises.mkdir(node_path.dirname(result.path), { recursive: true });
|
|
73
73
|
await promises.writeFile(result.path, fileContent);
|
|
74
|
-
internalLogger.info(
|
|
74
|
+
internalLogger.info(node_util.styleText('green', `Updated: ${result.path}`));
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -82,14 +82,14 @@ async function runExtractor(config, options = {}) {
|
|
|
82
82
|
await plugin.afterSync?.(results, config);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
spinner.succeed(
|
|
85
|
+
spinner.succeed(node_util.styleText('bold', 'Extraction complete!'));
|
|
86
86
|
// Show the funnel message only if files were actually changed.
|
|
87
87
|
if (anyFileUpdated)
|
|
88
88
|
await printLocizeFunnel(options.logger);
|
|
89
89
|
return anyFileUpdated;
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
|
-
spinner.fail(
|
|
92
|
+
spinner.fail(node_util.styleText('red', 'Extraction failed.'));
|
|
93
93
|
// Re-throw or handle error
|
|
94
94
|
throw error;
|
|
95
95
|
}
|
|
@@ -204,7 +204,7 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
catch (error) {
|
|
207
|
-
logger$1.warn(`${
|
|
207
|
+
logger$1.warn(`${node_util.styleText('yellow', 'Skipping file due to error:')} ${file}`);
|
|
208
208
|
const err = error;
|
|
209
209
|
const msg = typeof err?.message === 'string' && err.message.trim().length > 0
|
|
210
210
|
? err.message
|
|
@@ -248,18 +248,18 @@ async function printLocizeFunnel(logger$1) {
|
|
|
248
248
|
return;
|
|
249
249
|
const internalLogger = logger$1 ?? new logger.ConsoleLogger();
|
|
250
250
|
if (typeof internalLogger.info === 'function') {
|
|
251
|
-
internalLogger.info(
|
|
251
|
+
internalLogger.info(node_util.styleText(['yellow', 'bold'], '\n💡 Tip: Tired of running the extractor manually?'));
|
|
252
252
|
internalLogger.info(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,');
|
|
253
253
|
internalLogger.info(' where keys are created and translated automatically as you code.');
|
|
254
|
-
internalLogger.info(` Learn more: ${
|
|
255
|
-
internalLogger.info(` Watch the video: ${
|
|
254
|
+
internalLogger.info(` Learn more: ${node_util.styleText('cyan', 'https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
|
|
255
|
+
internalLogger.info(` Watch the video: ${node_util.styleText('cyan', 'https://youtu.be/joPsZghT3wM')}`);
|
|
256
256
|
}
|
|
257
257
|
else {
|
|
258
|
-
console.log(
|
|
258
|
+
console.log(node_util.styleText(['yellow', 'bold'], '\n💡 Tip: Tired of running the extractor manually?'));
|
|
259
259
|
console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,');
|
|
260
260
|
console.log(' where keys are created and translated automatically as you code.');
|
|
261
|
-
console.log(` Learn more: ${
|
|
262
|
-
console.log(` Watch the video: ${
|
|
261
|
+
console.log(` Learn more: ${node_util.styleText('cyan', 'https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
|
|
262
|
+
console.log(` Watch the video: ${node_util.styleText('cyan', 'https://youtu.be/joPsZghT3wM')}`);
|
|
263
263
|
}
|
|
264
264
|
return funnelMsgTracker.recordFunnelShown('extract');
|
|
265
265
|
}
|
package/dist/cjs/linter.js
CHANGED
|
@@ -5,7 +5,7 @@ var promises = require('node:fs/promises');
|
|
|
5
5
|
var core = require('@swc/core');
|
|
6
6
|
var node_path = require('node:path');
|
|
7
7
|
var node_events = require('node:events');
|
|
8
|
-
var
|
|
8
|
+
var node_util = require('node:util');
|
|
9
9
|
var logger = require('./utils/logger.js');
|
|
10
10
|
var wrapOra = require('./utils/wrap-ora.js');
|
|
11
11
|
|
|
@@ -313,25 +313,25 @@ async function runLinterCli(config, options = {}) {
|
|
|
313
313
|
try {
|
|
314
314
|
const { success, message, files } = await linter.run();
|
|
315
315
|
if (!success) {
|
|
316
|
-
spinner.fail(
|
|
316
|
+
spinner.fail(node_util.styleText(['red', 'bold'], message));
|
|
317
317
|
// Print detailed report after spinner fails
|
|
318
318
|
for (const [file, issues] of Object.entries(files)) {
|
|
319
319
|
if (internalLogger.info)
|
|
320
|
-
internalLogger.info(
|
|
320
|
+
internalLogger.info(node_util.styleText('yellow', `\n${file}`));
|
|
321
321
|
else
|
|
322
|
-
console.log(
|
|
322
|
+
console.log(node_util.styleText('yellow', `\n${file}`));
|
|
323
323
|
issues.forEach(({ text, line, type }) => {
|
|
324
324
|
const label = type === 'interpolation' ? 'Interpolation issue' : 'Found hardcoded string';
|
|
325
325
|
if (typeof internalLogger.info === 'function')
|
|
326
|
-
internalLogger.info(` ${
|
|
326
|
+
internalLogger.info(` ${node_util.styleText('gray', `${line}:`)} ${node_util.styleText('red', 'Error:')} ${label}: "${text}"`);
|
|
327
327
|
else
|
|
328
|
-
console.log(` ${
|
|
328
|
+
console.log(` ${node_util.styleText('gray', `${line}:`)} ${node_util.styleText('red', 'Error:')} ${label}: "${text}"`);
|
|
329
329
|
});
|
|
330
330
|
}
|
|
331
331
|
process.exit(1);
|
|
332
332
|
}
|
|
333
333
|
else {
|
|
334
|
-
spinner.succeed(
|
|
334
|
+
spinner.succeed(node_util.styleText(['green', 'bold'], message));
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
337
|
catch (error) {
|
package/dist/cjs/locize.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var execa = require('execa');
|
|
4
|
-
var
|
|
4
|
+
var node_util = require('node:util');
|
|
5
5
|
var ora = require('ora');
|
|
6
6
|
var inquirer = require('inquirer');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -24,9 +24,9 @@ async function checkLocizeCliExists() {
|
|
|
24
24
|
}
|
|
25
25
|
catch (error) {
|
|
26
26
|
if (error.code === 'ENOENT') {
|
|
27
|
-
console.error(
|
|
28
|
-
console.log(
|
|
29
|
-
console.log(
|
|
27
|
+
console.error(node_util.styleText('red', 'Error: `locize-cli` command not found.'));
|
|
28
|
+
console.log(node_util.styleText('yellow', 'Please install it globally to use the Locize integration:'));
|
|
29
|
+
console.log(node_util.styleText('cyan', 'npm install -g locize-cli'));
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -54,7 +54,7 @@ async function checkLocizeCliExists() {
|
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
56
|
async function interactiveCredentialSetup(config) {
|
|
57
|
-
console.log(
|
|
57
|
+
console.log(node_util.styleText('yellow', '\nLocize configuration is missing or invalid. Let\'s set it up!'));
|
|
58
58
|
const answers = await inquirer.prompt([
|
|
59
59
|
{
|
|
60
60
|
type: 'input',
|
|
@@ -76,7 +76,7 @@ async function interactiveCredentialSetup(config) {
|
|
|
76
76
|
},
|
|
77
77
|
]);
|
|
78
78
|
if (!answers.projectId) {
|
|
79
|
-
console.error(
|
|
79
|
+
console.error(node_util.styleText('red', 'Project ID is required to continue.'));
|
|
80
80
|
return undefined;
|
|
81
81
|
}
|
|
82
82
|
const { save } = await inquirer.prompt([{
|
|
@@ -98,11 +98,11 @@ LOCIZE_API_KEY=${answers.apiKey}
|
|
|
98
98
|
apiKey: process.env.LOCIZE_API_KEY,
|
|
99
99
|
version: '${answers.version}',
|
|
100
100
|
},`;
|
|
101
|
-
console.log(
|
|
102
|
-
console.log(
|
|
103
|
-
console.log(
|
|
104
|
-
console.log(
|
|
105
|
-
console.log(
|
|
101
|
+
console.log(node_util.styleText('cyan', '\nGreat! For the best security, we recommend using environment variables for your API key.'));
|
|
102
|
+
console.log(node_util.styleText('bold', '\nRecommended approach (.env file):'));
|
|
103
|
+
console.log(node_util.styleText('green', envSnippet));
|
|
104
|
+
console.log(node_util.styleText('bold', 'Then, in your i18next.config.ts:'));
|
|
105
|
+
console.log(node_util.styleText('green', configSnippet));
|
|
106
106
|
}
|
|
107
107
|
return {
|
|
108
108
|
projectId: answers.projectId,
|
|
@@ -217,9 +217,9 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
217
217
|
try {
|
|
218
218
|
// 1. First attempt
|
|
219
219
|
const initialArgs = buildArgs(command, effectiveConfig, cliOptions);
|
|
220
|
-
console.log(
|
|
220
|
+
console.log(node_util.styleText('cyan', `\nRunning 'locize ${initialArgs.join(' ')}'...`));
|
|
221
221
|
const result = await execa.execa('locize', initialArgs, { stdio: 'pipe' });
|
|
222
|
-
spinner.succeed(
|
|
222
|
+
spinner.succeed(node_util.styleText('green', `'locize ${command}' completed successfully.`));
|
|
223
223
|
if (result?.stdout)
|
|
224
224
|
console.log(result.stdout); // Print captured output on success
|
|
225
225
|
}
|
|
@@ -234,14 +234,14 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
234
234
|
try {
|
|
235
235
|
// 3. Retry attempt, rebuilding args with the NOW-UPDATED currentConfig object
|
|
236
236
|
const retryArgs = buildArgs(command, effectiveConfig, cliOptions);
|
|
237
|
-
console.log(
|
|
237
|
+
console.log(node_util.styleText('cyan', `\nRunning 'locize ${retryArgs.join(' ')}'...`));
|
|
238
238
|
const result = await execa.execa('locize', retryArgs, { stdio: 'pipe' });
|
|
239
|
-
spinner.succeed(
|
|
239
|
+
spinner.succeed(node_util.styleText('green', 'Retry successful!'));
|
|
240
240
|
if (result?.stdout)
|
|
241
241
|
console.log(result.stdout);
|
|
242
242
|
}
|
|
243
243
|
catch (retryError) {
|
|
244
|
-
spinner.fail(
|
|
244
|
+
spinner.fail(node_util.styleText('red', 'Error during retry.'));
|
|
245
245
|
console.error(retryError.stderr || retryError.message);
|
|
246
246
|
process.exit(1);
|
|
247
247
|
}
|
|
@@ -253,12 +253,12 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
253
253
|
}
|
|
254
254
|
else {
|
|
255
255
|
// Handle other errors
|
|
256
|
-
spinner.fail(
|
|
256
|
+
spinner.fail(node_util.styleText('red', `Error executing 'locize ${command}'.`));
|
|
257
257
|
console.error(stderr || error.message);
|
|
258
258
|
process.exit(1);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
|
-
console.log(
|
|
261
|
+
console.log(node_util.styleText('green', `\n✅ 'locize ${command}' completed successfully.`));
|
|
262
262
|
}
|
|
263
263
|
const runLocizeSync = (config, cliOptions) => runLocizeCommand('sync', config, cliOptions);
|
|
264
264
|
const runLocizeDownload = (config, cliOptions) => runLocizeCommand('download', config, cliOptions);
|
package/dist/cjs/rename-key.js
CHANGED
|
@@ -7,7 +7,7 @@ var logger = require('./utils/logger.js');
|
|
|
7
7
|
var fileUtils = require('./utils/file-utils.js');
|
|
8
8
|
var nestedObject = require('./utils/nested-object.js');
|
|
9
9
|
var funnelMsgTracker = require('./utils/funnel-msg-tracker.js');
|
|
10
|
-
var
|
|
10
|
+
var node_util = require('node:util');
|
|
11
11
|
|
|
12
12
|
const pluralSuffixes = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
13
13
|
/**
|
|
@@ -106,11 +106,11 @@ async function runRenameKey(config, oldKey, newKey, options = {}, logger$1 = new
|
|
|
106
106
|
async function printLocizeFunnel() {
|
|
107
107
|
if (!(await funnelMsgTracker.shouldShowFunnel('rename-key')))
|
|
108
108
|
return;
|
|
109
|
-
console.log(
|
|
109
|
+
console.log(node_util.styleText(['yellow', 'bold'], '\n💡 Tip: Managing translations across multiple projects?'));
|
|
110
110
|
console.log(' With Locize, you can rename, move, and copy translation keys directly');
|
|
111
111
|
console.log(' in the web interface—no CLI needed. Perfect for collaboration with');
|
|
112
112
|
console.log(' translators and managing complex refactoring across namespaces.');
|
|
113
|
-
console.log(` Learn more: ${
|
|
113
|
+
console.log(` Learn more: ${node_util.styleText('cyan', 'https://www.locize.com/docs/how-can-a-segment-key-be-copied-moved-or-renamed')}`);
|
|
114
114
|
return funnelMsgTracker.recordFunnelShown('rename-key');
|
|
115
115
|
}
|
|
116
116
|
function parseKeyWithNamespace(key, config) {
|
package/dist/cjs/status.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var node_util = require('node:util');
|
|
4
4
|
var ora = require('ora');
|
|
5
5
|
var node_path = require('node:path');
|
|
6
|
+
require('@swc/core');
|
|
7
|
+
require('node:fs/promises');
|
|
6
8
|
var keyFinder = require('./extractor/core/key-finder.js');
|
|
9
|
+
require('glob');
|
|
7
10
|
var nestedObject = require('./utils/nested-object.js');
|
|
8
11
|
var fileUtils = require('./utils/file-utils.js');
|
|
9
12
|
var funnelMsgTracker = require('./utils/funnel-msg-tracker.js');
|
|
13
|
+
require('./extractor/parsers/jsx-parser.js');
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Runs a health check on the project's i18next translations and displays a status report.
|
|
@@ -239,19 +243,19 @@ async function displayStatusReport(report, config, options) {
|
|
|
239
243
|
*/
|
|
240
244
|
async function displayDetailedLocaleReport(report, config, locale, namespaceFilter) {
|
|
241
245
|
if (locale === config.extract.primaryLanguage) {
|
|
242
|
-
console.log(
|
|
246
|
+
console.log(node_util.styleText('yellow', `Locale "${locale}" is the primary language. All keys are considered present.`));
|
|
243
247
|
return;
|
|
244
248
|
}
|
|
245
249
|
if (!config.locales.includes(locale)) {
|
|
246
|
-
console.error(
|
|
250
|
+
console.error(node_util.styleText('red', `Error: Locale "${locale}" is not defined in your configuration.`));
|
|
247
251
|
return;
|
|
248
252
|
}
|
|
249
253
|
const localeData = report.locales.get(locale);
|
|
250
254
|
if (!localeData) {
|
|
251
|
-
console.error(
|
|
255
|
+
console.error(node_util.styleText('red', `Error: Locale "${locale}" is not a valid secondary language.`));
|
|
252
256
|
return;
|
|
253
257
|
}
|
|
254
|
-
console.log(
|
|
258
|
+
console.log(node_util.styleText('bold', `\nKey Status for "${node_util.styleText('cyan', locale)}":`));
|
|
255
259
|
const totalKeysForLocale = localeData.totalKeys;
|
|
256
260
|
printProgressBar('Overall', localeData.totalTranslated, totalKeysForLocale);
|
|
257
261
|
const namespacesToDisplay = namespaceFilter ? [namespaceFilter] : Array.from(localeData.namespaces.keys()).sort();
|
|
@@ -259,19 +263,19 @@ async function displayDetailedLocaleReport(report, config, locale, namespaceFilt
|
|
|
259
263
|
const nsData = localeData.namespaces.get(ns);
|
|
260
264
|
if (!nsData)
|
|
261
265
|
continue;
|
|
262
|
-
console.log(
|
|
266
|
+
console.log(node_util.styleText(['cyan', 'bold'], `\nNamespace: ${ns}`));
|
|
263
267
|
printProgressBar('Namespace Progress', nsData.translatedKeys, nsData.totalKeys);
|
|
264
268
|
nsData.keyDetails.forEach(({ key, isTranslated }) => {
|
|
265
|
-
const icon = isTranslated ?
|
|
269
|
+
const icon = isTranslated ? node_util.styleText('green', '✓') : node_util.styleText('red', '✗');
|
|
266
270
|
console.log(` ${icon} ${key}`);
|
|
267
271
|
});
|
|
268
272
|
}
|
|
269
273
|
const missingCount = totalKeysForLocale - localeData.totalTranslated;
|
|
270
274
|
if (missingCount > 0) {
|
|
271
|
-
console.log(
|
|
275
|
+
console.log(node_util.styleText(['yellow', 'bold'], `\nSummary: Found ${missingCount} missing translations for "${locale}".`));
|
|
272
276
|
}
|
|
273
277
|
else {
|
|
274
|
-
console.log(
|
|
278
|
+
console.log(node_util.styleText(['green', 'bold'], `\nSummary: 🎉 All keys are translated for "${locale}".`));
|
|
275
279
|
}
|
|
276
280
|
await printLocizeFunnel();
|
|
277
281
|
}
|
|
@@ -288,10 +292,10 @@ async function displayDetailedLocaleReport(report, config, locale, namespaceFilt
|
|
|
288
292
|
async function displayNamespaceSummaryReport(report, config, namespace) {
|
|
289
293
|
const nsData = report.keysByNs.get(namespace);
|
|
290
294
|
if (!nsData) {
|
|
291
|
-
console.error(
|
|
295
|
+
console.error(node_util.styleText('red', `Error: Namespace "${namespace}" was not found in your source code.`));
|
|
292
296
|
return;
|
|
293
297
|
}
|
|
294
|
-
console.log(
|
|
298
|
+
console.log(node_util.styleText(['cyan', 'bold'], `\nStatus for Namespace: "${namespace}"`));
|
|
295
299
|
console.log('------------------------');
|
|
296
300
|
for (const [locale, localeData] of report.locales.entries()) {
|
|
297
301
|
const nsLocaleData = localeData.namespaces.get(namespace);
|
|
@@ -316,12 +320,13 @@ async function displayNamespaceSummaryReport(report, config, namespace) {
|
|
|
316
320
|
*/
|
|
317
321
|
async function displayOverallSummaryReport(report, config) {
|
|
318
322
|
const { primaryLanguage } = config.extract;
|
|
319
|
-
console.log(
|
|
323
|
+
console.log(node_util.styleText(['cyan', 'bold'], '\ni18next Project Status'));
|
|
320
324
|
console.log('------------------------');
|
|
321
|
-
console.log(`🔑 Keys Found: ${
|
|
322
|
-
console.log(`📚 Namespaces Found: ${
|
|
323
|
-
console.log(`🌍 Locales: ${
|
|
324
|
-
|
|
325
|
+
console.log(`🔑 Keys Found: ${node_util.styleText('bold', `${report.totalBaseKeys}`)}`);
|
|
326
|
+
console.log(`📚 Namespaces Found: ${node_util.styleText('bold', `${report.keysByNs.size}`)}`);
|
|
327
|
+
console.log(`🌍 Locales: ${node_util.styleText('bold', config.locales.join(', '))}`);
|
|
328
|
+
if (primaryLanguage)
|
|
329
|
+
console.log(`✅ Primary Language: ${node_util.styleText('bold', primaryLanguage)}`);
|
|
325
330
|
console.log('\nTranslation Progress:');
|
|
326
331
|
for (const [locale, localeData] of report.locales.entries()) {
|
|
327
332
|
const percentage = localeData.totalKeys > 0 ? Math.round((localeData.totalTranslated / localeData.totalKeys) * 100) : 100;
|
|
@@ -340,7 +345,7 @@ async function displayOverallSummaryReport(report, config) {
|
|
|
340
345
|
function printProgressBar(label, current, total) {
|
|
341
346
|
const percentage = total > 0 ? Math.round((current / total) * 100) : 100;
|
|
342
347
|
const bar = generateProgressBarText(percentage);
|
|
343
|
-
console.log(`${
|
|
348
|
+
console.log(`${node_util.styleText('bold', label)}: ${bar} ${percentage}% (${current}/${total})`);
|
|
344
349
|
}
|
|
345
350
|
/**
|
|
346
351
|
* Generates a visual progress bar string based on percentage completion.
|
|
@@ -355,14 +360,14 @@ function generateProgressBarText(percentage) {
|
|
|
355
360
|
const totalBars = 20;
|
|
356
361
|
const filledBars = Math.floor((percentage / 100) * totalBars);
|
|
357
362
|
const emptyBars = totalBars - filledBars;
|
|
358
|
-
return `[${
|
|
363
|
+
return `[${node_util.styleText('green', ''.padStart(filledBars, '■'))}${''.padStart(emptyBars, '□')}]`;
|
|
359
364
|
}
|
|
360
365
|
async function printLocizeFunnel() {
|
|
361
366
|
if (!(await funnelMsgTracker.shouldShowFunnel('status')))
|
|
362
367
|
return;
|
|
363
|
-
console.log(
|
|
368
|
+
console.log(node_util.styleText(['yellow', 'bold'], '\n✨ Take your localization to the next level!'));
|
|
364
369
|
console.log('Manage translations with your team in the cloud with Locize => https://www.locize.com/docs/getting-started');
|
|
365
|
-
console.log(`Run ${
|
|
370
|
+
console.log(`Run ${node_util.styleText('cyan', 'npx i18next-cli locize-migrate')} to get started.`);
|
|
366
371
|
return funnelMsgTracker.recordFunnelShown('status');
|
|
367
372
|
}
|
|
368
373
|
|
package/dist/cjs/syncer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var node_util = require('node:util');
|
|
4
4
|
var glob = require('glob');
|
|
5
5
|
var promises = require('node:fs/promises');
|
|
6
6
|
var node_path = require('node:path');
|
|
@@ -75,7 +75,7 @@ async function runSyncer(config, options = {}) {
|
|
|
75
75
|
continue;
|
|
76
76
|
const primaryTranslations = await fileUtils.loadTranslationFile(primaryPath);
|
|
77
77
|
if (!primaryTranslations) {
|
|
78
|
-
logMessages.push(` ${
|
|
78
|
+
logMessages.push(` ${node_util.styleText('yellow', '-')} Could not read primary file: ${primaryPath}`);
|
|
79
79
|
continue;
|
|
80
80
|
}
|
|
81
81
|
const primaryKeys = nestedObject.getNestedKeys(primaryTranslations, keySeparator ?? '.');
|
|
@@ -102,27 +102,27 @@ async function runSyncer(config, options = {}) {
|
|
|
102
102
|
const serializedContent = fileUtils.serializeTranslationFile(newSecondaryTranslations, perFileFormat, indentation, raw);
|
|
103
103
|
await promises.mkdir(node_path.dirname(fullSecondaryPath), { recursive: true });
|
|
104
104
|
await promises.writeFile(fullSecondaryPath, serializedContent);
|
|
105
|
-
logMessages.push(` ${
|
|
105
|
+
logMessages.push(` ${node_util.styleText('green', '✓')} Synchronized: ${secondaryPath}`);
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
108
|
-
logMessages.push(` ${
|
|
108
|
+
logMessages.push(` ${node_util.styleText('gray', '-')} Already in sync: ${secondaryPath}`);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
spinner.succeed(
|
|
112
|
+
spinner.succeed(node_util.styleText('bold', 'Synchronization complete!'));
|
|
113
113
|
logMessages.forEach(msg => internalLogger.info ? internalLogger.info(msg) : console.log(msg));
|
|
114
114
|
if (wasAnythingSynced) {
|
|
115
115
|
await printLocizeFunnel();
|
|
116
116
|
}
|
|
117
117
|
else {
|
|
118
118
|
if (typeof internalLogger.info === 'function')
|
|
119
|
-
internalLogger.info(
|
|
119
|
+
internalLogger.info(node_util.styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
120
120
|
else
|
|
121
|
-
console.log(
|
|
121
|
+
console.log(node_util.styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
catch (error) {
|
|
125
|
-
spinner.fail(
|
|
125
|
+
spinner.fail(node_util.styleText('red', 'Synchronization failed.'));
|
|
126
126
|
if (typeof internalLogger.error === 'function')
|
|
127
127
|
internalLogger.error(error);
|
|
128
128
|
else
|
|
@@ -132,9 +132,9 @@ async function runSyncer(config, options = {}) {
|
|
|
132
132
|
async function printLocizeFunnel() {
|
|
133
133
|
if (!(await funnelMsgTracker.shouldShowFunnel('syncer')))
|
|
134
134
|
return;
|
|
135
|
-
console.log(
|
|
136
|
-
console.log(
|
|
137
|
-
console.log(` Get started with the official TMS for i18next: ${
|
|
135
|
+
console.log(node_util.styleText(['green', 'bold'], '\n✅ Sync complete.'));
|
|
136
|
+
console.log(node_util.styleText('yellow', '🚀 Ready to collaborate with translators? Move your files to the cloud.'));
|
|
137
|
+
console.log(` Get started with the official TMS for i18next: ${node_util.styleText('cyan', 'npx i18next-cli locize-migrate')}`);
|
|
138
138
|
return funnelMsgTracker.recordFunnelShown('syncer');
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -4,7 +4,7 @@ var logger = require('./utils/logger.js');
|
|
|
4
4
|
var i18nextResourcesForTs = require('i18next-resources-for-ts');
|
|
5
5
|
var glob = require('glob');
|
|
6
6
|
var wrapOra = require('./utils/wrap-ora.js');
|
|
7
|
-
var
|
|
7
|
+
var node_util = require('node:util');
|
|
8
8
|
var promises = require('node:fs/promises');
|
|
9
9
|
var node_path = require('node:path');
|
|
10
10
|
var core = require('@swc/core');
|
|
@@ -111,7 +111,7 @@ async function runTypesGenerator(config, options = {}) {
|
|
|
111
111
|
}
|
|
112
112
|
const nonObjectKeys = keys.filter(k => !parsedContent[k] || typeof parsedContent[k] !== 'object');
|
|
113
113
|
if (nonObjectKeys.length > 0) {
|
|
114
|
-
console.warn(
|
|
114
|
+
console.warn(node_util.styleText('yellow', `Warning: The file ${file} contains top-level keys that are not objects (${nonObjectKeys.join(', ')}). When 'mergeNamespaces' is enabled, top-level keys are treated as namespaces. These keys will be ignored.`));
|
|
115
115
|
}
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
@@ -127,7 +127,7 @@ ${i18nextResourcesForTs.mergeResourcesAsInterface(resources, { optimize: !!enabl
|
|
|
127
127
|
const resourcesOutputPath = node_path.resolve(process.cwd(), config.types.resourcesFile);
|
|
128
128
|
await promises.mkdir(node_path.dirname(resourcesOutputPath), { recursive: true });
|
|
129
129
|
await promises.writeFile(resourcesOutputPath, interfaceDefinition);
|
|
130
|
-
logMessages.push(` ${
|
|
130
|
+
logMessages.push(` ${node_util.styleText('green', '✓')} Resources interface written to ${config.types.resourcesFile}`);
|
|
131
131
|
let outputPathExists;
|
|
132
132
|
try {
|
|
133
133
|
await promises.access(outputPath);
|
|
@@ -154,13 +154,13 @@ declare module 'i18next' {
|
|
|
154
154
|
}`;
|
|
155
155
|
await promises.mkdir(node_path.dirname(outputPath), { recursive: true });
|
|
156
156
|
await promises.writeFile(outputPath, fileContent);
|
|
157
|
-
logMessages.push(` ${
|
|
157
|
+
logMessages.push(` ${node_util.styleText('green', '✓')} TypeScript definitions written to ${config.types.output || ''}`);
|
|
158
158
|
}
|
|
159
|
-
spinner.succeed(
|
|
159
|
+
spinner.succeed(node_util.styleText('bold', 'TypeScript definitions generated successfully.'));
|
|
160
160
|
logMessages.forEach(msg => typeof internalLogger.info === 'function' ? internalLogger.info(msg) : console.log(msg));
|
|
161
161
|
}
|
|
162
162
|
catch (error) {
|
|
163
|
-
spinner.fail(
|
|
163
|
+
spinner.fail(node_util.styleText('red', 'Failed to generate TypeScript definitions.'));
|
|
164
164
|
if (typeof internalLogger.error === 'function')
|
|
165
165
|
internalLogger.error(error);
|
|
166
166
|
else
|
package/dist/esm/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import chokidar from 'chokidar';
|
|
4
4
|
import { glob } from 'glob';
|
|
5
5
|
import { minimatch } from 'minimatch';
|
|
6
|
-
import
|
|
6
|
+
import { styleText } from 'node:util';
|
|
7
7
|
import { ensureConfig, loadConfig } from './config.js';
|
|
8
8
|
import { detectConfig } from './heuristic-config.js';
|
|
9
9
|
import { runExtractor } from './extractor/core/extractor.js';
|
|
@@ -26,7 +26,7 @@ const program = new Command();
|
|
|
26
26
|
program
|
|
27
27
|
.name('i18next-cli')
|
|
28
28
|
.description('A unified, high-performance i18next CLI.')
|
|
29
|
-
.version('1.42.
|
|
29
|
+
.version('1.42.7'); // This string is replaced with the actual version at build time by rollup
|
|
30
30
|
// new: global config override option
|
|
31
31
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
32
32
|
program
|
|
@@ -98,14 +98,14 @@ program
|
|
|
98
98
|
const cfgPath = program.opts().config;
|
|
99
99
|
let config = await loadConfig(cfgPath);
|
|
100
100
|
if (!config) {
|
|
101
|
-
console.log(
|
|
101
|
+
console.log(styleText('blue', 'No config file found. Attempting to detect project structure...'));
|
|
102
102
|
const detected = await detectConfig();
|
|
103
103
|
if (!detected) {
|
|
104
|
-
console.error(
|
|
105
|
-
console.log(`Please create a config file first by running: ${
|
|
104
|
+
console.error(styleText('red', 'Could not automatically detect your project structure.'));
|
|
105
|
+
console.log(`Please create a config file first by running: ${styleText('cyan', 'npx i18next-cli init')}`);
|
|
106
106
|
process.exit(1);
|
|
107
107
|
}
|
|
108
|
-
console.log(
|
|
108
|
+
console.log(styleText('green', 'Project structure detected successfully!'));
|
|
109
109
|
config = detected;
|
|
110
110
|
}
|
|
111
111
|
await runStatus(config, { detail: locale, namespace: options.namespace });
|
|
@@ -162,14 +162,14 @@ program
|
|
|
162
162
|
// The existing logic for loading the config or detecting it is now inside this function
|
|
163
163
|
let config = await loadConfig(cfgPath);
|
|
164
164
|
if (!config) {
|
|
165
|
-
console.log(
|
|
165
|
+
console.log(styleText('blue', 'No config file found. Attempting to detect project structure...'));
|
|
166
166
|
const detected = await detectConfig();
|
|
167
167
|
if (!detected) {
|
|
168
|
-
console.error(
|
|
169
|
-
console.log(`Please create a config file first by running: ${
|
|
168
|
+
console.error(styleText('red', 'Could not automatically detect your project structure.'));
|
|
169
|
+
console.log(`Please create a config file first by running: ${styleText('cyan', 'npx i18next-cli init')}`);
|
|
170
170
|
process.exit(1);
|
|
171
171
|
}
|
|
172
|
-
console.log(
|
|
172
|
+
console.log(styleText('green', 'Project structure detected successfully!'));
|
|
173
173
|
config = detected;
|
|
174
174
|
}
|
|
175
175
|
await runLinterCli(config, { quiet: !!options.quiet });
|
|
@@ -240,21 +240,21 @@ program
|
|
|
240
240
|
const result = await runRenameKey(config, oldKey, newKey, options);
|
|
241
241
|
if (!result.success) {
|
|
242
242
|
if (result.conflicts) {
|
|
243
|
-
console.error(
|
|
243
|
+
console.error(styleText('red', '\n❌ Conflicts detected:'));
|
|
244
244
|
result.conflicts.forEach(c => console.error(` - ${c}`));
|
|
245
245
|
}
|
|
246
246
|
if (result.error) {
|
|
247
|
-
console.error(
|
|
247
|
+
console.error(styleText('red', `\n❌ ${result.error}`));
|
|
248
248
|
}
|
|
249
249
|
process.exit(1);
|
|
250
250
|
}
|
|
251
251
|
const totalChanges = result.sourceFiles.reduce((sum, f) => sum + f.changes, 0);
|
|
252
252
|
if (totalChanges === 0) {
|
|
253
|
-
console.log(
|
|
253
|
+
console.log(styleText('yellow', `\n⚠️ No usages found for "${oldKey}"`));
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
catch (error) {
|
|
257
|
-
console.error(
|
|
257
|
+
console.error(styleText('red', 'Error renaming key:'), error);
|
|
258
258
|
process.exit(1);
|
|
259
259
|
}
|
|
260
260
|
});
|
package/dist/esm/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import { access, readFile } from 'node:fs/promises';
|
|
|
4
4
|
import { createJiti } from 'jiti';
|
|
5
5
|
import { parse } from 'jsonc-parser';
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
|
-
import
|
|
7
|
+
import { styleText } from 'node:util';
|
|
8
8
|
import { runInit } from './init.js';
|
|
9
9
|
import { ConsoleLogger } from './utils/logger.js';
|
|
10
10
|
|
|
@@ -128,18 +128,18 @@ async function ensureConfig(configPath, logger = new ConsoleLogger()) {
|
|
|
128
128
|
const { shouldInit } = await inquirer.prompt([{
|
|
129
129
|
type: 'confirm',
|
|
130
130
|
name: 'shouldInit',
|
|
131
|
-
message:
|
|
131
|
+
message: styleText('yellow', 'Configuration file not found. Would you like to create one now?'),
|
|
132
132
|
default: true,
|
|
133
133
|
}]);
|
|
134
134
|
if (shouldInit) {
|
|
135
135
|
await runInit(); // Run the interactive setup wizard (keeps existing behavior)
|
|
136
|
-
logger.info(
|
|
136
|
+
logger.info(styleText('green', 'Configuration created. Resuming command...'));
|
|
137
137
|
config = await loadConfig(configPath, logger); // Try loading the newly created config
|
|
138
138
|
if (config) {
|
|
139
139
|
return config;
|
|
140
140
|
}
|
|
141
141
|
else {
|
|
142
|
-
logger.error(
|
|
142
|
+
logger.error(styleText('red', 'Error: Failed to load configuration after creation. Please try running the command again.'));
|
|
143
143
|
process.exit(1);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSpinnerLike } from '../../utils/wrap-ora.js';
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
3
|
import { parse } from '@swc/core';
|
|
4
4
|
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
5
5
|
import { dirname, extname } from 'node:path';
|
|
@@ -69,7 +69,7 @@ async function runExtractor(config, options = {}) {
|
|
|
69
69
|
const fileContent = serializeTranslationFile(result.newTranslations, effectiveFormat, config.extract.indentation, rawContent);
|
|
70
70
|
await mkdir(dirname(result.path), { recursive: true });
|
|
71
71
|
await writeFile(result.path, fileContent);
|
|
72
|
-
internalLogger.info(
|
|
72
|
+
internalLogger.info(styleText('green', `Updated: ${result.path}`));
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -80,14 +80,14 @@ async function runExtractor(config, options = {}) {
|
|
|
80
80
|
await plugin.afterSync?.(results, config);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
spinner.succeed(
|
|
83
|
+
spinner.succeed(styleText('bold', 'Extraction complete!'));
|
|
84
84
|
// Show the funnel message only if files were actually changed.
|
|
85
85
|
if (anyFileUpdated)
|
|
86
86
|
await printLocizeFunnel(options.logger);
|
|
87
87
|
return anyFileUpdated;
|
|
88
88
|
}
|
|
89
89
|
catch (error) {
|
|
90
|
-
spinner.fail(
|
|
90
|
+
spinner.fail(styleText('red', 'Extraction failed.'));
|
|
91
91
|
// Re-throw or handle error
|
|
92
92
|
throw error;
|
|
93
93
|
}
|
|
@@ -202,7 +202,7 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
catch (error) {
|
|
205
|
-
logger.warn(`${
|
|
205
|
+
logger.warn(`${styleText('yellow', 'Skipping file due to error:')} ${file}`);
|
|
206
206
|
const err = error;
|
|
207
207
|
const msg = typeof err?.message === 'string' && err.message.trim().length > 0
|
|
208
208
|
? err.message
|
|
@@ -246,18 +246,18 @@ async function printLocizeFunnel(logger) {
|
|
|
246
246
|
return;
|
|
247
247
|
const internalLogger = logger ?? new ConsoleLogger();
|
|
248
248
|
if (typeof internalLogger.info === 'function') {
|
|
249
|
-
internalLogger.info(
|
|
249
|
+
internalLogger.info(styleText(['yellow', 'bold'], '\n💡 Tip: Tired of running the extractor manually?'));
|
|
250
250
|
internalLogger.info(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,');
|
|
251
251
|
internalLogger.info(' where keys are created and translated automatically as you code.');
|
|
252
|
-
internalLogger.info(` Learn more: ${
|
|
253
|
-
internalLogger.info(` Watch the video: ${
|
|
252
|
+
internalLogger.info(` Learn more: ${styleText('cyan', 'https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
|
|
253
|
+
internalLogger.info(` Watch the video: ${styleText('cyan', 'https://youtu.be/joPsZghT3wM')}`);
|
|
254
254
|
}
|
|
255
255
|
else {
|
|
256
|
-
console.log(
|
|
256
|
+
console.log(styleText(['yellow', 'bold'], '\n💡 Tip: Tired of running the extractor manually?'));
|
|
257
257
|
console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,');
|
|
258
258
|
console.log(' where keys are created and translated automatically as you code.');
|
|
259
|
-
console.log(` Learn more: ${
|
|
260
|
-
console.log(` Watch the video: ${
|
|
259
|
+
console.log(` Learn more: ${styleText('cyan', 'https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
|
|
260
|
+
console.log(` Watch the video: ${styleText('cyan', 'https://youtu.be/joPsZghT3wM')}`);
|
|
261
261
|
}
|
|
262
262
|
return recordFunnelShown('extract');
|
|
263
263
|
}
|
package/dist/esm/linter.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import { parse } from '@swc/core';
|
|
4
4
|
import { extname } from 'node:path';
|
|
5
5
|
import { EventEmitter } from 'node:events';
|
|
6
|
-
import
|
|
6
|
+
import { styleText } from 'node:util';
|
|
7
7
|
import { ConsoleLogger } from './utils/logger.js';
|
|
8
8
|
import { createSpinnerLike } from './utils/wrap-ora.js';
|
|
9
9
|
|
|
@@ -311,25 +311,25 @@ async function runLinterCli(config, options = {}) {
|
|
|
311
311
|
try {
|
|
312
312
|
const { success, message, files } = await linter.run();
|
|
313
313
|
if (!success) {
|
|
314
|
-
spinner.fail(
|
|
314
|
+
spinner.fail(styleText(['red', 'bold'], message));
|
|
315
315
|
// Print detailed report after spinner fails
|
|
316
316
|
for (const [file, issues] of Object.entries(files)) {
|
|
317
317
|
if (internalLogger.info)
|
|
318
|
-
internalLogger.info(
|
|
318
|
+
internalLogger.info(styleText('yellow', `\n${file}`));
|
|
319
319
|
else
|
|
320
|
-
console.log(
|
|
320
|
+
console.log(styleText('yellow', `\n${file}`));
|
|
321
321
|
issues.forEach(({ text, line, type }) => {
|
|
322
322
|
const label = type === 'interpolation' ? 'Interpolation issue' : 'Found hardcoded string';
|
|
323
323
|
if (typeof internalLogger.info === 'function')
|
|
324
|
-
internalLogger.info(` ${
|
|
324
|
+
internalLogger.info(` ${styleText('gray', `${line}:`)} ${styleText('red', 'Error:')} ${label}: "${text}"`);
|
|
325
325
|
else
|
|
326
|
-
console.log(` ${
|
|
326
|
+
console.log(` ${styleText('gray', `${line}:`)} ${styleText('red', 'Error:')} ${label}: "${text}"`);
|
|
327
327
|
});
|
|
328
328
|
}
|
|
329
329
|
process.exit(1);
|
|
330
330
|
}
|
|
331
331
|
else {
|
|
332
|
-
spinner.succeed(
|
|
332
|
+
spinner.succeed(styleText(['green', 'bold'], message));
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
catch (error) {
|
package/dist/esm/locize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
import { sep, resolve } from 'node:path';
|
|
@@ -22,9 +22,9 @@ async function checkLocizeCliExists() {
|
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
24
|
if (error.code === 'ENOENT') {
|
|
25
|
-
console.error(
|
|
26
|
-
console.log(
|
|
27
|
-
console.log(
|
|
25
|
+
console.error(styleText('red', 'Error: `locize-cli` command not found.'));
|
|
26
|
+
console.log(styleText('yellow', 'Please install it globally to use the Locize integration:'));
|
|
27
|
+
console.log(styleText('cyan', 'npm install -g locize-cli'));
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -52,7 +52,7 @@ async function checkLocizeCliExists() {
|
|
|
52
52
|
* ```
|
|
53
53
|
*/
|
|
54
54
|
async function interactiveCredentialSetup(config) {
|
|
55
|
-
console.log(
|
|
55
|
+
console.log(styleText('yellow', '\nLocize configuration is missing or invalid. Let\'s set it up!'));
|
|
56
56
|
const answers = await inquirer.prompt([
|
|
57
57
|
{
|
|
58
58
|
type: 'input',
|
|
@@ -74,7 +74,7 @@ async function interactiveCredentialSetup(config) {
|
|
|
74
74
|
},
|
|
75
75
|
]);
|
|
76
76
|
if (!answers.projectId) {
|
|
77
|
-
console.error(
|
|
77
|
+
console.error(styleText('red', 'Project ID is required to continue.'));
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
80
80
|
const { save } = await inquirer.prompt([{
|
|
@@ -96,11 +96,11 @@ LOCIZE_API_KEY=${answers.apiKey}
|
|
|
96
96
|
apiKey: process.env.LOCIZE_API_KEY,
|
|
97
97
|
version: '${answers.version}',
|
|
98
98
|
},`;
|
|
99
|
-
console.log(
|
|
100
|
-
console.log(
|
|
101
|
-
console.log(
|
|
102
|
-
console.log(
|
|
103
|
-
console.log(
|
|
99
|
+
console.log(styleText('cyan', '\nGreat! For the best security, we recommend using environment variables for your API key.'));
|
|
100
|
+
console.log(styleText('bold', '\nRecommended approach (.env file):'));
|
|
101
|
+
console.log(styleText('green', envSnippet));
|
|
102
|
+
console.log(styleText('bold', 'Then, in your i18next.config.ts:'));
|
|
103
|
+
console.log(styleText('green', configSnippet));
|
|
104
104
|
}
|
|
105
105
|
return {
|
|
106
106
|
projectId: answers.projectId,
|
|
@@ -215,9 +215,9 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
215
215
|
try {
|
|
216
216
|
// 1. First attempt
|
|
217
217
|
const initialArgs = buildArgs(command, effectiveConfig, cliOptions);
|
|
218
|
-
console.log(
|
|
218
|
+
console.log(styleText('cyan', `\nRunning 'locize ${initialArgs.join(' ')}'...`));
|
|
219
219
|
const result = await execa('locize', initialArgs, { stdio: 'pipe' });
|
|
220
|
-
spinner.succeed(
|
|
220
|
+
spinner.succeed(styleText('green', `'locize ${command}' completed successfully.`));
|
|
221
221
|
if (result?.stdout)
|
|
222
222
|
console.log(result.stdout); // Print captured output on success
|
|
223
223
|
}
|
|
@@ -232,14 +232,14 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
232
232
|
try {
|
|
233
233
|
// 3. Retry attempt, rebuilding args with the NOW-UPDATED currentConfig object
|
|
234
234
|
const retryArgs = buildArgs(command, effectiveConfig, cliOptions);
|
|
235
|
-
console.log(
|
|
235
|
+
console.log(styleText('cyan', `\nRunning 'locize ${retryArgs.join(' ')}'...`));
|
|
236
236
|
const result = await execa('locize', retryArgs, { stdio: 'pipe' });
|
|
237
|
-
spinner.succeed(
|
|
237
|
+
spinner.succeed(styleText('green', 'Retry successful!'));
|
|
238
238
|
if (result?.stdout)
|
|
239
239
|
console.log(result.stdout);
|
|
240
240
|
}
|
|
241
241
|
catch (retryError) {
|
|
242
|
-
spinner.fail(
|
|
242
|
+
spinner.fail(styleText('red', 'Error during retry.'));
|
|
243
243
|
console.error(retryError.stderr || retryError.message);
|
|
244
244
|
process.exit(1);
|
|
245
245
|
}
|
|
@@ -251,12 +251,12 @@ async function runLocizeCommand(command, config, cliOptions = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
else {
|
|
253
253
|
// Handle other errors
|
|
254
|
-
spinner.fail(
|
|
254
|
+
spinner.fail(styleText('red', `Error executing 'locize ${command}'.`));
|
|
255
255
|
console.error(stderr || error.message);
|
|
256
256
|
process.exit(1);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
-
console.log(
|
|
259
|
+
console.log(styleText('green', `\n✅ 'locize ${command}' completed successfully.`));
|
|
260
260
|
}
|
|
261
261
|
const runLocizeSync = (config, cliOptions) => runLocizeCommand('sync', config, cliOptions);
|
|
262
262
|
const runLocizeDownload = (config, cliOptions) => runLocizeCommand('download', config, cliOptions);
|
package/dist/esm/rename-key.js
CHANGED
|
@@ -5,7 +5,7 @@ import { ConsoleLogger } from './utils/logger.js';
|
|
|
5
5
|
import { getOutputPath, loadTranslationFile, serializeTranslationFile } from './utils/file-utils.js';
|
|
6
6
|
import { getNestedValue, setNestedValue } from './utils/nested-object.js';
|
|
7
7
|
import { shouldShowFunnel, recordFunnelShown } from './utils/funnel-msg-tracker.js';
|
|
8
|
-
import
|
|
8
|
+
import { styleText } from 'node:util';
|
|
9
9
|
|
|
10
10
|
const pluralSuffixes = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
11
11
|
/**
|
|
@@ -104,11 +104,11 @@ async function runRenameKey(config, oldKey, newKey, options = {}, logger = new C
|
|
|
104
104
|
async function printLocizeFunnel() {
|
|
105
105
|
if (!(await shouldShowFunnel('rename-key')))
|
|
106
106
|
return;
|
|
107
|
-
console.log(
|
|
107
|
+
console.log(styleText(['yellow', 'bold'], '\n💡 Tip: Managing translations across multiple projects?'));
|
|
108
108
|
console.log(' With Locize, you can rename, move, and copy translation keys directly');
|
|
109
109
|
console.log(' in the web interface—no CLI needed. Perfect for collaboration with');
|
|
110
110
|
console.log(' translators and managing complex refactoring across namespaces.');
|
|
111
|
-
console.log(` Learn more: ${
|
|
111
|
+
console.log(` Learn more: ${styleText('cyan', 'https://www.locize.com/docs/how-can-a-segment-key-be-copied-moved-or-renamed')}`);
|
|
112
112
|
return recordFunnelShown('rename-key');
|
|
113
113
|
}
|
|
114
114
|
function parseKeyWithNamespace(key, config) {
|
package/dist/esm/status.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
+
import '@swc/core';
|
|
5
|
+
import 'node:fs/promises';
|
|
4
6
|
import { findKeys } from './extractor/core/key-finder.js';
|
|
7
|
+
import 'glob';
|
|
5
8
|
import { getNestedValue } from './utils/nested-object.js';
|
|
6
9
|
import { loadTranslationFile, getOutputPath } from './utils/file-utils.js';
|
|
7
10
|
import { shouldShowFunnel, recordFunnelShown } from './utils/funnel-msg-tracker.js';
|
|
11
|
+
import './extractor/parsers/jsx-parser.js';
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Runs a health check on the project's i18next translations and displays a status report.
|
|
@@ -237,19 +241,19 @@ async function displayStatusReport(report, config, options) {
|
|
|
237
241
|
*/
|
|
238
242
|
async function displayDetailedLocaleReport(report, config, locale, namespaceFilter) {
|
|
239
243
|
if (locale === config.extract.primaryLanguage) {
|
|
240
|
-
console.log(
|
|
244
|
+
console.log(styleText('yellow', `Locale "${locale}" is the primary language. All keys are considered present.`));
|
|
241
245
|
return;
|
|
242
246
|
}
|
|
243
247
|
if (!config.locales.includes(locale)) {
|
|
244
|
-
console.error(
|
|
248
|
+
console.error(styleText('red', `Error: Locale "${locale}" is not defined in your configuration.`));
|
|
245
249
|
return;
|
|
246
250
|
}
|
|
247
251
|
const localeData = report.locales.get(locale);
|
|
248
252
|
if (!localeData) {
|
|
249
|
-
console.error(
|
|
253
|
+
console.error(styleText('red', `Error: Locale "${locale}" is not a valid secondary language.`));
|
|
250
254
|
return;
|
|
251
255
|
}
|
|
252
|
-
console.log(
|
|
256
|
+
console.log(styleText('bold', `\nKey Status for "${styleText('cyan', locale)}":`));
|
|
253
257
|
const totalKeysForLocale = localeData.totalKeys;
|
|
254
258
|
printProgressBar('Overall', localeData.totalTranslated, totalKeysForLocale);
|
|
255
259
|
const namespacesToDisplay = namespaceFilter ? [namespaceFilter] : Array.from(localeData.namespaces.keys()).sort();
|
|
@@ -257,19 +261,19 @@ async function displayDetailedLocaleReport(report, config, locale, namespaceFilt
|
|
|
257
261
|
const nsData = localeData.namespaces.get(ns);
|
|
258
262
|
if (!nsData)
|
|
259
263
|
continue;
|
|
260
|
-
console.log(
|
|
264
|
+
console.log(styleText(['cyan', 'bold'], `\nNamespace: ${ns}`));
|
|
261
265
|
printProgressBar('Namespace Progress', nsData.translatedKeys, nsData.totalKeys);
|
|
262
266
|
nsData.keyDetails.forEach(({ key, isTranslated }) => {
|
|
263
|
-
const icon = isTranslated ?
|
|
267
|
+
const icon = isTranslated ? styleText('green', '✓') : styleText('red', '✗');
|
|
264
268
|
console.log(` ${icon} ${key}`);
|
|
265
269
|
});
|
|
266
270
|
}
|
|
267
271
|
const missingCount = totalKeysForLocale - localeData.totalTranslated;
|
|
268
272
|
if (missingCount > 0) {
|
|
269
|
-
console.log(
|
|
273
|
+
console.log(styleText(['yellow', 'bold'], `\nSummary: Found ${missingCount} missing translations for "${locale}".`));
|
|
270
274
|
}
|
|
271
275
|
else {
|
|
272
|
-
console.log(
|
|
276
|
+
console.log(styleText(['green', 'bold'], `\nSummary: 🎉 All keys are translated for "${locale}".`));
|
|
273
277
|
}
|
|
274
278
|
await printLocizeFunnel();
|
|
275
279
|
}
|
|
@@ -286,10 +290,10 @@ async function displayDetailedLocaleReport(report, config, locale, namespaceFilt
|
|
|
286
290
|
async function displayNamespaceSummaryReport(report, config, namespace) {
|
|
287
291
|
const nsData = report.keysByNs.get(namespace);
|
|
288
292
|
if (!nsData) {
|
|
289
|
-
console.error(
|
|
293
|
+
console.error(styleText('red', `Error: Namespace "${namespace}" was not found in your source code.`));
|
|
290
294
|
return;
|
|
291
295
|
}
|
|
292
|
-
console.log(
|
|
296
|
+
console.log(styleText(['cyan', 'bold'], `\nStatus for Namespace: "${namespace}"`));
|
|
293
297
|
console.log('------------------------');
|
|
294
298
|
for (const [locale, localeData] of report.locales.entries()) {
|
|
295
299
|
const nsLocaleData = localeData.namespaces.get(namespace);
|
|
@@ -314,12 +318,13 @@ async function displayNamespaceSummaryReport(report, config, namespace) {
|
|
|
314
318
|
*/
|
|
315
319
|
async function displayOverallSummaryReport(report, config) {
|
|
316
320
|
const { primaryLanguage } = config.extract;
|
|
317
|
-
console.log(
|
|
321
|
+
console.log(styleText(['cyan', 'bold'], '\ni18next Project Status'));
|
|
318
322
|
console.log('------------------------');
|
|
319
|
-
console.log(`🔑 Keys Found: ${
|
|
320
|
-
console.log(`📚 Namespaces Found: ${
|
|
321
|
-
console.log(`🌍 Locales: ${
|
|
322
|
-
|
|
323
|
+
console.log(`🔑 Keys Found: ${styleText('bold', `${report.totalBaseKeys}`)}`);
|
|
324
|
+
console.log(`📚 Namespaces Found: ${styleText('bold', `${report.keysByNs.size}`)}`);
|
|
325
|
+
console.log(`🌍 Locales: ${styleText('bold', config.locales.join(', '))}`);
|
|
326
|
+
if (primaryLanguage)
|
|
327
|
+
console.log(`✅ Primary Language: ${styleText('bold', primaryLanguage)}`);
|
|
323
328
|
console.log('\nTranslation Progress:');
|
|
324
329
|
for (const [locale, localeData] of report.locales.entries()) {
|
|
325
330
|
const percentage = localeData.totalKeys > 0 ? Math.round((localeData.totalTranslated / localeData.totalKeys) * 100) : 100;
|
|
@@ -338,7 +343,7 @@ async function displayOverallSummaryReport(report, config) {
|
|
|
338
343
|
function printProgressBar(label, current, total) {
|
|
339
344
|
const percentage = total > 0 ? Math.round((current / total) * 100) : 100;
|
|
340
345
|
const bar = generateProgressBarText(percentage);
|
|
341
|
-
console.log(`${
|
|
346
|
+
console.log(`${styleText('bold', label)}: ${bar} ${percentage}% (${current}/${total})`);
|
|
342
347
|
}
|
|
343
348
|
/**
|
|
344
349
|
* Generates a visual progress bar string based on percentage completion.
|
|
@@ -353,14 +358,14 @@ function generateProgressBarText(percentage) {
|
|
|
353
358
|
const totalBars = 20;
|
|
354
359
|
const filledBars = Math.floor((percentage / 100) * totalBars);
|
|
355
360
|
const emptyBars = totalBars - filledBars;
|
|
356
|
-
return `[${
|
|
361
|
+
return `[${styleText('green', ''.padStart(filledBars, '■'))}${''.padStart(emptyBars, '□')}]`;
|
|
357
362
|
}
|
|
358
363
|
async function printLocizeFunnel() {
|
|
359
364
|
if (!(await shouldShowFunnel('status')))
|
|
360
365
|
return;
|
|
361
|
-
console.log(
|
|
366
|
+
console.log(styleText(['yellow', 'bold'], '\n✨ Take your localization to the next level!'));
|
|
362
367
|
console.log('Manage translations with your team in the cloud with Locize => https://www.locize.com/docs/getting-started');
|
|
363
|
-
console.log(`Run ${
|
|
368
|
+
console.log(`Run ${styleText('cyan', 'npx i18next-cli locize-migrate')} to get started.`);
|
|
364
369
|
return recordFunnelShown('status');
|
|
365
370
|
}
|
|
366
371
|
|
package/dist/esm/syncer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
2
|
import { glob } from 'glob';
|
|
3
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, resolve, dirname } from 'node:path';
|
|
@@ -73,7 +73,7 @@ async function runSyncer(config, options = {}) {
|
|
|
73
73
|
continue;
|
|
74
74
|
const primaryTranslations = await loadTranslationFile(primaryPath);
|
|
75
75
|
if (!primaryTranslations) {
|
|
76
|
-
logMessages.push(` ${
|
|
76
|
+
logMessages.push(` ${styleText('yellow', '-')} Could not read primary file: ${primaryPath}`);
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
79
|
const primaryKeys = getNestedKeys(primaryTranslations, keySeparator ?? '.');
|
|
@@ -100,27 +100,27 @@ async function runSyncer(config, options = {}) {
|
|
|
100
100
|
const serializedContent = serializeTranslationFile(newSecondaryTranslations, perFileFormat, indentation, raw);
|
|
101
101
|
await mkdir(dirname(fullSecondaryPath), { recursive: true });
|
|
102
102
|
await writeFile(fullSecondaryPath, serializedContent);
|
|
103
|
-
logMessages.push(` ${
|
|
103
|
+
logMessages.push(` ${styleText('green', '✓')} Synchronized: ${secondaryPath}`);
|
|
104
104
|
}
|
|
105
105
|
else {
|
|
106
|
-
logMessages.push(` ${
|
|
106
|
+
logMessages.push(` ${styleText('gray', '-')} Already in sync: ${secondaryPath}`);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
spinner.succeed(
|
|
110
|
+
spinner.succeed(styleText('bold', 'Synchronization complete!'));
|
|
111
111
|
logMessages.forEach(msg => internalLogger.info ? internalLogger.info(msg) : console.log(msg));
|
|
112
112
|
if (wasAnythingSynced) {
|
|
113
113
|
await printLocizeFunnel();
|
|
114
114
|
}
|
|
115
115
|
else {
|
|
116
116
|
if (typeof internalLogger.info === 'function')
|
|
117
|
-
internalLogger.info(
|
|
117
|
+
internalLogger.info(styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
118
118
|
else
|
|
119
|
-
console.log(
|
|
119
|
+
console.log(styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
catch (error) {
|
|
123
|
-
spinner.fail(
|
|
123
|
+
spinner.fail(styleText('red', 'Synchronization failed.'));
|
|
124
124
|
if (typeof internalLogger.error === 'function')
|
|
125
125
|
internalLogger.error(error);
|
|
126
126
|
else
|
|
@@ -130,9 +130,9 @@ async function runSyncer(config, options = {}) {
|
|
|
130
130
|
async function printLocizeFunnel() {
|
|
131
131
|
if (!(await shouldShowFunnel('syncer')))
|
|
132
132
|
return;
|
|
133
|
-
console.log(
|
|
134
|
-
console.log(
|
|
135
|
-
console.log(` Get started with the official TMS for i18next: ${
|
|
133
|
+
console.log(styleText(['green', 'bold'], '\n✅ Sync complete.'));
|
|
134
|
+
console.log(styleText('yellow', '🚀 Ready to collaborate with translators? Move your files to the cloud.'));
|
|
135
|
+
console.log(` Get started with the official TMS for i18next: ${styleText('cyan', 'npx i18next-cli locize-migrate')}`);
|
|
136
136
|
return recordFunnelShown('syncer');
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -2,7 +2,7 @@ import { ConsoleLogger } from './utils/logger.js';
|
|
|
2
2
|
import { mergeResourcesAsInterface } from 'i18next-resources-for-ts';
|
|
3
3
|
import { glob } from 'glob';
|
|
4
4
|
import { createSpinnerLike } from './utils/wrap-ora.js';
|
|
5
|
-
import
|
|
5
|
+
import { styleText } from 'node:util';
|
|
6
6
|
import { mkdir, writeFile, access, readFile } from 'node:fs/promises';
|
|
7
7
|
import { join, dirname, basename, extname, resolve, relative } from 'node:path';
|
|
8
8
|
import { transform } from '@swc/core';
|
|
@@ -109,7 +109,7 @@ async function runTypesGenerator(config, options = {}) {
|
|
|
109
109
|
}
|
|
110
110
|
const nonObjectKeys = keys.filter(k => !parsedContent[k] || typeof parsedContent[k] !== 'object');
|
|
111
111
|
if (nonObjectKeys.length > 0) {
|
|
112
|
-
console.warn(
|
|
112
|
+
console.warn(styleText('yellow', `Warning: The file ${file} contains top-level keys that are not objects (${nonObjectKeys.join(', ')}). When 'mergeNamespaces' is enabled, top-level keys are treated as namespaces. These keys will be ignored.`));
|
|
113
113
|
}
|
|
114
114
|
continue;
|
|
115
115
|
}
|
|
@@ -125,7 +125,7 @@ ${mergeResourcesAsInterface(resources, { optimize: !!enableSelector, indentation
|
|
|
125
125
|
const resourcesOutputPath = resolve(process.cwd(), config.types.resourcesFile);
|
|
126
126
|
await mkdir(dirname(resourcesOutputPath), { recursive: true });
|
|
127
127
|
await writeFile(resourcesOutputPath, interfaceDefinition);
|
|
128
|
-
logMessages.push(` ${
|
|
128
|
+
logMessages.push(` ${styleText('green', '✓')} Resources interface written to ${config.types.resourcesFile}`);
|
|
129
129
|
let outputPathExists;
|
|
130
130
|
try {
|
|
131
131
|
await access(outputPath);
|
|
@@ -152,13 +152,13 @@ declare module 'i18next' {
|
|
|
152
152
|
}`;
|
|
153
153
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
154
154
|
await writeFile(outputPath, fileContent);
|
|
155
|
-
logMessages.push(` ${
|
|
155
|
+
logMessages.push(` ${styleText('green', '✓')} TypeScript definitions written to ${config.types.output || ''}`);
|
|
156
156
|
}
|
|
157
|
-
spinner.succeed(
|
|
157
|
+
spinner.succeed(styleText('bold', 'TypeScript definitions generated successfully.'));
|
|
158
158
|
logMessages.forEach(msg => typeof internalLogger.info === 'function' ? internalLogger.info(msg) : console.log(msg));
|
|
159
159
|
}
|
|
160
160
|
catch (error) {
|
|
161
|
-
spinner.fail(
|
|
161
|
+
spinner.fail(styleText('red', 'Failed to generate TypeScript definitions.'));
|
|
162
162
|
if (typeof internalLogger.error === 'function')
|
|
163
163
|
internalLogger.error(error);
|
|
164
164
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-cli",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.7",
|
|
4
4
|
"description": "A unified, high-performance i18next CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,7 +63,6 @@
|
|
|
63
63
|
"memfs": "4.56.10",
|
|
64
64
|
"neostandard": "0.12.2",
|
|
65
65
|
"rollup-plugin-typescript2": "0.36.0",
|
|
66
|
-
"ts-node": "10.9.2",
|
|
67
66
|
"typescript": "5.9.3",
|
|
68
67
|
"unplugin-swc": "1.5.9",
|
|
69
68
|
"vitest": "4.0.18"
|
|
@@ -72,7 +71,6 @@
|
|
|
72
71
|
"@croct/json5-parser": "0.2.2",
|
|
73
72
|
"@swc/core": "1.15.11",
|
|
74
73
|
"yaml": "2.8.2",
|
|
75
|
-
"chalk": "5.6.2",
|
|
76
74
|
"chokidar": "5.0.0",
|
|
77
75
|
"commander": "14.0.3",
|
|
78
76
|
"execa": "9.6.1",
|
|
@@ -84,7 +82,6 @@
|
|
|
84
82
|
"minimatch": "10.1.2",
|
|
85
83
|
"ora": "9.3.0",
|
|
86
84
|
"react": "^19.2.4",
|
|
87
|
-
"react-i18next": "^16.5.4"
|
|
88
|
-
"swc-walk": "1.0.1"
|
|
85
|
+
"react-i18next": "^16.5.4"
|
|
89
86
|
}
|
|
90
87
|
}
|