i18next-cli 0.9.9 → 0.9.11

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +25 -10
  2. package/README.md +75 -3
  3. package/dist/cjs/cli.js +1 -1
  4. package/dist/cjs/extractor/core/extractor.js +1 -1
  5. package/dist/cjs/extractor/core/translation-manager.js +1 -1
  6. package/dist/cjs/extractor/parsers/ast-visitors.js +1 -1
  7. package/dist/cjs/heuristic-config.js +1 -1
  8. package/dist/cjs/status.js +1 -1
  9. package/dist/cjs/syncer.js +1 -1
  10. package/dist/cjs/utils/file-utils.js +1 -1
  11. package/dist/esm/cli.js +1 -1
  12. package/dist/esm/extractor/core/extractor.js +1 -1
  13. package/dist/esm/extractor/core/translation-manager.js +1 -1
  14. package/dist/esm/extractor/parsers/ast-visitors.js +1 -1
  15. package/dist/esm/heuristic-config.js +1 -1
  16. package/dist/esm/status.js +1 -1
  17. package/dist/esm/syncer.js +1 -1
  18. package/dist/esm/utils/file-utils.js +1 -1
  19. package/package.json +1 -1
  20. package/src/cli.ts +1 -1
  21. package/src/extractor/core/extractor.ts +13 -7
  22. package/src/extractor/core/translation-manager.ts +38 -21
  23. package/src/extractor/parsers/ast-visitors.ts +71 -16
  24. package/src/heuristic-config.ts +14 -3
  25. package/src/status.ts +21 -13
  26. package/src/syncer.ts +77 -67
  27. package/src/types.ts +18 -0
  28. package/src/utils/file-utils.ts +54 -1
  29. package/types/extractor/core/extractor.d.ts.map +1 -1
  30. package/types/extractor/core/translation-manager.d.ts.map +1 -1
  31. package/types/extractor/parsers/ast-visitors.d.ts +5 -1
  32. package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
  33. package/types/heuristic-config.d.ts.map +1 -1
  34. package/types/status.d.ts.map +1 -1
  35. package/types/syncer.d.ts.map +1 -1
  36. package/types/types.d.ts +16 -0
  37. package/types/types.d.ts.map +1 -1
  38. package/types/utils/file-utils.d.ts +16 -1
  39. package/types/utils/file-utils.d.ts.map +1 -1
