i18next-cli 1.42.6 → 1.42.8

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 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 chalk = require('chalk');
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.6'); // This string is replaced with the actual version at build time by rollup
31
+ .version('1.42.8'); // 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(chalk.blue('No config file found. Attempting to detect project structure...'));
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(chalk.red('Could not automatically detect your project structure.'));
107
- console.log(`Please create a config file first by running: ${chalk.cyan('npx i18next-cli init')}`);
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(chalk.green('Project structure detected successfully!'));
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(chalk.blue('No config file found. Attempting to detect project structure...'));
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(chalk.red('Could not automatically detect your project structure.'));
171
- console.log(`Please create a config file first by running: ${chalk.cyan('npx i18next-cli init')}`);
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(chalk.green('Project structure detected successfully!'));
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(chalk.red('\n❌ Conflicts detected:'));
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(chalk.red(`\n❌ ${result.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(chalk.yellow(`\n⚠️ No usages found for "${oldKey}"`));
255
+ console.log(node_util.styleText('yellow', `\n⚠️ No usages found for "${oldKey}"`));
256
256
  }
257
257
  }
258
258
  catch (error) {
259
- console.error(chalk.red('Error renaming key:'), error);
259
+ console.error(node_util.styleText('red', 'Error renaming key:'), error);
260
260
  process.exit(1);
261
261
  }
262
262
  });
@@ -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 chalk = require('chalk');
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: chalk.yellow('Configuration file not found. Would you like to create one now?'),
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(chalk.green('Configuration created. Resuming command...'));
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(chalk.red('Error: Failed to load configuration after creation. Please try running the command again.'));
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 chalk = require('chalk');
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(chalk.green(`Updated: ${result.path}`));
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(chalk.bold('Extraction complete!'));
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(chalk.red('Extraction failed.'));
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(`${chalk.yellow('Skipping file due to error:')} ${file}`);
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(chalk.yellow.bold('\n💡 Tip: Tired of running the extractor manually?'));
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: ${chalk.cyan('https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
255
- internalLogger.info(` Watch the video: ${chalk.cyan('https://youtu.be/joPsZghT3wM')}`);
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(chalk.yellow.bold('\n💡 Tip: Tired of running the extractor manually?'));
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: ${chalk.cyan('https://www.locize.com/blog/i18next-savemissing-ai-automation')}`);
262
- console.log(` Watch the video: ${chalk.cyan('https://youtu.be/joPsZghT3wM')}`);
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
  }
@@ -796,6 +796,10 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
796
796
  const namespacesToProcess = existingIsNamespaced
797
797
  ? new Set([...keysByNS.keys(), ...existingKeys])
798
798
  : new Set([...keysByNS.keys(), NO_NS_TOKEN]);
799
+ // Remove ignored namespaces so their section is never modified
800
+ for (const ns of ignoreNamespaces) {
801
+ namespacesToProcess.delete(ns);
802
+ }
799
803
  for (const nsKey of namespacesToProcess) {
800
804
  const nsKeys = keysByNS.get(nsKey) || [];
801
805
  if (nsKey === NO_NS_TOKEN) {
@@ -808,6 +812,12 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
808
812
  newMergedTranslations[nsKey] = buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale, nsKey, preservePatterns, objectKeys, syncPrimaryWithDefaults);
809
813
  }
810
814
  }
815
+ // Preserve ignored namespaces as-is from the existing merged file
816
+ for (const ns of ignoreNamespaces) {
817
+ if (ns in existingMergedFile) {
818
+ newMergedTranslations[ns] = existingMergedFile[ns];
819
+ }
820
+ }
811
821
  const oldContent = JSON.stringify(existingMergedFile, null, indentation);
812
822
  const newContent = JSON.stringify(newMergedTranslations, null, indentation);
813
823
  // Push a single result for the merged file
@@ -833,6 +843,10 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
833
843
  namespacesToProcess.add(ns);
834
844
  }
835
845
  }
846
+ // Remove ignored namespaces so their files are never modified
847
+ for (const ns of ignoreNamespaces) {
848
+ namespacesToProcess.delete(ns);
849
+ }
836
850
  // Process each namespace individually and create a result for each one
837
851
  for (const ns of namespacesToProcess) {
838
852
  const nsKeys = keysByNS.get(ns) || [];
@@ -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 chalk = require('chalk');
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
 
@@ -23,6 +23,22 @@ function extractInterpolationKeys(str, config) {
23
23
  }
24
24
  return keys;
25
25
  }