package/CHANGELOG.md CHANGED
@@ -5,31 +5,46 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.0.0](https://github.com/i18next/i18next-cli/compare/v0.9.9...v1.0.0) - 2025-xx-yy
8
+ ## [1.0.0](https://github.com/i18next/i18next-cli/compare/v0.9.11...v1.0.0) - 2025-xx-yy
9
9
 
10
10
  - not yet released
11
11
 
12
- ## [0.9.9] - 2025-09-25
12
+ ## [0.9.11](https://github.com/i18next/i18next-cli/compare/v0.9.10...v0.9.11) - 2025-09-26
13
+
14
+ ### Added
15
+ - **Extractor:** Added support for plural-specific default values (e.g., `defaultValue_other`, `defaultValue_two`) in `t()` function options. [#3](https://github.com/i18next/i18next-cli/issues/3)
16
+ - **Extractor:** Added support for ordinal plurals (e.g., `t('key', { count: 1, ordinal: true })`), generating the correct suffixed keys (`key_ordinal_one`, `key_ordinal_two`, etc.) for all languages.
17
+
18
+ ### Fixed
19
+ - **Extractor:** Fixed an issue where the AST walker would not find `t()` calls inside nested functions, such as an `array.map()` callback, within JSX. [#4](https://github.com/i18next/i18next-cli/issues/4)
20
+
21
+ ## [0.9.10] - 2025-09-25(https://github.com/i18next/i18next-cli/compare/v0.9.9...v0.9.10)
22
+
23
+ ### Added
24
+ - **JavaScript/TypeScript Translation Files:** Added the `outputFormat` option to support generating translation files as `.json` (default), `.js` (ESM or CJS), or `.ts` modules.
25
+ - **Merged Namespace Files:** Added the `mergeNamespaces` option to combine all namespaces into a single file per language, streamlining imports and file structures.
26
+
27
+ ## [0.9.9] - 2025-09-25(https://github.com/i18next/i18next-cli/compare/v0.9.8...v0.9.9)
13
28
 
14
29
  - **Extractor:** Now supports static and dynamic (ternary) `context` options in both `t()` and `<Trans>`.
15
30
 
16
- ## [0.9.8] - 2025-09-25
31
+ ## [0.9.8](https://github.com/i18next/i18next-cli/compare/v0.9.7...v0.9.8) - 2025-09-25
17
32
 
18
33
  - support t returnObjects
19
34
 
20
- ## [0.9.7] - 2025-09-25
35
+ ## [0.9.7](https://github.com/i18next/i18next-cli/compare/v0.9.6...v0.9.7) - 2025-09-25
21
36
 
22
37
  - support t key fallbacks
23
38
 
24
- ## [0.9.6] - 2025-09-25
39
+ ## [0.9.6](https://github.com/i18next/i18next-cli/compare/v0.9.5...v0.9.6) - 2025-09-25
25
40
 
26
41
  - show amount of namespaces in status output
27
42
 
28
- ## [0.9.5] - 2025-09-25
43
+ ## [0.9.5](https://github.com/i18next/i18next-cli/compare/v0.9.4...v0.9.5) - 2025-09-25
29
44
 
30
45
  - introduced ignoredTags option
31
46
 
32
- ## [0.9.4] - 2025-09-25
47
+ ## [0.9.4](https://github.com/i18next/i18next-cli/compare/v0.9.3...v0.9.4) - 2025-09-25
33
48
 
34
49
  ### Added
35
50
  - **Status Command:** Added a `--namespace` option to filter the status report by a single namespace.
@@ -38,16 +53,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
53
  - **Linter:** Corrected a persistent bug causing inaccurate line number reporting for found issues.
39
54
  - **Linter:** Significantly improved accuracy by adding heuristics to ignore URLs, paths, symbols, and common non-translatable JSX attributes (like `className`, `type`, etc.).
40
55
 
41
- ## [0.9.3] - 2025-09-25
56
+ ## [0.9.3](https://github.com/i18next/i18next-cli/compare/v0.9.2...v0.9.3) - 2025-09-25
42
57
 
43
58
  - improved heuristic-config
44
59
 
45
- ## [0.9.2] - 2025-09-25
60
+ ## [0.9.2](https://github.com/i18next/i18next-cli/compare/v0.9.1...v0.9.2) - 2025-09-25
46
61
 
47
62
  - added new paths for heuristic-config
48
63
  - fix some other dependencies
49
64
 
50
- ## [0.9.1] - 2025-09-25
65
+ ## [0.9.1](https://github.com/i18next/i18next-cli/compare/v0.9.0...v0.9.1) - 2025-09-25
51
66
 
52
67
  - move glob from devDependency to dependency
53
68
 
package/README.md CHANGED
@@ -271,6 +271,13 @@ export default defineConfig({
271
271
  extract: {
272
272
  input: ['src/**/*.{ts,tsx}'],
273
273
  output: 'locales/{{language}}/{{namespace}}.json',
274
+
275
+ // Use '.ts' files with `export default` instead of '.json'
276
+ outputFormat: 'ts',
277
+
278
+ // Combine all namespaces into a single file per language (e.g., locales/en.ts)
279
+ // Note: `output` path must not contain `{{namespace}}` when this is true.
280
+ mergeNamespaces: false,
274
281
 
275
282
  // Translation functions to detect
276
283
  functions: ['t', 'i18n.t', 'i18next.t'],
@@ -416,6 +423,59 @@ Extract keys from comments for documentation or edge cases:
416
423
  // t('user.greeting', { defaultValue: 'Hello!', ns: 'common' })
417
424
  ```
418
425
 
426
+ ### JavaScript & TypeScript Translation Files
427
+
428
+ For projects that prefer to keep everything in a single module type, you can configure the CLI to output JavaScript or TypeScript files instead of JSON.
429
+
430
+ Configuration (`i18next.config.ts`):
431
+
432
+ ```typescript
433
+ export default defineConfig({
434
+ extract: {
435
+ output: 'src/locales/{{language}}/{{namespace}}.ts', // Note the .ts extension
436
+ outputFormat: 'ts', // Use TypeScript with ES Modules
437
+ }
438
+ });
439
+ ```
440
+
441
+ This will generate files like `src/locales/en/translation.ts` with the following content:
442
+
443
+ ```typescript
444
+ export default {
445
+ "myKey": "My value"
446
+ } as const;
447
+ ```
448
+
449
+ ### Merging Namespaces
450
+
451
+ You can also combine all namespaces into a single file per language. This is useful for reducing the number of network requests in some application setups.
452
+
453
+ Configuration (`i18next.config.ts`):
454
+
455
+ ```typescript
456
+ export default defineConfig({
457
+ extract: {
458
+ // Note: The `output` path no longer contains the {{namespace}} placeholder
459
+ output: 'src/locales/{{language}}.ts',
460
+ outputFormat: 'ts',
461
+ mergeNamespaces: true,
462
+ }
463
+ });
464
+ ```
465
+
466
+ This will generate a single file per language, like `src/locales/en.ts`, with namespaces as top-level keys:
467
+
468
+ ```typescript
469
+ export default {
470
+ "translation": {
471
+ "key1": "Value 1"
472
+ },
473
+ "common": {
474
+ "keyA": "Value A"
475
+ }
476
+ } as const;
477
+ ```
478
+
419
479
  ## Migration from i18next-parser
420
480
 
421
481
  Automatically migrate from legacy `i18next-parser.config.js`:
@@ -465,6 +525,7 @@ t('invalid.key'); // ❌ TypeScript error
465
525
  The toolkit automatically detects these i18next usage patterns:
466
526
 
467
527
  ### Function Calls
528
+
468
529
  ```javascript
469
530
  // Basic usage
470
531
  t('key')
@@ -479,22 +540,32 @@ t('key', { ns: 'namespace' })
479
540
  t('key', { name: 'John' })
480
541
 
481
542
  // With plurals and context
482
- t('key', { count: 1 });
543
+ t('key', { count: 1 }); // Cardinal plural
483
544
  t('keyWithContext', { context: 'male' });
484
545
  t('keyWithDynContext', { context: isMale ? 'male' : 'female' });
485
546
 
547
+ // With ordinal plurals
548
+ t('place', { count: 1, ordinal: true });
549
+ t('place', {
550
+ count: 2,
551
+ ordinal: true,
552
+ defaultValue_ordinal_one: '{{count}}st place',
553
+ defaultValue_ordinal_two: '{{count}}nd place',
554
+ defaultValue_ordinal_other: '{{count}}th place'
555
+ });
556
+
486
557
  // With key fallbacks
487
558
  t(['key.primary', 'key.fallback']);
488
- t(['key.primary', 'key.fallback'], 'The fallback value');
489
559
  t(['key.primary', 'key.fallback'], { defaultValue: 'The fallback value' });
490
560
 
491
561
  // With structured content (returnObjects)
492
562
  t('countries', { returnObjects: true });
493
563
  ```
494
564
 
495
- The extractor correctly handles pluralization (`count`) and context options, generating all necessary suffixed keys (e.g., `key_one`, `key_other`, `keyWithContext_male`). It can even statically analyze ternary expressions in the `context` option to extract all possible variations.
565
+ The extractor correctly handles **cardinal and ordinal plurals** (`count`), as well as context options, generating all necessary suffixed keys (e.g., `key_one`, `key_ordinal_one`, `keyWithContext_male`). It can even statically analyze ternary expressions in the `context` option to extract all possible variations.
496
566
 
497
567
  ### React Components
568
+
498
569
  ```jsx
499
570
  // Trans component
500
571
  <Trans i18nKey="welcome">Welcome {{name}}</Trans>
@@ -508,6 +579,7 @@ const { t } = useTranslation(['ns1', 'ns2']);
508
579
  ```
509
580
 
510
581
  ### Complex Patterns
582
+
511
583
  ```javascript
512
584
  // Aliased functions
513
585
  const translate = t;
package/dist/cjs/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var e=require("commander"),t=require("chokidar"),o=require("glob"),n=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:fs/promises"),require("node:path");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.9"),f.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a);e.ci&&t&&(console.error(n.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(n.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${n.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}),f.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,t)=>{let o=await i.loadConfig();if(!o){console.log(n.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),o=e}await g.runStatus(o,{detail:e,namespace:t.namespace})}),f.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const n=await i.ensureConfig(),a=()=>c.runTypesGenerator(n);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(n.types?.input||[]),{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),f.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=await i.ensureConfig();await s.runSyncer(e)}),f.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await l.runMigrator()}),f.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(u.runInit),f.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let e=await i.loadConfig();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await d.runLinter(e)}),f.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
2
+ "use strict";var e=require("commander"),t=require("chokidar"),o=require("glob"),n=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.11"),f.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a);e.ci&&t&&(console.error(n.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(n.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${n.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}),f.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,t)=>{let o=await i.loadConfig();if(!o){console.log(n.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),o=e}await g.runStatus(o,{detail:e,namespace:t.namespace})}),f.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const n=await i.ensureConfig(),a=()=>c.runTypesGenerator(n);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(n.types?.input||[]),{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),f.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=await i.ensureConfig();await s.runSyncer(e)}),f.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await l.runMigrator()}),f.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(u.runInit),f.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let e=await i.loadConfig();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await d.runLinter(e)}),f.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
@@ -1 +1 @@
1
- "use strict";var e=require("ora"),t=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),n=require("node:path"),o=require("./key-finder.js"),s=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../plugin-manager.js"),l=require("../parsers/comment-parser.js"),u=require("../../utils/logger.js");function g(e,t,r,a=new u.ConsoleLogger){if(e&&"object"==typeof e){for(const n of t)try{n.onVisitNode?.(e,r)}catch(e){a.warn(`Plugin ${n.name} onVisitNode failed:`,e)}for(const n of Object.keys(e)){const o=e[n];if(Array.isArray(o))for(const e of o)e&&"object"==typeof e&&g(e,t,r,a);else o&&"object"==typeof o&&g(o,t,r,a)}}}exports.extract=async function(e){e.extract.primaryLanguage||(e.extract.primaryLanguage=e.locales[0]),e.extract.secondaryLanguages||(e.extract.secondaryLanguages=e.locales.filter(t=>t!==e?.extract?.primaryLanguage)),e.extract.functions||(e.extract.functions=["t"]),e.extract.transComponents||(e.extract.transComponents=["Trans"]);const{allKeys:t,objectKeys:r}=await o.findKeys(e);return s.getTranslations(t,r,e)},exports.processFile=async function(e,t,n,o,s=new u.ConsoleLogger){try{let i=await a.readFile(e,"utf-8");for(const r of t.plugins||[])i=await(r.onLoad?.(i,e))??i;const u=await r.parse(i,{syntax:"typescript",tsx:!0,comments:!0}),f=c.createPluginContext(n);l.extractKeysFromComments(i,t.extract.functions||["t"],f,t),o.visit(u),(t.plugins||[]).length>0&&g(u,t.plugins||[],f,s)}catch(t){throw new i.ExtractorError("Failed to process file",e,t)}},exports.runExtractor=async function(r,c=new u.ConsoleLogger){r.extract.primaryLanguage||(r.extract.primaryLanguage=r.locales[0]||"en"),r.extract.secondaryLanguages||(r.extract.secondaryLanguages=r.locales.filter(e=>e!==r?.extract?.primaryLanguage)),i.validateExtractorConfig(r);const l=e("Running i18next key extractor...\n").start();try{const{allKeys:e,objectKeys:i}=await o.findKeys(r,c);l.text=`Found ${e.size} unique keys. Updating translation files...`;const u=await s.getTranslations(e,i,r);let g=!1;for(const e of u)e.updated&&(g=!0,await a.mkdir(n.dirname(e.path),{recursive:!0}),await a.writeFile(e.path,JSON.stringify(e.newTranslations,null,2)),c.info(t.green(`Updated: ${e.path}`)));return l.succeed(t.bold("Extraction complete!")),g}catch(e){throw l.fail(t.red("Extraction failed.")),e}};
1
+ "use strict";var e=require("ora"),t=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),n=require("node:path"),o=require("./key-finder.js"),s=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../plugin-manager.js"),l=require("../parsers/comment-parser.js"),u=require("../../utils/logger.js"),f=require("../../utils/file-utils.js");function g(e,t,r,a=new u.ConsoleLogger){if(e&&"object"==typeof e){for(const n of t)try{n.onVisitNode?.(e,r)}catch(e){a.warn(`Plugin ${n.name} onVisitNode failed:`,e)}for(const n of Object.keys(e)){const o=e[n];if(Array.isArray(o))for(const e of o)e&&"object"==typeof e&&g(e,t,r,a);else o&&"object"==typeof o&&g(o,t,r,a)}}}exports.extract=async function(e){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(t=>t!==e?.extract?.primaryLanguage),e.extract.functions||=["t"],e.extract.transComponents||=["Trans"];const{allKeys:t,objectKeys:r}=await o.findKeys(e);return s.getTranslations(t,r,e)},exports.processFile=async function(e,t,n,o,s=new u.ConsoleLogger){try{let i=await a.readFile(e,"utf-8");for(const r of t.plugins||[])i=await(r.onLoad?.(i,e))??i;const u=await r.parse(i,{syntax:"typescript",tsx:!0,comments:!0}),f=c.createPluginContext(n);l.extractKeysFromComments(i,t.extract.functions||["t"],f,t),o.visit(u),(t.plugins||[]).length>0&&g(u,t.plugins||[],f,s)}catch(t){throw new i.ExtractorError("Failed to process file",e,t)}},exports.runExtractor=async function(r,c=new u.ConsoleLogger){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(e=>e!==r?.extract?.primaryLanguage),i.validateExtractorConfig(r);const l=e("Running i18next key extractor...\n").start();try{const{allKeys:e,objectKeys:i}=await o.findKeys(r,c);l.text=`Found ${e.size} unique keys. Updating translation files...`;const u=await s.getTranslations(e,i,r);let g=!1;for(const e of u)if(e.updated){g=!0;const o=f.serializeTranslationFile(e.newTranslations,r.extract.outputFormat,r.extract.indentation);await a.mkdir(n.dirname(e.path),{recursive:!0}),await a.writeFile(e.path,o),c.info(t.green(`Updated: ${e.path}`))}return l.succeed(t.bold("Extraction complete!")),g}catch(e){throw l.fail(t.red("Extraction failed.")),e}};
@@ -1 +1 @@
1
- "use strict";var e=require("node:fs/promises"),t=require("node:path"),s=require("../../utils/nested-object.js"),a=require("../../utils/file-utils.js");function r(e){const t=`^${e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(t)}exports.getTranslations=async function(n,o,c){const u=c.extract.defaultNS??"translation",l=c.extract.keySeparator??".",i=[...c.extract.preservePatterns||[]];for(const e of o)i.push(`${e}.*`);const p=i.map(r);c.extract.primaryLanguage||(c.extract.primaryLanguage=c.locales[0]||"en"),c.extract.secondaryLanguages||(c.extract.secondaryLanguages=c.locales.filter(e=>e!==c.extract.primaryLanguage));const g=new Map;for(const e of n.values()){const t=e.ns||u;g.has(t)||g.set(t,[]),g.get(t).push(e)}const f=[];for(const r of c.locales)for(const[n,o]of g.entries()){const u=a.getOutputPath(c.extract.output,r,n),i=t.resolve(process.cwd(),u);let g="",d={};try{g=await e.readFile(i,"utf-8"),d=JSON.parse(g)}catch(e){}const x={},y=s.getNestedKeys(d,l);for(const e of y)if(p.some(t=>t.test(e))){const t=s.getNestedValue(d,e,l);s.setNestedValue(x,e,t,l)}const h=!1===c.extract.sort?o:o.sort((e,t)=>e.key.localeCompare(t.key));for(const{key:e,defaultValue:t}of h){const a=s.getNestedValue(d,e,l)??(r===c.extract?.primaryLanguage?t:"");s.setNestedValue(x,e,a,l)}const m=c.extract.indentation??2,N=JSON.stringify(x,null,m);f.push({path:i,updated:N!==g,newTranslations:x,existingTranslations:d})}return f};
1
+ "use strict";var t=require("node:path"),e=require("../../utils/nested-object.js"),s=require("../../utils/file-utils.js");function a(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}exports.getTranslations=async function(n,r,o){const c=o.extract.defaultNS??"translation",i=o.extract.keySeparator??".",l=[...o.extract.preservePatterns||[]],u=o.extract.mergeNamespaces??!1;for(const t of r)l.push(`${t}.*`);const p=l.map(a);o.extract.primaryLanguage||=o.locales[0]||"en",o.extract.secondaryLanguages||=o.locales.filter(t=>t!==o?.extract?.primaryLanguage);const g=o.extract.primaryLanguage,f=new Map;for(const t of n.values()){const e=t.ns||c;f.has(e)||f.set(e,[]),f.get(e).push(t)}const d=[];for(const a of o.locales){const n={},r={};for(const[c,l]of f.entries()){const f=s.getOutputPath(o.extract.output,a,u?void 0:c),x=t.resolve(process.cwd(),f),y=await s.loadTranslationFile(x)||{},h={},N=e.getNestedKeys(y,i);for(const t of N)if(p.some(e=>e.test(t))){const s=e.getNestedValue(y,t,i);e.setNestedValue(h,t,s,i)}const m=!1===o.extract.sort?l:[...l].sort((t,e)=>t.key.localeCompare(e.key));for(const{key:t,defaultValue:s}of m){const n=e.getNestedValue(y,t,i)??(a===g?s:o.extract.defaultValue??"");e.setNestedValue(h,t,n,i)}if(u)n[c]=h,Object.keys(y).length>0&&(r[c]=y);else{const t=y?JSON.stringify(y,null,o.extract.indentation??2):"",e=JSON.stringify(h,null,o.extract.indentation??2);d.push({path:x,updated:e!==t,newTranslations:h,existingTranslations:y})}}if(u){const e=s.getOutputPath(o.extract.output,a),c=t.resolve(process.cwd(),e),i=Object.keys(r).length>0?JSON.stringify(r,null,o.extract.indentation??2):"",l=JSON.stringify(n,null,o.extract.indentation??2);d.push({path:c,updated:l!==i,newTranslations:n,existingTranslations:r})}}return d};
@@ -1 +1 @@
1
- "use strict";var e=require("./jsx-parser.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||[]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;let s,a;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?a=t:"StringLiteral"===t.type&&(s=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(a=t)}const o=a?this.getObjectPropValue(a,"defaultValue"):void 0,l="string"==typeof o?o:s;for(let e=0;e<r.length;e++){let t,i=r[e];if("ObjectExpression"===a?.type){const e=this.getObjectPropValue(a,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const s=this.config.extract.nsSeparator??":";if(!t&&s&&i.includes(s)){const e=i.split(s);t=e.shift(),i=e.join(s)}t||(t=this.config.extract.defaultNS);let o=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1&&l||i;if("ObjectExpression"===a?.type){const e=this.getObjectProperty(a,"context");if("ConditionalExpression"===e?.value?.type){const n=this.resolvePossibleStringValues(e.value),i=this.config.extract.contextSeparator??"_";if(n.length>0){n.forEach(e=>{this.pluginContext.addKey({key:`${o}${i}${e}`,ns:t,defaultValue:p})}),this.pluginContext.addKey({key:o,ns:t,defaultValue:p});continue}}const n=this.getObjectPropValue(a,"context");if("string"==typeof n&&n){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${o}${e}${n}`,ns:t,defaultValue:p});continue}if(void 0!==this.getObjectPropValue(a,"count")){this.handlePluralKeys(o,p,t);continue}!0===this.getObjectPropValue(a,"returnObjects")&&this.objectKeys.add(o)}this.pluginContext.addKey({key:o,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const i of e)this.pluginContext.addKey({key:`${n.key}${t}${i}`,ns:n.ns,defaultValue:n.defaultValue});this.pluginContext.addKey(n)}}else n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getObjectProperty(e,t){return e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t))}};
1
+ "use strict";var e=require("./jsx-parser.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||["t"]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;let s,a;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?a=t:"StringLiteral"===t.type&&(s=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(a=t)}const o=a?this.getObjectPropValue(a,"defaultValue"):void 0,l="string"==typeof o?o:s;for(let e=0;e<r.length;e++){let t,i=r[e];if(a){const e=this.getObjectPropValue(a,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const s=this.config.extract.nsSeparator??":";if(!t&&s&&i.includes(s)){const e=i.split(s);t=e.shift(),i=e.join(s)}t||(t=this.config.extract.defaultNS);let o=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1&&l||i;if(a){const e=this.getObjectProperty(a,"context");if("ConditionalExpression"===e?.value?.type){const n=this.resolvePossibleStringValues(e.value),i=this.config.extract.contextSeparator??"_";if(n.length>0){n.forEach(e=>{this.pluginContext.addKey({key:`${o}${i}${e}`,ns:t,defaultValue:p})}),this.pluginContext.addKey({key:o,ns:t,defaultValue:p});continue}}const n=this.getObjectPropValue(a,"context");if("string"==typeof n&&n){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${o}${e}${n}`,ns:t,defaultValue:p});continue}if(void 0!==this.getObjectPropValue(a,"count")){this.handlePluralKeys(o,t,a);continue}!0===this.getObjectPropValue(a,"returnObjects")&&this.objectKeys.add(o)}this.pluginContext.addKey({key:o,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=!0===this.getObjectPropValue(n,"ordinal"),r=i?"ordinal":"cardinal",s=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:r}).resolvedOptions().pluralCategories,a=this.config.extract.pluralSeparator??"_",o=this.getObjectPropValue(n,"defaultValue"),l=this.getObjectPropValue(n,"defaultValue_other");for(const r of s){const s=i?`defaultValue_ordinal_${r}`:`defaultValue_${r}`,p=this.getObjectPropValue(n,s);let u;u="string"==typeof p?p:"one"===r&&"string"==typeof o?o:"string"==typeof l?l:"string"==typeof o?o:e;const c=i?`${e}${a}ordinal${a}${r}`:`${e}${a}${r}`;this.pluginContext.addKey({key:c,ns:t,defaultValue:u,hasCount:!0})}}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const r=this.getObjectPropValue(n,"defaultValue");this.pluginContext.addKey({key:e,ns:t,defaultValue:"string"==typeof r?r:e})}}handleSimplePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const i of e)this.pluginContext.addKey({key:`${n.key}${t}${i}`,ns:n.ns,defaultValue:n.defaultValue});this.pluginContext.addKey(n)}}else n.hasCount?this.handleSimplePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getObjectProperty(e,t){return e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t))}};
@@ -1 +1 @@
1
- "use strict";var e=require("glob"),s=require("node:fs/promises"),n=require("node:path");const o=["public/locales/dev/*.json","locales/dev/*.json","src/locales/dev/*.json","src/assets/locales/dev/*.json","app/i18n/locales/dev/*.json","public/locales/en/*.json","locales/en/*.json","src/locales/en/*.json","src/assets/locales/en/*.json","app/i18n/locales/en/*.json"];exports.detectConfig=async function(){for(const t of o){const o=await e.glob(t,{ignore:"node_modules/**"});if(o.length>0){const e=o[0],t=n.dirname(n.dirname(e));try{let e=(await s.readdir(t)).filter(e=>/^(dev|[a-z]{2}(-[A-Z]{2})?)$/.test(e));if(e.length>0)return e.sort(),e.includes("dev")&&(e=["dev",...e.filter(e=>"dev"!==e)]),e.includes("en")&&(e=["en",...e.filter(e=>"en"!==e)]),{locales:e,extract:{input:["src/**/*.{js,jsx,ts,tsx}","app/**/*.{js,jsx,ts,tsx}","pages/**/*.{js,jsx,ts,tsx}","components/**/*.{js,jsx,ts,tsx}"],output:n.join(t,"{{language}}","{{namespace}}.json"),primaryLanguage:e.includes("en")?"en":e[0]}}}catch{continue}}}return null};
1
+ "use strict";var e=require("glob"),s=require("node:fs/promises"),n=require("node:path");const t=["public/locales/dev/*.json","locales/dev/*.json","src/locales/dev/*.json","src/assets/locales/dev/*.json","app/i18n/locales/dev/*.json","public/locales/en/*.json","locales/en/*.json","src/locales/en/*.json","src/assets/locales/en/*.json","app/i18n/locales/en/*.json"];exports.detectConfig=async function(){for(const o of t){const t=await e.glob(o,{ignore:"node_modules/**"});if(t.length>0){const e=t[0],o=n.dirname(n.dirname(e)),l=n.extname(e);let a="json";".ts"===l?a="ts":".js"===l&&(a="js");try{let e=(await s.readdir(o)).filter(e=>/^(dev|[a-z]{2}(-[A-Z]{2})?)$/.test(e));if(e.length>0)return e.sort(),e.includes("dev")&&(e=["dev",...e.filter(e=>"dev"!==e)]),e.includes("en")&&(e=["en",...e.filter(e=>"en"!==e)]),{locales:e,extract:{input:["src/**/*.{js,jsx,ts,tsx}","app/**/*.{js,jsx,ts,tsx}","pages/**/*.{js,jsx,ts,tsx}","components/**/*.{js,jsx,ts,tsx}"],output:n.join(o,"{{language}}",`{{namespace}}${l}`),outputFormat:a,primaryLanguage:e.includes("en")?"en":e[0]}}}catch{continue}}}return null};
@@ -1 +1 @@
1
- "use strict";var e=require("chalk"),o=require("ora"),t=require("node:path"),a=require("node:fs/promises"),s=require("./extractor/core/key-finder.js"),n=require("./utils/nested-object.js"),r=require("./utils/file-utils.js");function l(o,t,a){const s=a>0?Math.round(t/a*100):100,n=c(s);console.log(`${e.bold(o)}: ${n} ${s}% (${t}/${a})`)}function c(o){const t=Math.round(o/100*20),a=20-t;return`[${e.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function i(){console.log(e.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${e.cyan("npx i18next-cli locize-migrate")} to get started.`)}exports.runStatus=async function(u,y={}){u.extract.primaryLanguage||(u.extract.primaryLanguage=u.locales[0]||"en"),u.extract.secondaryLanguages||(u.extract.secondaryLanguages=u.locales.filter(e=>e!==u?.extract?.primaryLanguage));const d=o("Analyzing project localization status...\n").start();try{const o=await async function(e){const{allKeys:o}=await s.findKeys(e),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=e.extract,u=e.locales.filter(e=>e!==l),y=new Map;for(const e of o.values()){const o=e.ns||i;y.has(o)||y.set(o,[]),y.get(o).push(e)}const d={totalKeys:o.size,keysByNs:y,locales:new Map};for(const o of u){let s=0;const l=new Map;for(const[i,u]of y.entries()){const y=r.getOutputPath(e.extract.output,o,i);let d={};try{const e=await a.readFile(t.resolve(process.cwd(),y),"utf-8");d=JSON.parse(e)}catch{}let g=0;const f=u.map(({key:e})=>{const o=!!n.getNestedValue(d,e,c??".");return o&&g++,{key:e,isTranslated:o}});l.set(i,{totalKeys:u.length,translatedKeys:g,keyDetails:f}),s+=g}d.locales.set(o,{totalTranslated:s,namespaces:l})}return d}(u);d.succeed("Analysis complete."),function(o,t,a){a.detail?function(o,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(e.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(e.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=o.locales.get(a);if(!n)return void console.error(e.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(e.bold(`\nKey Status for "${e.cyan(a)}":`));const r=Array.from(o.keysByNs.values()).flat().length;l("Overall",n.totalTranslated,r);const c=s?[s]:Array.from(n.namespaces.keys()).sort();for(const o of c){const t=n.namespaces.get(o);t&&(console.log(e.cyan.bold(`\nNamespace: ${o}`)),l("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:o,isTranslated:t})=>{const a=t?e.green("✓"):e.red("✗");console.log(` ${a} ${o}`)}))}const u=r-n.totalTranslated;u>0?console.log(e.yellow.bold(`\nSummary: Found ${u} missing translations for "${a}".`)):console.log(e.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));i()}(o,t,a.detail,a.namespace):a.namespace?function(o,t,a){const s=o.keysByNs.get(a);if(!s)return void console.error(e.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(e.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[e,t]of o.locales.entries()){const o=t.namespaces.get(a);if(o){const t=o.totalKeys>0?Math.round(o.translatedKeys/o.totalKeys*100):100,a=c(t);console.log(`- ${e}: ${a} ${t}% (${o.translatedKeys}/${o.totalKeys} keys)`)}}i()}(o,0,a.namespace):function(o,t){const{primaryLanguage:a}=t.extract;console.log(e.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${e.bold(o.totalKeys)}`),console.log(`📚 Namespaces Found: ${e.bold(o.keysByNs.size)}`),console.log(`🌍 Locales: ${e.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${e.bold(a)}`),console.log("\nTranslation Progress:");for(const[e,t]of o.locales.entries()){const a=o.totalKeys>0?Math.round(t.totalTranslated/o.totalKeys*100):100,s=c(a);console.log(`- ${e}: ${s} ${a}% (${t.totalTranslated}/${o.totalKeys} keys)`)}i()}(o,t)}(o,u,y)}catch(e){d.fail("Failed to generate status report."),console.error(e)}};
1
+ "use strict";var e=require("chalk"),o=require("ora"),t=require("node:path"),a=require("./extractor/core/key-finder.js"),s=require("./utils/nested-object.js"),n=require("./utils/file-utils.js");function l(o,t,a){const s=a>0?Math.round(t/a*100):100,n=r(s);console.log(`${e.bold(o)}: ${n} ${s}% (${t}/${a})`)}function r(o){const t=Math.round(o/100*20),a=20-t;return`[${e.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function c(){console.log(e.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${e.cyan("npx i18next-cli locize-migrate")} to get started.`)}exports.runStatus=async function(i,u={}){i.extract.primaryLanguage||=i.locales[0]||"en",i.extract.secondaryLanguages||=i.locales.filter(e=>e!==i?.extract?.primaryLanguage);const d=o("Analyzing project localization status...\n").start();try{const o=await async function(e){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(o=>o!==e?.extract?.primaryLanguage);const{allKeys:o}=await a.findKeys(e),{secondaryLanguages:l,keySeparator:r=".",defaultNS:c="translation",mergeNamespaces:i=!1}=e.extract,u=new Map;for(const e of o.values()){const o=e.ns||c;u.has(o)||u.set(o,[]),u.get(o).push(e)}const d={totalKeys:o.size,keysByNs:u,locales:new Map};for(const o of l){let a=0;const l=new Map,c=i?await n.loadTranslationFile(t.resolve(process.cwd(),n.getOutputPath(e.extract.output,o)))||{}:null;for(const[d,y]of u.entries()){const u=i?c?.[d]||{}:await n.loadTranslationFile(t.resolve(process.cwd(),n.getOutputPath(e.extract.output,o,d)))||{};let g=0;const f=y.map(({key:e})=>{const o=!!s.getNestedValue(u,e,r??".");return o&&g++,{key:e,isTranslated:o}});l.set(d,{totalKeys:y.length,translatedKeys:g,keyDetails:f}),a+=g}d.locales.set(o,{totalTranslated:a,namespaces:l})}return d}(i);d.succeed("Analysis complete."),function(o,t,a){a.detail?function(o,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(e.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(e.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=o.locales.get(a);if(!n)return void console.error(e.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(e.bold(`\nKey Status for "${e.cyan(a)}":`));const r=Array.from(o.keysByNs.values()).flat().length;l("Overall",n.totalTranslated,r);const i=s?[s]:Array.from(n.namespaces.keys()).sort();for(const o of i){const t=n.namespaces.get(o);t&&(console.log(e.cyan.bold(`\nNamespace: ${o}`)),l("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:o,isTranslated:t})=>{const a=t?e.green("✓"):e.red("✗");console.log(` ${a} ${o}`)}))}const u=r-n.totalTranslated;u>0?console.log(e.yellow.bold(`\nSummary: Found ${u} missing translations for "${a}".`)):console.log(e.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));c()}(o,t,a.detail,a.namespace):a.namespace?function(o,t,a){const s=o.keysByNs.get(a);if(!s)return void console.error(e.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(e.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[e,t]of o.locales.entries()){const o=t.namespaces.get(a);if(o){const t=o.totalKeys>0?Math.round(o.translatedKeys/o.totalKeys*100):100,a=r(t);console.log(`- ${e}: ${a} ${t}% (${o.translatedKeys}/${o.totalKeys} keys)`)}}c()}(o,0,a.namespace):function(o,t){const{primaryLanguage:a}=t.extract;console.log(e.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${e.bold(o.totalKeys)}`),console.log(`📚 Namespaces Found: ${e.bold(o.keysByNs.size)}`),console.log(`🌍 Locales: ${e.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${e.bold(a)}`),console.log("\nTranslation Progress:");for(const[e,t]of o.locales.entries()){const a=o.totalKeys>0?Math.round(t.totalTranslated/o.totalKeys*100):100,s=r(a);console.log(`- ${e}: ${s} ${a}% (${t.totalTranslated}/${o.totalKeys} keys)`)}c()}(o,t)}(o,i,u)}catch(e){d.fail("Failed to generate status report."),console.error(e)}};
@@ -1 +1 @@
1
- "use strict";var e=require("node:fs/promises"),t=require("path"),r=require("chalk"),o=require("ora"),a=require("./utils/nested-object.js"),n=require("./utils/file-utils.js");exports.runSyncer=async function(c){const l=o("Running i18next locale synchronizer...\n").start();c.extract.primaryLanguage||=c.locales[0]||"en";const{primaryLanguage:s}=c.extract,i=c.locales.filter(e=>e!==s),u=c.extract.keySeparator??".",d=[];let y=!1;const g=c.extract.defaultNS??"translation",f=n.getOutputPath(c.extract.output,s,g),p=t.resolve(process.cwd(),f);let h;try{const t=await e.readFile(p,"utf-8");h=JSON.parse(t)}catch(e){return void console.error(`Primary language file not found at ${f}. Cannot sync.`)}const x=a.getNestedKeys(h,u);for(const o of i){const l=n.getOutputPath(c.extract.output,o,g),s=t.resolve(process.cwd(),l);let i={},f="";try{f=await e.readFile(s,"utf-8"),i=JSON.parse(f)}catch(e){}const p={};for(const e of x){const t=a.getNestedValue(i,e,u)??(c.extract?.defaultValue||"");a.setNestedValue(p,e,t,u)}const h=c.extract.indentation??2,w=JSON.stringify(p,null,h);w!==f?(y=!0,await e.mkdir(t.dirname(s),{recursive:!0}),await e.writeFile(s,w),d.push(` ${r.green("✓")} Synchronized: ${l}`)):d.push(` ${r.gray("-")} Already in sync: ${l}`)}l.succeed(r.bold("Synchronization complete!")),d.forEach(e=>console.log(e)),y?(console.log(r.green.bold("\n✅ Sync complete.")),console.log(r.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${r.cyan("npx i18next-cli locize-migrate")}`)):console.log(r.green.bold("\n✅ All locales are already in sync."))};
1
+ "use strict";var e=require("node:fs/promises"),o=require("path"),t=require("chalk"),n=require("ora"),r=require("glob"),a=require("./utils/nested-object.js"),i=require("./utils/file-utils.js");exports.runSyncer=async function(l){const s=n("Running i18next locale synchronizer...\n").start();try{const n=l.extract.primaryLanguage||l.locales[0]||"en",c=l.locales.filter(e=>e!==n),{output:u,keySeparator:d=".",outputFormat:f="json",indentation:y=2,defaultValue:g=""}=l.extract,h=[];let p=!1;const w=i.getOutputPath(u,n,"*"),m=await r.glob(w);if(0===m.length)return void s.warn(`No translation files found for primary language "${n}". Nothing to sync.`);for(const n of m){const r=o.basename(n).split(".")[0],l=await i.loadTranslationFile(n);if(!l){h.push(` ${t.yellow("-")} Could not read primary file: ${n}`);continue}const s=a.getNestedKeys(l,d??".");for(const n of c){const l=i.getOutputPath(u,n,r),c=o.resolve(process.cwd(),l),w=await i.loadTranslationFile(c)||{},m={};for(const e of s){const o=a.getNestedValue(w,e,d??".")??g;a.setNestedValue(m,e,o,d??".")}const S=JSON.stringify(w);if(JSON.stringify(m)!==S){p=!0;const n=i.serializeTranslationFile(m,f,y);await e.mkdir(o.dirname(c),{recursive:!0}),await e.writeFile(c,n),h.push(` ${t.green("✓")} Synchronized: ${l}`)}else h.push(` ${t.gray("-")} Already in sync: ${l}`)}}s.succeed(t.bold("Synchronization complete!")),h.forEach(e=>console.log(e)),p?(console.log(t.green.bold("\n✅ Sync complete.")),console.log(t.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${t.cyan("npx i18next-cli locize-migrate")}`)):console.log(t.green.bold("\n✅ All locales are already in sync."))}catch(e){s.fail(t.red("Synchronization failed.")),console.error(e)}};
@@ -1 +1 @@
1
- "use strict";require("node:fs/promises"),require("node:path"),exports.getOutputPath=function(e,r,a){return e.replace("{{language}}",r).replace("{{lng}}",r).replace("{{namespace}}",a).replace("{{ns}}",a)};
1
+ "use strict";var e=require("node:fs/promises");require("node:path");var t=require("jiti"),r="undefined"!=typeof document?document.currentScript:null;exports.getOutputPath=function(e,t,r=""){return e.replace("{{language}}",t).replace("{{lng}}",t).replace("{{namespace}}",r).replace("{{ns}}",r)},exports.loadTranslationFile=async function(n){try{if(n.endsWith(".json")){const t=await e.readFile(n,"utf-8");return JSON.parse(t)}if(n.endsWith(".ts")||n.endsWith(".js")){const e=t.createJiti("undefined"==typeof document?require("url").pathToFileURL(__filename).href:r&&"SCRIPT"===r.tagName.toUpperCase()&&r.src||new URL("utils/file-utils.js",document.baseURI).href);return await e.import(n,{default:!0})}}catch(e){return null}return null},exports.serializeTranslationFile=function(e,t="json",r=2){const n=JSON.stringify(e,null,r);switch(t){case"js":case"js-esm":return`export default ${n};\n`;case"js-cjs":return`module.exports = ${n};\n`;case"ts":return`export default ${n} as const;\n`;default:return n}};
package/dist/esm/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as o}from"commander";import e from"chokidar";import{glob as t}from"glob";import i from"chalk";import{ensureConfig as n,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:fs/promises";import"node:path";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as f}from"./status.js";import{runLocizeSync as g,runLocizeDownload as u,runLocizeMigrate as y}from"./locize.js";const w=new o;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.9"),w.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async o=>{const a=await n(),c=async()=>{const e=await r(a);o.ci&&e&&(console.error(i.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(i.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${i.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),o.watch){console.log("\nWatching for changes...");e.watch(await t(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),c()})}}),w.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,e)=>{let t=await a();if(!t){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),t=o}await f(t,{detail:o,namespace:e.namespace})}),w.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const i=await n(),a=()=>s(i);if(await a(),o.watch){console.log("\nWatching for changes...");e.watch(await t(i.types?.input||[]),{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=await n();await l(o)}),w.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await m()}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let o=await a();if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await c();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=e}await d(o)}),w.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const e=await n();await g(e,o)}),w.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const e=await n();await u(e,o)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const e=await n();await y(e,o)}),w.parse(process.argv);
2
+ import{Command as o}from"commander";import t from"chokidar";import{glob as e}from"glob";import i from"chalk";import{ensureConfig as n,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as f}from"./status.js";import{runLocizeSync as g,runLocizeDownload as u,runLocizeMigrate as y}from"./locize.js";const w=new o;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.11"),w.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async o=>{const a=await n(),c=async()=>{const t=await r(a);o.ci&&t&&(console.error(i.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(i.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${i.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),o.watch){console.log("\nWatching for changes...");t.watch(await e(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),c()})}}),w.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,t)=>{let e=await a();if(!e){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),e=o}await f(e,{detail:o,namespace:t.namespace})}),w.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const i=await n(),a=()=>s(i);if(await a(),o.watch){console.log("\nWatching for changes...");t.watch(await e(i.types?.input||[]),{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=await n();await l(o)}),w.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await m()}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let o=await a();if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const t=await c();t||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=t}await d(o)}),w.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const t=await n();await g(t,o)}),w.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const t=await n();await u(t,o)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const t=await n();await y(t,o)}),w.parse(process.argv);
@@ -1 +1 @@
1
- import t from"ora";import a from"chalk";import{parse as e}from"@swc/core";import{mkdir as r,writeFile as o,readFile as n}from"node:fs/promises";import{dirname as s}from"node:path";import{findKeys as i}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as f,ExtractorError as l}from"../../utils/validation.js";import{createPluginContext as p}from"../plugin-manager.js";import{extractKeysFromComments as m}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";async function g(e,n=new u){e.extract.primaryLanguage||(e.extract.primaryLanguage=e.locales[0]||"en"),e.extract.secondaryLanguages||(e.extract.secondaryLanguages=e.locales.filter(t=>t!==e?.extract?.primaryLanguage)),f(e);const l=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:f}=await i(e,n);l.text=`Found ${t.size} unique keys. Updating translation files...`;const p=await c(t,f,e);let m=!1;for(const t of p)t.updated&&(m=!0,await r(s(t.path),{recursive:!0}),await o(t.path,JSON.stringify(t.newTranslations,null,2)),n.info(a.green(`Updated: ${t.path}`)));return l.succeed(a.bold("Extraction complete!")),m}catch(t){throw l.fail(a.red("Extraction failed.")),t}}async function y(t,a,r,o,s=new u){try{let i=await n(t,"utf-8");for(const e of a.plugins||[])i=await(e.onLoad?.(i,t))??i;const c=await e(i,{syntax:"typescript",tsx:!0,comments:!0}),f=p(r);m(i,a.extract.functions||["t"],f,a),o.visit(c),(a.plugins||[]).length>0&&d(c,a.plugins||[],f,s)}catch(a){throw new l("Failed to process file",t,a)}}function d(t,a,e,r=new u){if(t&&"object"==typeof t){for(const o of a)try{o.onVisitNode?.(t,e)}catch(t){r.warn(`Plugin ${o.name} onVisitNode failed:`,t)}for(const o of Object.keys(t)){const n=t[o];if(Array.isArray(n))for(const t of n)t&&"object"==typeof t&&d(t,a,e,r);else n&&"object"==typeof n&&d(n,a,e,r)}}}async function x(t){t.extract.primaryLanguage||(t.extract.primaryLanguage=t.locales[0]),t.extract.secondaryLanguages||(t.extract.secondaryLanguages=t.locales.filter(a=>a!==t?.extract?.primaryLanguage)),t.extract.functions||(t.extract.functions=["t"]),t.extract.transComponents||(t.extract.transComponents=["Trans"]);const{allKeys:a,objectKeys:e}=await i(t);return c(a,e,t)}export{x as extract,y as processFile,g as runExtractor};
1
+ import t from"ora";import o from"chalk";import{parse as a}from"@swc/core";import{mkdir as e,writeFile as r,readFile as n}from"node:fs/promises";import{dirname as i}from"node:path";import{findKeys as s}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as f,ExtractorError as l}from"../../utils/validation.js";import{createPluginContext as p}from"../plugin-manager.js";import{extractKeysFromComments as m}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";import{serializeTranslationFile as y}from"../../utils/file-utils.js";async function g(a,n=new u){a.extract.primaryLanguage||=a.locales[0]||"en",a.extract.secondaryLanguages||=a.locales.filter(t=>t!==a?.extract?.primaryLanguage),f(a);const l=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:f}=await s(a,n);l.text=`Found ${t.size} unique keys. Updating translation files...`;const p=await c(t,f,a);let m=!1;for(const t of p)if(t.updated){m=!0;const s=y(t.newTranslations,a.extract.outputFormat,a.extract.indentation);await e(i(t.path),{recursive:!0}),await r(t.path,s),n.info(o.green(`Updated: ${t.path}`))}return l.succeed(o.bold("Extraction complete!")),m}catch(t){throw l.fail(o.red("Extraction failed.")),t}}async function d(t,o,e,r,i=new u){try{let s=await n(t,"utf-8");for(const a of o.plugins||[])s=await(a.onLoad?.(s,t))??s;const c=await a(s,{syntax:"typescript",tsx:!0,comments:!0}),f=p(e);m(s,o.extract.functions||["t"],f,o),r.visit(c),(o.plugins||[]).length>0&&x(c,o.plugins||[],f,i)}catch(o){throw new l("Failed to process file",t,o)}}function x(t,o,a,e=new u){if(t&&"object"==typeof t){for(const r of o)try{r.onVisitNode?.(t,a)}catch(t){e.warn(`Plugin ${r.name} onVisitNode failed:`,t)}for(const r of Object.keys(t)){const n=t[r];if(Array.isArray(n))for(const t of n)t&&"object"==typeof t&&x(t,o,a,e);else n&&"object"==typeof n&&x(n,o,a,e)}}}async function w(t){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(o=>o!==t?.extract?.primaryLanguage),t.extract.functions||=["t"],t.extract.transComponents||=["Trans"];const{allKeys:o,objectKeys:a}=await s(t);return c(o,a,t)}export{w as extract,d as processFile,g as runExtractor};
@@ -1 +1 @@
1
- import{readFile as t}from"node:fs/promises";import{resolve as e}from"node:path";import{getNestedKeys as a,getNestedValue as o,setNestedValue as r}from"../../utils/nested-object.js";import{getOutputPath as s}from"../../utils/file-utils.js";function n(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}async function c(c,i,p){const f=p.extract.defaultNS??"translation",l=p.extract.keySeparator??".",u=[...p.extract.preservePatterns||[]];for(const t of i)u.push(`${t}.*`);const g=u.map(n);p.extract.primaryLanguage||(p.extract.primaryLanguage=p.locales[0]||"en"),p.extract.secondaryLanguages||(p.extract.secondaryLanguages=p.locales.filter(t=>t!==p.extract.primaryLanguage));const m=new Map;for(const t of c.values()){const e=t.ns||f;m.has(e)||m.set(e,[]),m.get(e).push(t)}const x=[];for(const n of p.locales)for(const[c,i]of m.entries()){const f=s(p.extract.output,n,c),u=e(process.cwd(),f);let m="",y={};try{m=await t(u,"utf-8"),y=JSON.parse(m)}catch(t){}const d={},h=a(y,l);for(const t of h)if(g.some(e=>e.test(t))){const e=o(y,t,l);r(d,t,e,l)}const L=!1===p.extract.sort?i:i.sort((t,e)=>t.key.localeCompare(e.key));for(const{key:t,defaultValue:e}of L){const a=o(y,t,l)??(n===p.extract?.primaryLanguage?e:"");r(d,t,a,l)}const w=p.extract.indentation??2,$=JSON.stringify(d,null,w);x.push({path:u,updated:$!==m,newTranslations:d,existingTranslations:y})}return x}export{c as getTranslations};
1
+ import{resolve as t}from"node:path";import{getNestedKeys as e,getNestedValue as n,setNestedValue as a}from"../../utils/nested-object.js";import{getOutputPath as s,loadTranslationFile as o}from"../../utils/file-utils.js";function r(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}async function c(c,i,l){const u=l.extract.defaultNS??"translation",f=l.extract.keySeparator??".",p=[...l.extract.preservePatterns||[]],g=l.extract.mergeNamespaces??!1;for(const t of i)p.push(`${t}.*`);const x=p.map(r);l.extract.primaryLanguage||=l.locales[0]||"en",l.extract.secondaryLanguages||=l.locales.filter(t=>t!==l?.extract?.primaryLanguage);const d=l.extract.primaryLanguage,y=new Map;for(const t of c.values()){const e=t.ns||u;y.has(e)||y.set(e,[]),y.get(e).push(t)}const m=[];for(const r of l.locales){const c={},i={};for(const[u,p]of y.entries()){const y=s(l.extract.output,r,g?void 0:u),h=t(process.cwd(),y),w=await o(h)||{},k={},N=e(w,f);for(const t of N)if(x.some(e=>e.test(t))){const e=n(w,t,f);a(k,t,e,f)}const O=!1===l.extract.sort?p:[...p].sort((t,e)=>t.key.localeCompare(e.key));for(const{key:t,defaultValue:e}of O){const s=n(w,t,f)??(r===d?e:l.extract.defaultValue??"");a(k,t,s,f)}if(g)c[u]=k,Object.keys(w).length>0&&(i[u]=w);else{const t=w?JSON.stringify(w,null,l.extract.indentation??2):"",e=JSON.stringify(k,null,l.extract.indentation??2);m.push({path:h,updated:e!==t,newTranslations:k,existingTranslations:w})}}if(g){const e=s(l.extract.output,r),n=t(process.cwd(),e),a=Object.keys(i).length>0?JSON.stringify(i,null,l.extract.indentation??2):"",o=JSON.stringify(c,null,l.extract.indentation??2);m.push({path:n,updated:o!==a,newTranslations:c,existingTranslations:i})}}return m}export{c as getTranslations};
@@ -1 +1 @@
1
- import{extractFromTransComponent as e}from"./jsx-parser.js";class t{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||[]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;let s,a;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?a=t:"StringLiteral"===t.type&&(s=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(a=t)}const o=a?this.getObjectPropValue(a,"defaultValue"):void 0,l="string"==typeof o?o:s;for(let e=0;e<r.length;e++){let t,i=r[e];if("ObjectExpression"===a?.type){const e=this.getObjectPropValue(a,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const s=this.config.extract.nsSeparator??":";if(!t&&s&&i.includes(s)){const e=i.split(s);t=e.shift(),i=e.join(s)}t||(t=this.config.extract.defaultNS);let o=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1&&l||i;if("ObjectExpression"===a?.type){const e=this.getObjectProperty(a,"context");if("ConditionalExpression"===e?.value?.type){const n=this.resolvePossibleStringValues(e.value),i=this.config.extract.contextSeparator??"_";if(n.length>0){n.forEach(e=>{this.pluginContext.addKey({key:`${o}${i}${e}`,ns:t,defaultValue:p})}),this.pluginContext.addKey({key:o,ns:t,defaultValue:p});continue}}const n=this.getObjectPropValue(a,"context");if("string"==typeof n&&n){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${o}${e}${n}`,ns:t,defaultValue:p});continue}if(void 0!==this.getObjectPropValue(a,"count")){this.handlePluralKeys(o,p,t);continue}!0===this.getObjectPropValue(a,"returnObjects")&&this.objectKeys.add(o)}this.pluginContext.addKey({key:o,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const i of e)this.pluginContext.addKey({key:`${n.key}${t}${i}`,ns:n.ns,defaultValue:n.defaultValue});this.pluginContext.addKey(n)}}else n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getObjectProperty(e,t){return e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t))}}export{t as ASTVisitors};
1
+ import{extractFromTransComponent as e}from"./jsx-parser.js";class t{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||["t"]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;let s,a;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?a=t:"StringLiteral"===t.type&&(s=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(a=t)}const o=a?this.getObjectPropValue(a,"defaultValue"):void 0,l="string"==typeof o?o:s;for(let e=0;e<r.length;e++){let t,i=r[e];if(a){const e=this.getObjectPropValue(a,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const s=this.config.extract.nsSeparator??":";if(!t&&s&&i.includes(s)){const e=i.split(s);t=e.shift(),i=e.join(s)}t||(t=this.config.extract.defaultNS);let o=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1&&l||i;if(a){const e=this.getObjectProperty(a,"context");if("ConditionalExpression"===e?.value?.type){const n=this.resolvePossibleStringValues(e.value),i=this.config.extract.contextSeparator??"_";if(n.length>0){n.forEach(e=>{this.pluginContext.addKey({key:`${o}${i}${e}`,ns:t,defaultValue:p})}),this.pluginContext.addKey({key:o,ns:t,defaultValue:p});continue}}const n=this.getObjectPropValue(a,"context");if("string"==typeof n&&n){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${o}${e}${n}`,ns:t,defaultValue:p});continue}if(void 0!==this.getObjectPropValue(a,"count")){this.handlePluralKeys(o,t,a);continue}!0===this.getObjectPropValue(a,"returnObjects")&&this.objectKeys.add(o)}this.pluginContext.addKey({key:o,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=!0===this.getObjectPropValue(n,"ordinal"),r=i?"ordinal":"cardinal",s=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:r}).resolvedOptions().pluralCategories,a=this.config.extract.pluralSeparator??"_",o=this.getObjectPropValue(n,"defaultValue"),l=this.getObjectPropValue(n,"defaultValue_other");for(const r of s){const s=i?`defaultValue_ordinal_${r}`:`defaultValue_${r}`,p=this.getObjectPropValue(n,s);let u;u="string"==typeof p?p:"one"===r&&"string"==typeof o?o:"string"==typeof l?l:"string"==typeof o?o:e;const c=i?`${e}${a}ordinal${a}${r}`:`${e}${a}${r}`;this.pluginContext.addKey({key:c,ns:t,defaultValue:u,hasCount:!0})}}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const r=this.getObjectPropValue(n,"defaultValue");this.pluginContext.addKey({key:e,ns:t,defaultValue:"string"==typeof r?r:e})}}handleSimplePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const i of e)this.pluginContext.addKey({key:`${n.key}${t}${i}`,ns:n.ns,defaultValue:n.defaultValue});this.pluginContext.addKey(n)}}else n.hasCount?this.handleSimplePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getObjectProperty(e,t){return e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t))}}export{t as ASTVisitors};
@@ -1 +1 @@
1
- import{glob as s}from"glob";import{readdir as e}from"node:fs/promises";import{dirname as n,join as o}from"node:path";const t=["public/locales/dev/*.json","locales/dev/*.json","src/locales/dev/*.json","src/assets/locales/dev/*.json","app/i18n/locales/dev/*.json","public/locales/en/*.json","locales/en/*.json","src/locales/en/*.json","src/assets/locales/en/*.json","app/i18n/locales/en/*.json"];async function l(){for(const l of t){const t=await s(l,{ignore:"node_modules/**"});if(t.length>0){const s=t[0],l=n(n(s));try{let s=(await e(l)).filter(s=>/^(dev|[a-z]{2}(-[A-Z]{2})?)$/.test(s));if(s.length>0)return s.sort(),s.includes("dev")&&(s=["dev",...s.filter(s=>"dev"!==s)]),s.includes("en")&&(s=["en",...s.filter(s=>"en"!==s)]),{locales:s,extract:{input:["src/**/*.{js,jsx,ts,tsx}","app/**/*.{js,jsx,ts,tsx}","pages/**/*.{js,jsx,ts,tsx}","components/**/*.{js,jsx,ts,tsx}"],output:o(l,"{{language}}","{{namespace}}.json"),primaryLanguage:s.includes("en")?"en":s[0]}}}catch{continue}}}return null}export{l as detectConfig};
1
+ import{glob as s}from"glob";import{readdir as e}from"node:fs/promises";import{dirname as n,extname as o,join as t}from"node:path";const l=["public/locales/dev/*.json","locales/dev/*.json","src/locales/dev/*.json","src/assets/locales/dev/*.json","app/i18n/locales/dev/*.json","public/locales/en/*.json","locales/en/*.json","src/locales/en/*.json","src/assets/locales/en/*.json","app/i18n/locales/en/*.json"];async function a(){for(const a of l){const l=await s(a,{ignore:"node_modules/**"});if(l.length>0){const s=l[0],a=n(n(s)),c=o(s);let r="json";".ts"===c?r="ts":".js"===c&&(r="js");try{let s=(await e(a)).filter(s=>/^(dev|[a-z]{2}(-[A-Z]{2})?)$/.test(s));if(s.length>0)return s.sort(),s.includes("dev")&&(s=["dev",...s.filter(s=>"dev"!==s)]),s.includes("en")&&(s=["en",...s.filter(s=>"en"!==s)]),{locales:s,extract:{input:["src/**/*.{js,jsx,ts,tsx}","app/**/*.{js,jsx,ts,tsx}","pages/**/*.{js,jsx,ts,tsx}","components/**/*.{js,jsx,ts,tsx}"],output:t(a,"{{language}}",`{{namespace}}${c}`),outputFormat:r,primaryLanguage:s.includes("en")?"en":s[0]}}}catch{continue}}}return null}export{a as detectConfig};
@@ -1 +1 @@
1
- import o from"chalk";import e from"ora";import{resolve as t}from"node:path";import{readFile as a}from"node:fs/promises";import{findKeys as s}from"./extractor/core/key-finder.js";import{getNestedValue as n}from"./utils/nested-object.js";import{getOutputPath as r}from"./utils/file-utils.js";async function l(l,d={}){l.extract.primaryLanguage||(l.extract.primaryLanguage=l.locales[0]||"en"),l.extract.secondaryLanguages||(l.extract.secondaryLanguages=l.locales.filter(o=>o!==l?.extract?.primaryLanguage));const g=e("Analyzing project localization status...\n").start();try{const e=await async function(o){const{allKeys:e}=await s(o),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=o.extract,y=o.locales.filter(o=>o!==l),d=new Map;for(const o of e.values()){const e=o.ns||i;d.has(e)||d.set(e,[]),d.get(e).push(o)}const g={totalKeys:e.size,keysByNs:d,locales:new Map};for(const e of y){let s=0;const l=new Map;for(const[i,y]of d.entries()){const d=r(o.extract.output,e,i);let g={};try{const o=await a(t(process.cwd(),d),"utf-8");g=JSON.parse(o)}catch{}let u=0;const f=y.map(({key:o})=>{const e=!!n(g,o,c??".");return e&&u++,{key:o,isTranslated:e}});l.set(i,{totalKeys:y.length,translatedKeys:u,keyDetails:f}),s+=u}g.locales.set(e,{totalTranslated:s,namespaces:l})}return g}(l);g.succeed("Analysis complete."),function(e,t,a){a.detail?function(e,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(o.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(o.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=e.locales.get(a);if(!n)return void console.error(o.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(o.bold(`\nKey Status for "${o.cyan(a)}":`));const r=Array.from(e.keysByNs.values()).flat().length;c("Overall",n.totalTranslated,r);const l=s?[s]:Array.from(n.namespaces.keys()).sort();for(const e of l){const t=n.namespaces.get(e);t&&(console.log(o.cyan.bold(`\nNamespace: ${e}`)),c("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:e,isTranslated:t})=>{const a=t?o.green("✓"):o.red("✗");console.log(` ${a} ${e}`)}))}const i=r-n.totalTranslated;i>0?console.log(o.yellow.bold(`\nSummary: Found ${i} missing translations for "${a}".`)):console.log(o.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));y()}(e,t,a.detail,a.namespace):a.namespace?function(e,t,a){const s=e.keysByNs.get(a);if(!s)return void console.error(o.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(o.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[o,t]of e.locales.entries()){const e=t.namespaces.get(a);if(e){const t=e.totalKeys>0?Math.round(e.translatedKeys/e.totalKeys*100):100,a=i(t);console.log(`- ${o}: ${a} ${t}% (${e.translatedKeys}/${e.totalKeys} keys)`)}}y()}(e,0,a.namespace):function(e,t){const{primaryLanguage:a}=t.extract;console.log(o.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${o.bold(e.totalKeys)}`),console.log(`📚 Namespaces Found: ${o.bold(e.keysByNs.size)}`),console.log(`🌍 Locales: ${o.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${o.bold(a)}`),console.log("\nTranslation Progress:");for(const[o,t]of e.locales.entries()){const a=e.totalKeys>0?Math.round(t.totalTranslated/e.totalKeys*100):100,s=i(a);console.log(`- ${o}: ${s} ${a}% (${t.totalTranslated}/${e.totalKeys} keys)`)}y()}(e,t)}(e,l,d)}catch(o){g.fail("Failed to generate status report."),console.error(o)}}function c(e,t,a){const s=a>0?Math.round(t/a*100):100,n=i(s);console.log(`${o.bold(e)}: ${n} ${s}% (${t}/${a})`)}function i(e){const t=Math.round(e/100*20),a=20-t;return`[${o.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function y(){console.log(o.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${o.cyan("npx i18next-cli locize-migrate")} to get started.`)}export{l as runStatus};
1
+ import o from"chalk";import e from"ora";import{resolve as t}from"node:path";import{findKeys as a}from"./extractor/core/key-finder.js";import{getNestedValue as s}from"./utils/nested-object.js";import{loadTranslationFile as n,getOutputPath as l}from"./utils/file-utils.js";async function r(r,d={}){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(o=>o!==r?.extract?.primaryLanguage);const g=e("Analyzing project localization status...\n").start();try{const e=await async function(o){o.extract.primaryLanguage||=o.locales[0]||"en",o.extract.secondaryLanguages||=o.locales.filter(e=>e!==o?.extract?.primaryLanguage);const{allKeys:e}=await a(o),{secondaryLanguages:r,keySeparator:c=".",defaultNS:i="translation",mergeNamespaces:y=!1}=o.extract,d=new Map;for(const o of e.values()){const e=o.ns||i;d.has(e)||d.set(e,[]),d.get(e).push(o)}const g={totalKeys:e.size,keysByNs:d,locales:new Map};for(const e of r){let a=0;const r=new Map,i=y?await n(t(process.cwd(),l(o.extract.output,e)))||{}:null;for(const[g,u]of d.entries()){const d=y?i?.[g]||{}:await n(t(process.cwd(),l(o.extract.output,e,g)))||{};let f=0;const m=u.map(({key:o})=>{const e=!!s(d,o,c??".");return e&&f++,{key:o,isTranslated:e}});r.set(g,{totalKeys:u.length,translatedKeys:f,keyDetails:m}),a+=f}g.locales.set(e,{totalTranslated:a,namespaces:r})}return g}(r);g.succeed("Analysis complete."),function(e,t,a){a.detail?function(e,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(o.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(o.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=e.locales.get(a);if(!n)return void console.error(o.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(o.bold(`\nKey Status for "${o.cyan(a)}":`));const l=Array.from(e.keysByNs.values()).flat().length;c("Overall",n.totalTranslated,l);const r=s?[s]:Array.from(n.namespaces.keys()).sort();for(const e of r){const t=n.namespaces.get(e);t&&(console.log(o.cyan.bold(`\nNamespace: ${e}`)),c("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:e,isTranslated:t})=>{const a=t?o.green("✓"):o.red("✗");console.log(` ${a} ${e}`)}))}const i=l-n.totalTranslated;i>0?console.log(o.yellow.bold(`\nSummary: Found ${i} missing translations for "${a}".`)):console.log(o.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));y()}(e,t,a.detail,a.namespace):a.namespace?function(e,t,a){const s=e.keysByNs.get(a);if(!s)return void console.error(o.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(o.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[o,t]of e.locales.entries()){const e=t.namespaces.get(a);if(e){const t=e.totalKeys>0?Math.round(e.translatedKeys/e.totalKeys*100):100,a=i(t);console.log(`- ${o}: ${a} ${t}% (${e.translatedKeys}/${e.totalKeys} keys)`)}}y()}(e,0,a.namespace):function(e,t){const{primaryLanguage:a}=t.extract;console.log(o.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${o.bold(e.totalKeys)}`),console.log(`📚 Namespaces Found: ${o.bold(e.keysByNs.size)}`),console.log(`🌍 Locales: ${o.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${o.bold(a)}`),console.log("\nTranslation Progress:");for(const[o,t]of e.locales.entries()){const a=e.totalKeys>0?Math.round(t.totalTranslated/e.totalKeys*100):100,s=i(a);console.log(`- ${o}: ${s} ${a}% (${t.totalTranslated}/${e.totalKeys} keys)`)}y()}(e,t)}(e,r,d)}catch(o){g.fail("Failed to generate status report."),console.error(o)}}function c(e,t,a){const s=a>0?Math.round(t/a*100):100,n=i(s);console.log(`${o.bold(e)}: ${n} ${s}% (${t}/${a})`)}function i(e){const t=Math.round(e/100*20),a=20-t;return`[${o.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function y(){console.log(o.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${o.cyan("npx i18next-cli locize-migrate")} to get started.`)}export{r as runStatus};
@@ -1 +1 @@
1
- import{readFile as t,mkdir as o,writeFile as e}from"node:fs/promises";import{resolve as r,dirname as n}from"path";import a from"chalk";import c from"ora";import{getNestedKeys as l,getNestedValue as s,setNestedValue as i}from"./utils/nested-object.js";import{getOutputPath as f}from"./utils/file-utils.js";async function u(u){const p=c("Running i18next locale synchronizer...\n").start();u.extract.primaryLanguage||=u.locales[0]||"en";const{primaryLanguage:y}=u.extract,d=u.locales.filter(t=>t!==y),m=u.extract.keySeparator??".",g=[];let h=!1;const x=u.extract.defaultNS??"translation",w=f(u.extract.output,y,x),S=r(process.cwd(),w);let $;try{const o=await t(S,"utf-8");$=JSON.parse(o)}catch(t){return void console.error(`Primary language file not found at ${w}. Cannot sync.`)}const b=l($,m);for(const c of d){const l=f(u.extract.output,c,x),p=r(process.cwd(),l);let y={},d="";try{d=await t(p,"utf-8"),y=JSON.parse(d)}catch(t){}const w={};for(const t of b){const o=s(y,t,m)??(u.extract?.defaultValue||"");i(w,t,o,m)}const S=u.extract.indentation??2,$=JSON.stringify(w,null,S);$!==d?(h=!0,await o(n(p),{recursive:!0}),await e(p,$),g.push(` ${a.green("✓")} Synchronized: ${l}`)):g.push(` ${a.gray("-")} Already in sync: ${l}`)}p.succeed(a.bold("Synchronization complete!")),g.forEach(t=>console.log(t)),h?(console.log(a.green.bold("\n✅ Sync complete.")),console.log(a.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${a.cyan("npx i18next-cli locize-migrate")}`)):console.log(a.green.bold("\n✅ All locales are already in sync."))}export{u as runSyncer};
1
+ import{mkdir as o,writeFile as t}from"node:fs/promises";import{basename as e,resolve as n,dirname as r}from"path";import i from"chalk";import l from"ora";import{glob as a}from"glob";import{getNestedKeys as s,getNestedValue as c,setNestedValue as f}from"./utils/nested-object.js";import{getOutputPath as p,loadTranslationFile as u,serializeTranslationFile as y}from"./utils/file-utils.js";async function m(m){const d=l("Running i18next locale synchronizer...\n").start();try{const l=m.extract.primaryLanguage||m.locales[0]||"en",g=m.locales.filter(o=>o!==l),{output:h,keySeparator:w=".",outputFormat:S="json",indentation:$=2,defaultValue:x=""}=m.extract,b=[];let z=!1;const j=p(h,l,"*"),N=await a(j);if(0===N.length)return void d.warn(`No translation files found for primary language "${l}". Nothing to sync.`);for(const l of N){const a=e(l).split(".")[0],m=await u(l);if(!m){b.push(` ${i.yellow("-")} Could not read primary file: ${l}`);continue}const d=s(m,w??".");for(const e of g){const l=p(h,e,a),s=n(process.cwd(),l),m=await u(s)||{},g={};for(const o of d){const t=c(m,o,w??".");f(g,o,t??x,w??".")}const j=JSON.stringify(m);if(JSON.stringify(g)!==j){z=!0;const e=y(g,S,$);await o(r(s),{recursive:!0}),await t(s,e),b.push(` ${i.green("✓")} Synchronized: ${l}`)}else b.push(` ${i.gray("-")} Already in sync: ${l}`)}}d.succeed(i.bold("Synchronization complete!")),b.forEach(o=>console.log(o)),z?(console.log(i.green.bold("\n✅ Sync complete.")),console.log(i.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${i.cyan("npx i18next-cli locize-migrate")}`)):console.log(i.green.bold("\n✅ All locales are already in sync."))}catch(o){d.fail(i.red("Synchronization failed.")),console.error(o)}}export{m as runSyncer};
@@ -1 +1 @@
1
- import"node:fs/promises";import"node:path";function e(e,p,r){return e.replace("{{language}}",p).replace("{{lng}}",p).replace("{{namespace}}",r).replace("{{ns}}",r)}export{e as getOutputPath};
1
+ import{readFile as t}from"node:fs/promises";import"node:path";import{createJiti as e}from"jiti";function n(t,e,n=""){return t.replace("{{language}}",e).replace("{{lng}}",e).replace("{{namespace}}",n).replace("{{ns}}",n)}async function r(n){try{if(n.endsWith(".json")){const e=await t(n,"utf-8");return JSON.parse(e)}if(n.endsWith(".ts")||n.endsWith(".js")){const t=e(import.meta.url);return await t.import(n,{default:!0})}}catch(t){return null}return null}function s(t,e="json",n=2){const r=JSON.stringify(t,null,n);switch(e){case"js":case"js-esm":return`export default ${r};\n`;case"js-cjs":return`module.exports = ${r};\n`;case"ts":return`export default ${r} as const;\n`;default:return r}}export{n as getOutputPath,r as loadTranslationFile,s as serializeTranslationFile};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -21,7 +21,7 @@ const program = new Command()
21
21
  program
22
22
  .name('i18next-cli')
23
23
  .description('A unified, high-performance i18next CLI.')
24
- .version('0.9.9')
24
+ .version('0.9.11')
25
25
 
26
26
  program
27
27
  .command('extract')