26
+ // Known i18next t() option keys that are NOT interpolation parameters.
27
+ // These should not be flagged as "unused" in the translation string.
28
+ const i18nextOptionKeys = new Set([
29
+ 'defaultValue', 'count', 'context', 'ns', 'lng', 'lngs',
30
+ 'fallbackLng', 'returnDetails', 'returnObjects', 'joinArrays',
31
+ 'postProcess', 'interpolation', 'skipInterpolation', 'replace',
32
+ 'ordinal', 'keySeparator', 'nsSeparator', 'tDescription',
33
+ ]);
34
+ function isI18nextOptionKey(key) {
35
+ if (i18nextOptionKeys.has(key))
36
+ return true;
37
+ // defaultValue_one, defaultValue_other, defaultValue_many, etc.
38
+ if (key.startsWith('defaultValue_'))
39
+ return true;
40
+ return false;
41
+ }
26
42
  // Helper to lint interpolation parameter errors in t() calls
27
43
  function lintInterpolationParams(ast, code, config) {
28
44
  const issues = [];
@@ -132,7 +148,10 @@ function lintInterpolationParams(ast, code, config) {
132
148
  }
133
149
  }
134
150
  // For each provided param, check if it is used either directly or as the root of a dotted key.
151
+ // Skip known i18next t() option keys that are not interpolation parameters.
135
152
  for (const pk of paramKeys) {
153
+ if (isI18nextOptionKey(pk))
154
+ continue;
136
155
  const isUsed = keys.some(k => k === pk || k.split('.')[0] === pk);
137
156
  if (!isUsed) {
138
157
  issues.push({
@@ -313,25 +332,25 @@ async function runLinterCli(config, options = {}) {
313
332
  try {
314
333
  const { success, message, files } = await linter.run();
315
334
  if (!success) {
316
- spinner.fail(chalk.red.bold(message));
335
+ spinner.fail(node_util.styleText(['red', 'bold'], message));
317
336
  // Print detailed report after spinner fails
318
337
  for (const [file, issues] of Object.entries(files)) {
319
338
  if (internalLogger.info)
320
- internalLogger.info(chalk.yellow(`\n${file}`));
339
+ internalLogger.info(node_util.styleText('yellow', `\n${file}`));
321
340
  else
322
- console.log(chalk.yellow(`\n${file}`));
341
+ console.log(node_util.styleText('yellow', `\n${file}`));
323
342
  issues.forEach(({ text, line, type }) => {
324
343
  const label = type === 'interpolation' ? 'Interpolation issue' : 'Found hardcoded string';
325
344
  if (typeof internalLogger.info === 'function')
326
- internalLogger.info(` ${chalk.gray(`${line}:`)} ${chalk.red('Error:')} ${label}: "${text}"`);
345
+ internalLogger.info(` ${node_util.styleText('gray', `${line}:`)} ${node_util.styleText('red', 'Error:')} ${label}: "${text}"`);
327
346
  else
328
- console.log(` ${chalk.gray(`${line}:`)} ${chalk.red('Error:')} ${label}: "${text}"`);
347
+ console.log(` ${node_util.styleText('gray', `${line}:`)} ${node_util.styleText('red', 'Error:')} ${label}: "${text}"`);
329
348
  });
330
349
  }
331
350
  process.exit(1);
332
351
  }
333
352
  else {
334
- spinner.succeed(chalk.green.bold(message));
353
+ spinner.succeed(node_util.styleText(['green', 'bold'], message));
335
354
  }
336
355
  }
337
356
  catch (error) {
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var execa = require('execa');
4
- var chalk = require('chalk');
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(chalk.red('Error: `locize-cli` command not found.'));
28
- console.log(chalk.yellow('Please install it globally to use the Locize integration:'));
29
- console.log(chalk.cyan('npm install -g locize-cli'));
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(chalk.yellow('\nLocize configuration is missing or invalid. Let\'s set it up!'));
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(chalk.red('Project ID is required to continue.'));
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(chalk.cyan('\nGreat! For the best security, we recommend using environment variables for your API key.'));
102
- console.log(chalk.bold('\nRecommended approach (.env file):'));
103
- console.log(chalk.green(envSnippet));
104
- console.log(chalk.bold('Then, in your i18next.config.ts:'));
105
- console.log(chalk.green(configSnippet));
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(chalk.cyan(`\nRunning 'locize ${initialArgs.join(' ')}'...`));
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(chalk.green(`'locize ${command}' completed successfully.`));
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(chalk.cyan(`\nRunning 'locize ${retryArgs.join(' ')}'...`));
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(chalk.green('Retry successful!'));
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(chalk.red('Error during retry.'));
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(chalk.red(`Error executing 'locize ${command}'.`));
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(chalk.green(`\n✅ 'locize ${command}' completed successfully.`));
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);
@@ -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 chalk = require('chalk');
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(chalk.yellow.bold('\n💡 Tip: Managing translations across multiple projects?'));
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: ${chalk.cyan('https://www.locize.com/docs/how-can-a-segment-key-be-copied-moved-or-renamed')}`);
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) {
@@ -1,12 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chalk = require('chalk');
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(chalk.yellow(`Locale "${locale}" is the primary language. All keys are considered present.`));
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(chalk.red(`Error: Locale "${locale}" is not defined in your configuration.`));
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(chalk.red(`Error: Locale "${locale}" is not a valid secondary language.`));
255
+ console.error(node_util.styleText('red', `Error: Locale "${locale}" is not a valid secondary language.`));
252
256
  return;
253
257
  }
254
- console.log(chalk.bold(`\nKey Status for "${chalk.cyan(locale)}":`));
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(chalk.cyan.bold(`\nNamespace: ${ns}`));
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 ? chalk.green('✓') : chalk.red('✗');
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(chalk.yellow.bold(`\nSummary: Found ${missingCount} missing translations for "${locale}".`));
275
+ console.log(node_util.styleText(['yellow', 'bold'], `\nSummary: Found ${missingCount} missing translations for "${locale}".`));
272
276
  }
273
277
  else {
274
- console.log(chalk.green.bold(`\nSummary: 🎉 All keys are translated for "${locale}".`));
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(chalk.red(`Error: Namespace "${namespace}" was not found in your source code.`));
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(chalk.cyan.bold(`\nStatus for Namespace: "${namespace}"`));
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(chalk.cyan.bold('\ni18next Project Status'));
323
+ console.log(node_util.styleText(['cyan', 'bold'], '\ni18next Project Status'));
320
324
  console.log('------------------------');
321
- console.log(`🔑 Keys Found: ${chalk.bold(report.totalBaseKeys)}`);
322
- console.log(`📚 Namespaces Found: ${chalk.bold(report.keysByNs.size)}`);
323
- console.log(`🌍 Locales: ${chalk.bold(config.locales.join(', '))}`);
324
- console.log(`✅ Primary Language: ${chalk.bold(primaryLanguage)}`);
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(`${chalk.bold(label)}: ${bar} ${percentage}% (${current}/${total})`);
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 `[${chalk.green(''.padStart(filledBars, '■'))}${''.padStart(emptyBars, '□')}]`;
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(chalk.yellow.bold('\n✨ Take your localization to the next level!'));
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 ${chalk.cyan('npx i18next-cli locize-migrate')} to get started.`);
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
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chalk = require('chalk');
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(` ${chalk.yellow('-')} Could not read primary file: ${primaryPath}`);
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(` ${chalk.green('✓')} Synchronized: ${secondaryPath}`);
105
+ logMessages.push(` ${node_util.styleText('green', '✓')} Synchronized: ${secondaryPath}`);
106
106
  }
107
107
  else {
108
- logMessages.push(` ${chalk.gray('-')} Already in sync: ${secondaryPath}`);
108
+ logMessages.push(` ${node_util.styleText('gray', '-')} Already in sync: ${secondaryPath}`);
109
109
  }
110
110
  }
111
111
  }
112
- spinner.succeed(chalk.bold('Synchronization complete!'));
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(chalk.green.bold('\n✅ All locales are already in sync.'));
119
+ internalLogger.info(node_util.styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
120
120
  else
121
- console.log(chalk.green.bold('\n✅ All locales are already in sync.'));
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(chalk.red('Synchronization failed.'));
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(chalk.green.bold('\n✅ Sync complete.'));
136
- console.log(chalk.yellow('🚀 Ready to collaborate with translators? Move your files to the cloud.'));
137
- console.log(` Get started with the official TMS for i18next: ${chalk.cyan('npx i18next-cli locize-migrate')}`);
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