i18next-cli 1.21.1 → 1.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +107 -0
  3. package/dist/cjs/cli.js +1 -1
  4. package/dist/cjs/extractor/core/ast-visitors.js +1 -1
  5. package/dist/cjs/extractor/core/extractor.js +1 -1
  6. package/dist/cjs/extractor/parsers/call-expression-handler.js +1 -1
  7. package/dist/cjs/extractor/parsers/jsx-handler.js +1 -1
  8. package/dist/cjs/extractor/plugin-manager.js +1 -1
  9. package/dist/cjs/types-generator.js +1 -1
  10. package/dist/esm/cli.js +1 -1
  11. package/dist/esm/extractor/core/ast-visitors.js +1 -1
  12. package/dist/esm/extractor/core/extractor.js +1 -1
  13. package/dist/esm/extractor/parsers/call-expression-handler.js +1 -1
  14. package/dist/esm/extractor/parsers/jsx-handler.js +1 -1
  15. package/dist/esm/extractor/plugin-manager.js +1 -1
  16. package/dist/esm/types-generator.js +1 -1
  17. package/package.json +1 -1
  18. package/src/cli.ts +1 -1
  19. package/src/extractor/core/ast-visitors.ts +48 -2
  20. package/src/extractor/core/extractor.ts +3 -0
  21. package/src/extractor/parsers/call-expression-handler.ts +45 -2
  22. package/src/extractor/parsers/jsx-handler.ts +93 -19
  23. package/src/extractor/plugin-manager.ts +27 -3
  24. package/src/types-generator.ts +3 -1
  25. package/src/types.ts +7 -0
  26. package/types/extractor/core/ast-visitors.d.ts +23 -0
  27. package/types/extractor/core/ast-visitors.d.ts.map +1 -1
  28. package/types/extractor/core/extractor.d.ts.map +1 -1
  29. package/types/extractor/parsers/call-expression-handler.d.ts +8 -1
  30. package/types/extractor/parsers/call-expression-handler.d.ts.map +1 -1
  31. package/types/extractor/parsers/jsx-handler.d.ts +9 -1
  32. package/types/extractor/parsers/jsx-handler.d.ts.map +1 -1
  33. package/types/extractor/plugin-manager.d.ts.map +1 -1
  34. package/types/types-generator.d.ts.map +1 -1
  35. package/types/types.d.ts +6 -0
  36. package/types/types.d.ts.map +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ 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.22.1](https://github.com/i18next/i18next-cli/compare/v1.22.0...v1.22.1) - 2025-11-06
9
+
10
+ - **Types-Generator:** Add a comment to the start of the types file to note it is auto-generated [#97](https://github.com/i18next/i18next-cli/pull/97)
11
+
12
+ ## [1.22.0](https://github.com/i18next/i18next-cli/compare/v1.21.1...v1.22.0) - 2025-11-06
13
+
14
+ - **Plugin System:** Enhanced `ExtractedKey` type and plugin hooks to support location metadata tracking. Plugins can now access file paths, etc. for each extracted translation key via the `locations` array property. [#95](https://github.com/i18next/i18next-cli/issues/95)
15
+
8
16
  ## [1.21.1](https://github.com/i18next/i18next-cli/compare/v1.21.0...v1.21.1) - 2025-11-05
9
17
 
10
18
  - **Extractor (`--sync-primary`):** Fixed a bug where `<Trans i18nKey='namespace:key' />` components with namespaced keys were having their existing primary language translations overwritten with the full namespaced key string (e.g., `'translation:app.preservedTrans'`) when using the `--sync-primary` flag. The extractor now correctly identifies namespaced keys in defaultValue as derived (auto-generated) defaults rather than explicit developer-provided values, preserving existing translations when no explicit defaultValue is provided. This ensures that `<Trans>` components without explicit defaults behave consistently with `t()` calls under `--sync-primary`. [#92](https://github.com/i18next/i18next-cli/issues/92)
package/README.md CHANGED
@@ -581,6 +581,113 @@ export default defineConfig({
581
581
  });
582
582
  ```
583
583
 
584
+ ### Location Metadata Tracking
585
+
586
+ Track where each translation key is used in your codebase with a custom metadata plugin.
587
+
588
+ **Example Plugin Implementation:**
589
+
590
+ ```typescript
591
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
592
+ import { dirname } from 'node:path';
593
+ import type { Plugin } from 'i18next-cli';
594
+
595
+ interface LocationMetadataOptions {
596
+ /** Output path for the metadata file (default: 'locales/metadata.json') */
597
+ output?: string;
598
+ /** Include line and column numbers (default: true) */
599
+ includePosition?: boolean;
600
+ }
601
+
602
+ export const locationMetadataPlugin = (options: LocationMetadataOptions = {}): Plugin => {
603
+ const {
604
+ output = 'locales/metadata.json',
605
+ includePosition = true,
606
+ } = options;
607
+
608
+ return {
609
+ name: 'location-metadata',
610
+
611
+ async onEnd(keys) {
612
+ const metadata: Record<string, any> = {};
613
+
614
+ for (const [uniqueKey, extractedKey] of keys.entries()) {
615
+ const { key, ns, locations } = extractedKey;
616
+
617
+ // Skip keys without location data
618
+ if (!locations || locations.length === 0) {
619
+ continue;
620
+ }
621
+
622
+ // Format location data
623
+ const locationData = locations.map(loc => {
624
+ if (includePosition && loc.line !== undefined) {
625
+ return `${loc.file}:${loc.line}:${loc.column ?? 0}`;
626
+ }
627
+ return loc.file;
628
+ });
629
+
630
+ // Organize metadata
631
+ const namespace = ns || 'translation';
632
+ if (!metadata[namespace]) {
633
+ metadata[namespace] = {};
634
+ }
635
+ metadata[namespace][key] = locationData;
636
+ }
637
+
638
+ // Write metadata file
639
+ await mkdir(dirname(output), { recursive: true });
640
+ await writeFile(output, JSON.stringify(metadata, null, 2), 'utf-8');
641
+
642
+ console.log(`📍 Location metadata written to ${output}`);
643
+ }
644
+ };
645
+ };
646
+ ```
647
+
648
+ **Configuration:**
649
+
650
+ ```typescript
651
+ // i18next.config.ts
652
+ import { defineConfig } from 'i18next-cli';
653
+ import { locationMetadataPlugin } from './plugins/location-metadata.mjs';
654
+
655
+ export default defineConfig({
656
+ locales: ['en', 'de'],
657
+ extract: {
658
+ input: ['src/**/*.{ts,tsx}'],
659
+ output: 'locales/{{language}}/{{namespace}}.json',
660
+ },
661
+ plugins: [
662
+ locationMetadataPlugin({
663
+ output: 'locales/metadata.json'
664
+ })
665
+ ]
666
+ });
667
+ ```
668
+
669
+ **Example Output (`locales/metadata.json`):**
670
+
671
+ ```json
672
+ {
673
+ "translation": {
674
+ "app.title": [
675
+ "src/App.tsx:12:15",
676
+ "src/components/Header.tsx:8:22"
677
+ ],
678
+ "user.greeting": [
679
+ "src/pages/Profile.tsx:45:10"
680
+ ]
681
+ },
682
+ "common": {
683
+ "button.save": [
684
+ "src/components/SaveButton.tsx:18:7",
685
+ "src/forms/UserForm.tsx:92:5"
686
+ ]
687
+ }
688
+ }
689
+ ```
690
+
584
691
  ### Dynamic Key Preservation
585
692
 
586
693
  Use `preservePatterns` to maintain dynamically generated keys:
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("minimatch"),i=require("chalk"),a=require("./config.js"),r=require("./heuristic-config.js"),c=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var s=require("./types-generator.js"),l=require("./syncer.js"),u=require("./migrator.js"),g=require("./init.js"),d=require("./linter.js"),p=require("./status.js"),f=require("./locize.js");const m=new e.Command;m.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.21.1"),m.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),m.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.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async e=>{try{const o=m.opts().config,i=await a.ensureConfig(o),r=async()=>{const t=await c.runExtractor(i,{isWatchMode:!!e.watch,isDryRun:!!e.dryRun,syncPrimaryWithDefaults:!!e.syncPrimary});return e.ci&&!t?(console.log("✅ No files were updated."),process.exit(0)):e.ci&&t&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),t};if(await r(),e.watch){console.log("\nWatching for changes...");const e=await w(i.extract.input),o=y(i.extract.ignore),a=h(i.extract.output),c=[...o,...a].filter(Boolean),s=e.filter(e=>!c.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}catch(e){console.error("Error running extractor:",e),process.exit(1)}}),m.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)=>{const o=m.opts().config;let n=await a.loadConfig(o);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await r.detectConfig();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 i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=e}await p.runStatus(n,{detail:e,namespace:t.namespace})}),m.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 o=m.opts().config,i=await a.ensureConfig(o),r=()=>s.runTypesGenerator(i);if(await r(),e.watch){console.log("\nWatching for changes...");const e=await w(i.types?.input||[]),o=[...y(i.extract?.ignore)].filter(Boolean),a=e.filter(e=>!o.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(a,{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}),m.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=m.opts().config,t=await a.ensureConfig(e);await l.runSyncer(t)}),m.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await u.runMigrator(e)}),m.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(g.runInit),m.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async e=>{const o=m.opts().config,c=async()=>{let e=await a.loadConfig(o);if(!e){console.log(i.blue("No config file found. Attempting to detect project structure..."));const t=await r.detectConfig();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 i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),e=t}await d.runLinterCli(e)};if(await c(),e.watch){console.log("\nWatching for changes...");const e=await a.loadConfig(o);if(e?.extract?.input){const o=await w(e.extract.input),i=[...y(e.extract.ignore),...h(e.extract.output)].filter(Boolean),a=o.filter(e=>!i.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(a,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}}),m.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=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeSync(o,e)}),m.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeDownload(o,e)}),m.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeMigrate(o,e)}),m.parse(process.argv);const y=e=>Array.isArray(e)?e:e?[e]:[],h=e=>e&&"string"==typeof e?[e.replace(/\{\{[^}]+\}\}/g,"*")]:[],w=async(e=[])=>{const t=y(e),n=await Promise.all(t.map(e=>o.glob(e||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
2
+ "use strict";var e=require("commander"),t=require("chokidar"),o=require("glob"),n=require("minimatch"),i=require("chalk"),a=require("./config.js"),r=require("./heuristic-config.js"),c=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var s=require("./types-generator.js"),l=require("./syncer.js"),u=require("./migrator.js"),g=require("./init.js"),d=require("./linter.js"),p=require("./status.js"),f=require("./locize.js");const m=new e.Command;m.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.22.1"),m.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),m.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.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async e=>{try{const o=m.opts().config,i=await a.ensureConfig(o),r=async()=>{const t=await c.runExtractor(i,{isWatchMode:!!e.watch,isDryRun:!!e.dryRun,syncPrimaryWithDefaults:!!e.syncPrimary});return e.ci&&!t?(console.log("✅ No files were updated."),process.exit(0)):e.ci&&t&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),t};if(await r(),e.watch){console.log("\nWatching for changes...");const e=await w(i.extract.input),o=y(i.extract.ignore),a=h(i.extract.output),c=[...o,...a].filter(Boolean),s=e.filter(e=>!c.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}catch(e){console.error("Error running extractor:",e),process.exit(1)}}),m.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)=>{const o=m.opts().config;let n=await a.loadConfig(o);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await r.detectConfig();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 i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=e}await p.runStatus(n,{detail:e,namespace:t.namespace})}),m.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 o=m.opts().config,i=await a.ensureConfig(o),r=()=>s.runTypesGenerator(i);if(await r(),e.watch){console.log("\nWatching for changes...");const e=await w(i.types?.input||[]),o=[...y(i.extract?.ignore)].filter(Boolean),a=e.filter(e=>!o.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(a,{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}),m.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=m.opts().config,t=await a.ensureConfig(e);await l.runSyncer(t)}),m.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await u.runMigrator(e)}),m.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(g.runInit),m.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async e=>{const o=m.opts().config,c=async()=>{let e=await a.loadConfig(o);if(!e){console.log(i.blue("No config file found. Attempting to detect project structure..."));const t=await r.detectConfig();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 i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),e=t}await d.runLinterCli(e)};if(await c(),e.watch){console.log("\nWatching for changes...");const e=await a.loadConfig(o);if(e?.extract?.input){const o=await w(e.extract.input),i=[...y(e.extract.ignore),...h(e.extract.output)].filter(Boolean),a=o.filter(e=>!i.some(t=>n.minimatch(e,t,{dot:!0})));t.watch(a,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}}),m.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=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeSync(o,e)}),m.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeDownload(o,e)}),m.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=m.opts().config,o=await a.ensureConfig(t);await f.runLocizeMigrate(o,e)}),m.parse(process.argv);const y=e=>Array.isArray(e)?e:e?[e]:[],h=e=>e&&"string"==typeof e?[e.replace(/\{\{[^}]+\}\}/g,"*")]:[],w=async(e=[])=>{const t=y(e),n=await Promise.all(t.map(e=>o.glob(e||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
@@ -1 +1 @@
1
- "use strict";var e=require("../parsers/scope-manager.js"),s=require("../parsers/expression-resolver.js"),r=require("../parsers/call-expression-handler.js"),o=require("../parsers/jsx-handler.js");exports.ASTVisitors=class{pluginContext;config;logger;hooks;get objectKeys(){return this.callExpressionHandler.objectKeys}scopeManager;expressionResolver;callExpressionHandler;jsxHandler;constructor(a,i,t,n,l){this.pluginContext=i,this.config=a,this.logger=t,this.hooks={onBeforeVisitNode:n?.onBeforeVisitNode,onAfterVisitNode:n?.onAfterVisitNode,resolvePossibleKeyStringValues:n?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:n?.resolvePossibleContextStringValues},this.scopeManager=new e.ScopeManager(a),this.expressionResolver=l??new s.ExpressionResolver(this.hooks),this.callExpressionHandler=new r.CallExpressionHandler(a,i,t,this.expressionResolver),this.jsxHandler=new o.JSXHandler(a,i,this.expressionResolver)}visit(e){this.scopeManager.reset(),this.expressionResolver.resetFileSymbols(),this.scopeManager.enterScope(),this.walk(e),this.scopeManager.exitScope()}walk(e){if(!e)return;let s=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.scopeManager.enterScope(),s=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);break;case"TSEnumDeclaration":case"TsEnumDeclaration":case"TsEnumDecl":this.expressionResolver.captureEnumDeclaration(e);break;case"CallExpression":this.callExpressionHandler.handleCallExpression(e,this.scopeManager.getVarFromScope.bind(this.scopeManager));break;case"JSXElement":this.jsxHandler.handleJSXElement(e,this.scopeManager.getVarFromScope.bind(this.scopeManager))}this.hooks.onAfterVisitNode?.(e);for(const s in e){if("span"===s)continue;const r=e[s];if(Array.isArray(r)){for(const e of r)if(e&&"object"==typeof e)if("VariableDeclarator"!==e.type){if(e&&e.id&&Array.isArray(e.members)&&this.expressionResolver.captureEnumDeclaration(e),"VariableDeclaration"===e.type&&Array.isArray(e.declarations))for(const s of e.declarations)s&&"object"==typeof s&&"VariableDeclarator"===s.type&&(this.scopeManager.handleVariableDeclarator(s),this.expressionResolver.captureVariableDeclarator(s))}else this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);for(const e of r)e&&"object"==typeof e&&this.walk(e)}else r&&"object"==typeof r&&this.walk(r)}s&&this.scopeManager.exitScope()}getVarFromScope(e){return this.scopeManager.getVarFromScope(e)}};
1
+ "use strict";var e=require("../parsers/scope-manager.js"),r=require("../parsers/expression-resolver.js"),s=require("../parsers/call-expression-handler.js"),t=require("../parsers/jsx-handler.js");exports.ASTVisitors=class{pluginContext;config;logger;hooks;get objectKeys(){return this.callExpressionHandler.objectKeys}scopeManager;expressionResolver;callExpressionHandler;jsxHandler;currentFile="";currentCode="";constructor(o,a,i,n,l){this.pluginContext=a,this.config=o,this.logger=i,this.hooks={onBeforeVisitNode:n?.onBeforeVisitNode,onAfterVisitNode:n?.onAfterVisitNode,resolvePossibleKeyStringValues:n?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:n?.resolvePossibleContextStringValues},this.scopeManager=new e.ScopeManager(o),this.expressionResolver=l??new r.ExpressionResolver(this.hooks),this.callExpressionHandler=new s.CallExpressionHandler(o,a,i,this.expressionResolver,()=>this.getCurrentFile(),()=>this.getCurrentCode()),this.jsxHandler=new t.JSXHandler(o,a,this.expressionResolver,()=>this.getCurrentFile(),()=>this.getCurrentCode())}visit(e){this.scopeManager.reset(),this.expressionResolver.resetFileSymbols(),this.scopeManager.enterScope(),this.walk(e),this.scopeManager.exitScope()}walk(e){if(!e)return;let r=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.scopeManager.enterScope(),r=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);break;case"TSEnumDeclaration":case"TsEnumDeclaration":case"TsEnumDecl":this.expressionResolver.captureEnumDeclaration(e);break;case"CallExpression":this.callExpressionHandler.handleCallExpression(e,this.scopeManager.getVarFromScope.bind(this.scopeManager));break;case"JSXElement":this.jsxHandler.handleJSXElement(e,this.scopeManager.getVarFromScope.bind(this.scopeManager))}this.hooks.onAfterVisitNode?.(e);for(const r in e){if("span"===r)continue;const s=e[r];if(Array.isArray(s)){for(const e of s)if(e&&"object"==typeof e)if("VariableDeclarator"!==e.type){if(e&&e.id&&Array.isArray(e.members)&&this.expressionResolver.captureEnumDeclaration(e),"VariableDeclaration"===e.type&&Array.isArray(e.declarations))for(const r of e.declarations)r&&"object"==typeof r&&"VariableDeclarator"===r.type&&(this.scopeManager.handleVariableDeclarator(r),this.expressionResolver.captureVariableDeclarator(r))}else this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);for(const e of s)e&&"object"==typeof e&&this.walk(e)}else s&&"object"==typeof s&&this.walk(s)}r&&this.scopeManager.exitScope()}getVarFromScope(e){return this.scopeManager.getVarFromScope(e)}setCurrentFile(e,r){this.currentFile=e,this.currentCode=r}getCurrentFile(){return this.currentFile}getCurrentCode(){return this.currentCode}};
@@ -1 +1 @@
1
- "use strict";var t=require("ora"),e=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),o=require("node:path"),n=require("./key-finder.js"),s=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../parsers/comment-parser.js"),l=require("../../utils/logger.js"),u=require("../../utils/file-utils.js"),y=require("../../utils/funnel-msg-tracker.js");exports.extract=async function(t,{syncPrimaryWithDefaults:e=!1}={}){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(e=>e!==t?.extract?.primaryLanguage),t.extract.functions||=["t","*.t"],t.extract.transComponents||=["Trans"];const{allKeys:r,objectKeys:a}=await n.findKeys(t);return s.getTranslations(r,a,t,{syncPrimaryWithDefaults:e})},exports.processFile=async function(t,e,n,s,u,y=new l.ConsoleLogger){try{let l=await a.readFile(t,"utf-8");for(const r of e)try{const e=await(r.onLoad?.(l,t));void 0!==e&&(l=e)}catch(t){y.warn(`Plugin ${r.name} onLoad failed:`,t)}const d=o.extname(t).toLowerCase(),g=".ts"===d||".tsx"===d||".mts"===d||".cts"===d,m=".tsx"===d;let p;try{p=await r.parse(l,{syntax:g?"typescript":"ecmascript",tsx:m,decorators:!0,dynamicImport:!0,comments:!0})}catch(e){if(".ts"!==d||m)throw new i.ExtractorError("Failed to process file",t,e);try{p=await r.parse(l,{syntax:"typescript",tsx:!0,decorators:!0,dynamicImport:!0,comments:!0}),y.info?.(`Parsed ${t} using TSX fallback`)}catch(e){throw new i.ExtractorError("Failed to process file",t,e)}}s.getVarFromScope=n.getVarFromScope.bind(n),n.visit(p),c.extractKeysFromComments(l,s,u,n.getVarFromScope.bind(n))}catch(e){throw new i.ExtractorError("Failed to process file",t,e)}},exports.runExtractor=async function(r,{isWatchMode:c=!1,isDryRun:d=!1,syncPrimaryWithDefaults:g=!1}={},m=new l.ConsoleLogger){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(t=>t!==r?.extract?.primaryLanguage),r.extract.functions||=["t","*.t"],r.extract.transComponents||=["Trans"],i.validateExtractorConfig(r);const p=r.plugins||[],f=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:i}=await n.findKeys(r,m);f.text=`Found ${t.size} unique keys. Updating translation files...`;const c=await s.getTranslations(t,i,r,{syncPrimaryWithDefaults:g});let l=!1;for(const t of c)if(t.updated&&(l=!0,!d)){const n=u.serializeTranslationFile(t.newTranslations,r.extract.outputFormat,r.extract.indentation);await a.mkdir(o.dirname(t.path),{recursive:!0}),await a.writeFile(t.path,n),m.info(e.green(`Updated: ${t.path}`))}if(p.length>0){f.text="Running post-extraction plugins...";for(const t of p)await(t.afterSync?.(c,r))}return f.succeed(e.bold("Extraction complete!")),l&&await async function(){if(!await y.shouldShowFunnel("extract"))return;return console.log(e.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${e.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${e.cyan("https://youtu.be/joPsZghT3wM")}`),y.recordFunnelShown("extract")}(),l}catch(t){throw f.fail(e.red("Extraction failed.")),t}};
1
+ "use strict";var t=require("ora"),e=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),o=require("node:path"),n=require("./key-finder.js"),s=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../parsers/comment-parser.js"),l=require("../../utils/logger.js"),u=require("../../utils/file-utils.js"),y=require("../../utils/funnel-msg-tracker.js");exports.extract=async function(t,{syncPrimaryWithDefaults:e=!1}={}){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(e=>e!==t?.extract?.primaryLanguage),t.extract.functions||=["t","*.t"],t.extract.transComponents||=["Trans"];const{allKeys:r,objectKeys:a}=await n.findKeys(t);return s.getTranslations(r,a,t,{syncPrimaryWithDefaults:e})},exports.processFile=async function(t,e,n,s,u,y=new l.ConsoleLogger){try{let l=await a.readFile(t,"utf-8");for(const r of e)try{const e=await(r.onLoad?.(l,t));void 0!==e&&(l=e)}catch(t){y.warn(`Plugin ${r.name} onLoad failed:`,t)}const d=o.extname(t).toLowerCase(),g=".ts"===d||".tsx"===d||".mts"===d||".cts"===d,m=".tsx"===d;let p;try{p=await r.parse(l,{syntax:g?"typescript":"ecmascript",tsx:m,decorators:!0,dynamicImport:!0,comments:!0})}catch(e){if(".ts"!==d||m)throw new i.ExtractorError("Failed to process file",t,e);try{p=await r.parse(l,{syntax:"typescript",tsx:!0,decorators:!0,dynamicImport:!0,comments:!0}),y.info?.(`Parsed ${t} using TSX fallback`)}catch(e){throw new i.ExtractorError("Failed to process file",t,e)}}s.getVarFromScope=n.getVarFromScope.bind(n),n.setCurrentFile(t,l),n.visit(p),c.extractKeysFromComments(l,s,u,n.getVarFromScope.bind(n))}catch(e){throw new i.ExtractorError("Failed to process file",t,e)}},exports.runExtractor=async function(r,{isWatchMode:c=!1,isDryRun:d=!1,syncPrimaryWithDefaults:g=!1}={},m=new l.ConsoleLogger){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(t=>t!==r?.extract?.primaryLanguage),r.extract.functions||=["t","*.t"],r.extract.transComponents||=["Trans"],i.validateExtractorConfig(r);const p=r.plugins||[],f=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:i}=await n.findKeys(r,m);f.text=`Found ${t.size} unique keys. Updating translation files...`;const c=await s.getTranslations(t,i,r,{syncPrimaryWithDefaults:g});let l=!1;for(const t of c)if(t.updated&&(l=!0,!d)){const n=u.serializeTranslationFile(t.newTranslations,r.extract.outputFormat,r.extract.indentation);await a.mkdir(o.dirname(t.path),{recursive:!0}),await a.writeFile(t.path,n),m.info(e.green(`Updated: ${t.path}`))}if(p.length>0){f.text="Running post-extraction plugins...";for(const t of p)await(t.afterSync?.(c,r))}return f.succeed(e.bold("Extraction complete!")),l&&await async function(){if(!await y.shouldShowFunnel("extract"))return;return console.log(e.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${e.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${e.cyan("https://youtu.be/joPsZghT3wM")}`),y.recordFunnelShown("extract")}(),l}catch(t){throw f.fail(e.red("Extraction failed.")),t}};
@@ -1 +1 @@
1
- "use strict";var e=require("./ast-utils.js");exports.CallExpressionHandler=class{pluginContext;config;logger;expressionResolver;objectKeys=new Set;constructor(e,t,r,n){this.config=e,this.pluginContext=t,this.logger=r,this.expressionResolver=n}handleCallExpression(t,r){const n=this.getFunctionName(t.callee);if(!n)return;const s=r(n),o=this.config.extract.functions||["t","*.t"];let i=void 0!==s;if(!i)for(const e of o)if(e.startsWith("*.")){if(n.endsWith(e.substring(1))){i=!0;break}}else if(e===n){i=!0;break}if(!i||0===t.arguments.length)return;const{keysToProcess:l,isSelectorAPI:a}=this.handleCallExpressionArgument(t,0);if(0===l.length)return;let u=!1;const p=this.config.extract.pluralSeparator??"_";for(let e=0;e<l.length;e++)l[e].endsWith(`${p}ordinal`)&&(u=!0,l[e]=l[e].slice(0,-8));let f,c;if(t.arguments.length>1){const r=t.arguments[1].expression;"ObjectExpression"===r.type?c=r:"StringLiteral"===r.type?f=r.value:"TemplateLiteral"===r.type&&e.isSimpleTemplateLiteral(r)&&(f=r.quasis[0].cooked)}if(t.arguments.length>2){const e=t.arguments[2].expression;"ObjectExpression"===e.type&&(c=e)}const y=c?e.getObjectPropValue(c,"defaultValue"):void 0,g="string"==typeof y?y:f,h=e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1},d="string"==typeof g||h(c),x=h(c),k=Boolean(x||"string"==typeof g&&!("string"==typeof(v=g)&&/{{\s*count\s*}}/.test(v)));var v;for(let t=0;t<l.length;t++){let r,n=l[t];if(c){const t=e.getObjectPropValue(c,"ns");"string"==typeof t&&(r=t)}const o=this.config.extract.nsSeparator??":";if(!r&&o&&n.includes(o)){const e=n.split(o);if(r=e.shift(),n=e.join(o),!n||""===n.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${r}${o}'`);continue}}!r&&s?.defaultNs&&(r=s.defaultNs),r||(r=this.config.extract.defaultNS);let i=n;if(s?.keyPrefix){const e=this.config.extract.keySeparator??".";if(i=!1!==e?s.keyPrefix.endsWith(e)?`${s.keyPrefix}${n}`:`${s.keyPrefix}${e}${n}`:`${s.keyPrefix}${n}`,!1!==e){if(i.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${i}' (keyPrefix: '${s.keyPrefix}', key: '${n}')`);continue}}}const p=t===l.length-1&&g||n;if(c){const t=e.getObjectProperty(c,"context"),n=[];if("StringLiteral"===t?.value?.type||"NumericLiteral"===t?.value.type||"BooleanLiteral"===t?.value.type){const e=`${t.value.value}`,s=this.config.extract.contextSeparator??"_";""!==e&&n.push({key:`${i}${s}${e}`,ns:r,defaultValue:p,explicitDefault:d})}else if(t?.value){const e=this.expressionResolver.resolvePossibleContextStringValues(t.value),s=this.config.extract.contextSeparator??"_";e.length>0&&(e.forEach(e=>{n.push({key:`${i}${s}${e}`,ns:r,defaultValue:p,explicitDefault:d})}),n.push({key:i,ns:r,defaultValue:p,explicitDefault:d}))}const s=e=>{if(e){if("KeyValueProperty"===e.type&&e.key){if("Identifier"===e.key.type)return e.key.value;if("StringLiteral"===e.key.type)return e.key.value}return"KeyValueProperty"===e.type&&e.value&&"Identifier"===e.value.type?e.key&&"Identifier"===e.key.type?e.key.value:void 0:"ShorthandProperty"!==e.type&&"Identifier"!==e.type||!e.value?e.key&&"string"==typeof e.key?e.key:void 0:e.value}},o=(()=>{if(!c||!Array.isArray(c.properties))return!1;for(const e of c.properties){if("count"===s(e))return!0}return!1})(),l=(()=>{if(!c||!Array.isArray(c.properties))return!1;for(const e of c.properties){if("ordinal"===s(e))return!("KeyValueProperty"!==e.type||!e.value||"BooleanLiteral"!==e.value.type)&&Boolean(e.value.value)}return!1})();if(o||u){try{const e=u?"ordinal":"cardinal",t=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let s=!1;try{const r=new Intl.PluralRules(t,{type:e}).resolvedOptions().pluralCategories;1===r.length&&"other"===r[0]&&(s=!0)}catch{}if(!s){const t=new Set;for(const r of this.config.locales)try{new Intl.PluralRules(r,{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}catch{new Intl.PluralRules("en",{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}const r=Array.from(t).sort();1===r.length&&"other"===r[0]&&(s=!0)}if(s){if(n.length>0)for(const e of n)this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,hasCount:!0,isOrdinal:u});else this.pluginContext.addKey({key:i,ns:r,defaultValue:p,hasCount:!0,isOrdinal:u});continue}}catch(e){}this.config.extract.disablePlurals?n.length>0?n.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:i,ns:r,defaultValue:p,explicitDefault:d}):this.handlePluralKeys(i,r,c,l||u,g,k);continue}if(n.length>0){n.forEach(this.pluginContext.addKey);continue}!0===e.getObjectPropValue(c,"returnObjects")&&this.objectKeys.add(i)}a&&this.objectKeys.add(i),this.pluginContext.addKey({key:i,ns:r,defaultValue:p,explicitDefault:d})}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,n=[];let s=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(n.push(e),s=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&n.push(...this.expressionResolver.resolvePossibleKeyStringValues(e.expression));else n.push(...this.expressionResolver.resolvePossibleKeyStringValues(r));return{keysToProcess:n.filter(e=>!!e),isSelectorAPI:s}}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 r=t;const n=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)n.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;n.unshift(e.expression.value)}r=r.object}if(n.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return n.join(t)}return null}handlePluralKeys(t,r,n,s,o,i){try{const l=s?"ordinal":"cardinal",a=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:l});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:l});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}const u=Array.from(a).sort(),p=this.config.extract.pluralSeparator??"_",f=e.getObjectPropValue(n,"defaultValue"),c=e.getObjectPropValue(n,`defaultValue${p}other`),y=e.getObjectPropValue(n,`defaultValue${p}ordinal${p}other`),g=e.getObjectProperty(n,"context"),h=[];if(g?.value){const e=this.expressionResolver.resolvePossibleContextStringValues(g.value);if(e.length>0)if("StringLiteral"===g.value.type)for(const r of e)r.length>0&&h.push({key:t,context:r});else{for(const r of e)r.length>0&&h.push({key:t,context:r});!1!==this.config.extract?.generateBasePluralForms&&h.push({key:t})}else h.push({key:t})}else h.push({key:t});const d=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let x=!1;try{const e=new Intl.PluralRules(d,{type:l}).resolvedOptions().pluralCategories;1===e.length&&"other"===e[0]&&(x=!0)}catch{x=!1}if(x||1===u.length&&"other"===u[0]){for(const{key:t,context:l}of h){const a=e.getObjectPropValue(n,`defaultValue${p}other`);let u;u="string"==typeof a?a:"string"==typeof f?f:"string"==typeof o?o:l?`${t}_${l}`:t;const c=this.config.extract.contextSeparator??"_",y=l?`${t}${c}${l}`:t;this.pluginContext.addKey({key:y,ns:r,defaultValue:u,hasCount:!0,isOrdinal:s,explicitDefault:Boolean(i||"string"==typeof a)})}return}for(const{key:t,context:l}of h)for(const a of u){const u=s?`defaultValue${p}ordinal${p}${a}`:`defaultValue${p}${a}`,g=e.getObjectPropValue(n,u);let h,d;if(h="string"==typeof g?g:"one"===a&&"string"==typeof f?f:"one"===a&&"string"==typeof o?o:s&&"string"==typeof y?y:s||"string"!=typeof c?"string"==typeof f?f:"string"==typeof o?o:t:c,l){const e=this.config.extract.contextSeparator??"_";d=s?`${t}${e}${l}${p}ordinal${p}${a}`:`${t}${e}${l}${p}${a}`}else d=s?`${t}${p}ordinal${p}${a}`:`${t}${p}${a}`;this.pluginContext.addKey({key:d,ns:r,defaultValue:h,hasCount:!0,isOrdinal:s,explicitDefault:Boolean(i||"string"==typeof g||"string"==typeof c)})}}catch(s){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=o||e.getObjectPropValue(n,"defaultValue");this.pluginContext.addKey({key:t,ns:r,defaultValue:"string"==typeof i?i:t})}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}};
1
+ "use strict";var e=require("./ast-utils.js");exports.CallExpressionHandler=class{pluginContext;config;logger;expressionResolver;objectKeys=new Set;getCurrentFile;getCurrentCode;constructor(e,t,r,n,o,i){this.config=e,this.pluginContext=t,this.logger=r,this.expressionResolver=n,this.getCurrentFile=o,this.getCurrentCode=i}getLocationFromSpan(e){if(!e||"number"!=typeof e.start)return;const t=this.getCurrentCode(),r=e.start,n=t.substring(0,r).split("\n");return{line:n.length,column:n[n.length-1].length}}handleCallExpression(t,r){const n=this.getFunctionName(t.callee);if(!n)return;const o=r(n),i=this.config.extract.functions||["t","*.t"];let s=void 0!==o;if(!s)for(const e of i)if(e.startsWith("*.")){if(n.endsWith(e.substring(1))){s=!0;break}}else if(e===n){s=!0;break}if(!s||0===t.arguments.length)return;const{keysToProcess:l,isSelectorAPI:a}=this.handleCallExpressionArgument(t,0);if(0===l.length)return;let u=!1;const p=this.config.extract.pluralSeparator??"_";for(let e=0;e<l.length;e++)l[e].endsWith(`${p}ordinal`)&&(u=!0,l[e]=l[e].slice(0,-8));let c,f;if(t.arguments.length>1){const r=t.arguments[1].expression;"ObjectExpression"===r.type?f=r:"StringLiteral"===r.type?c=r.value:"TemplateLiteral"===r.type&&e.isSimpleTemplateLiteral(r)&&(c=r.quasis[0].cooked)}if(t.arguments.length>2){const e=t.arguments[2].expression;"ObjectExpression"===e.type&&(f=e)}const y=f?e.getObjectPropValue(f,"defaultValue"):void 0,g="string"==typeof y?y:c,h=e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1},d="string"==typeof g||h(f),x=h(f),k=Boolean(x||"string"==typeof g&&!("string"==typeof(v=g)&&/{{\s*count\s*}}/.test(v)));var v;for(let r=0;r<l.length;r++){let n,i=l[r];if(f){const t=e.getObjectPropValue(f,"ns");"string"==typeof t&&(n=t)}const s=this.config.extract.nsSeparator??":";if(!n&&s&&i.includes(s)){const e=i.split(s);if(n=e.shift(),i=e.join(s),!i||""===i.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${n}${s}'`);continue}}!n&&o?.defaultNs&&(n=o.defaultNs),n||(n=this.config.extract.defaultNS);let p=i;if(o?.keyPrefix){const e=this.config.extract.keySeparator??".";if(p=!1!==e?o.keyPrefix.endsWith(e)?`${o.keyPrefix}${i}`:`${o.keyPrefix}${e}${i}`:`${o.keyPrefix}${i}`,!1!==e){if(p.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${p}' (keyPrefix: '${o.keyPrefix}', key: '${i}')`);continue}}}const c=r===l.length-1&&g||i;if(f){const t=e.getObjectProperty(f,"context"),r=[];if("StringLiteral"===t?.value?.type||"NumericLiteral"===t?.value.type||"BooleanLiteral"===t?.value.type){const e=`${t.value.value}`,o=this.config.extract.contextSeparator??"_";""!==e&&r.push({key:`${p}${o}${e}`,ns:n,defaultValue:c,explicitDefault:d})}else if(t?.value){const e=this.expressionResolver.resolvePossibleContextStringValues(t.value),o=this.config.extract.contextSeparator??"_";e.length>0&&(e.forEach(e=>{r.push({key:`${p}${o}${e}`,ns:n,defaultValue:c,explicitDefault:d})}),r.push({key:p,ns:n,defaultValue:c,explicitDefault:d}))}const o=e=>{if(e){if("KeyValueProperty"===e.type&&e.key){if("Identifier"===e.key.type)return e.key.value;if("StringLiteral"===e.key.type)return e.key.value}return"KeyValueProperty"===e.type&&e.value&&"Identifier"===e.value.type?e.key&&"Identifier"===e.key.type?e.key.value:void 0:"ShorthandProperty"!==e.type&&"Identifier"!==e.type||!e.value?e.key&&"string"==typeof e.key?e.key:void 0:e.value}},i=(()=>{if(!f||!Array.isArray(f.properties))return!1;for(const e of f.properties){if("count"===o(e))return!0}return!1})(),s=(()=>{if(!f||!Array.isArray(f.properties))return!1;for(const e of f.properties){if("ordinal"===o(e))return!("KeyValueProperty"!==e.type||!e.value||"BooleanLiteral"!==e.value.type)&&Boolean(e.value.value)}return!1})();if(i||u){try{const e=u?"ordinal":"cardinal",t=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let o=!1;try{const r=new Intl.PluralRules(t,{type:e}).resolvedOptions().pluralCategories;1===r.length&&"other"===r[0]&&(o=!0)}catch{}if(!o){const t=new Set;for(const r of this.config.locales)try{new Intl.PluralRules(r,{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}catch{new Intl.PluralRules("en",{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}const r=Array.from(t).sort();1===r.length&&"other"===r[0]&&(o=!0)}if(o){if(r.length>0)for(const e of r)this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,hasCount:!0,isOrdinal:u});else this.pluginContext.addKey({key:p,ns:n,defaultValue:c,hasCount:!0,isOrdinal:u});continue}}catch(e){}this.config.extract.disablePlurals?r.length>0?r.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:p,ns:n,defaultValue:c,explicitDefault:d}):this.handlePluralKeys(p,n,f,s||u,g,k);continue}if(r.length>0){r.forEach(this.pluginContext.addKey);continue}!0===e.getObjectPropValue(f,"returnObjects")&&this.objectKeys.add(p)}a&&this.objectKeys.add(p);{const e=t.span?this.getLocationFromSpan(t.span):void 0;this.pluginContext.addKey({key:p,ns:n,defaultValue:c,explicitDefault:d,locations:e?[{file:this.getCurrentFile(),line:e.line,column:e.column}]:void 0})}}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,n=[];let o=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(n.push(e),o=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&n.push(...this.expressionResolver.resolvePossibleKeyStringValues(e.expression));else n.push(...this.expressionResolver.resolvePossibleKeyStringValues(r));return{keysToProcess:n.filter(e=>!!e),isSelectorAPI:o}}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 r=t;const n=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)n.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;n.unshift(e.expression.value)}r=r.object}if(n.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return n.join(t)}return null}handlePluralKeys(t,r,n,o,i,s){try{const l=o?"ordinal":"cardinal",a=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:l});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:l});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}const u=Array.from(a).sort(),p=this.config.extract.pluralSeparator??"_",c=e.getObjectPropValue(n,"defaultValue"),f=e.getObjectPropValue(n,`defaultValue${p}other`),y=e.getObjectPropValue(n,`defaultValue${p}ordinal${p}other`),g=e.getObjectProperty(n,"context"),h=[];if(g?.value){const e=this.expressionResolver.resolvePossibleContextStringValues(g.value);if(e.length>0)if("StringLiteral"===g.value.type)for(const r of e)r.length>0&&h.push({key:t,context:r});else{for(const r of e)r.length>0&&h.push({key:t,context:r});!1!==this.config.extract?.generateBasePluralForms&&h.push({key:t})}else h.push({key:t})}else h.push({key:t});const d=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let x=!1;try{const e=new Intl.PluralRules(d,{type:l}).resolvedOptions().pluralCategories;1===e.length&&"other"===e[0]&&(x=!0)}catch{x=!1}if(x||1===u.length&&"other"===u[0]){for(const{key:t,context:l}of h){const a=e.getObjectPropValue(n,`defaultValue${p}other`);let u;u="string"==typeof a?a:"string"==typeof c?c:"string"==typeof i?i:l?`${t}_${l}`:t;const f=this.config.extract.contextSeparator??"_",y=l?`${t}${f}${l}`:t;this.pluginContext.addKey({key:y,ns:r,defaultValue:u,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(s||"string"==typeof a)})}return}for(const{key:t,context:l}of h)for(const a of u){const u=o?`defaultValue${p}ordinal${p}${a}`:`defaultValue${p}${a}`,g=e.getObjectPropValue(n,u);let h,d;if(h="string"==typeof g?g:"one"===a&&"string"==typeof c?c:"one"===a&&"string"==typeof i?i:o&&"string"==typeof y?y:o||"string"!=typeof f?"string"==typeof c?c:"string"==typeof i?i:t:f,l){const e=this.config.extract.contextSeparator??"_";d=o?`${t}${e}${l}${p}ordinal${p}${a}`:`${t}${e}${l}${p}${a}`}else d=o?`${t}${p}ordinal${p}${a}`:`${t}${p}${a}`;this.pluginContext.addKey({key:d,ns:r,defaultValue:h,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(s||"string"==typeof g||"string"==typeof f)})}}catch(o){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const s=i||e.getObjectPropValue(n,"defaultValue");this.pluginContext.addKey({key:t,ns:r,defaultValue:"string"==typeof s?s:t})}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}};
@@ -1 +1 @@
1
- "use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.JSXHandler=class{config;pluginContext;expressionResolver;constructor(e,t,n){this.config=e,this.pluginContext=t,this.expressionResolver=n}handleJSXElement(t,n){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e.extractFromTransComponent(t,this.config),a=[];if(s){if(s.keyExpression){const e=this.expressionResolver.resolvePossibleKeyStringValues(s.keyExpression);a.push(...e)}else a.push(s.serializedChildren);let e;const{contextExpression:i,optionsNode:l,defaultValue:o,hasCount:r,isOrdinal:u,serializedChildren:f}=s;if(s.ns){const{ns:t}=s;e=a.map(e=>({key:e,ns:t,defaultValue:o||f,hasCount:r,isOrdinal:u}))}else{e=a.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let s;[n,...s]=e.split(t),e=s.join(t)}return{key:e,ns:n,defaultValue:o||f,hasCount:r,isOrdinal:u,explicitDefault:s.explicitDefault}});const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===i?.type&&"JSXExpressionContainer"===i.value?.type&&"Identifier"===i.value.expression.type){const t=n(i.value.expression.value);t?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=t.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),i&&r)if(this.config.extract.disablePlurals){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===i.type)for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n,a=this.expressionResolver.resolvePossibleContextStringValues(i),o=this.config.extract.contextSeparator??"_";if(a.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l));for(const t of a)for(const n of e){const e=`${n.key}${o}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,s,l,n.explicitDefault)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l,e.explicitDefault))}else if(i){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:a,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${n}${s}`,ns:a,defaultValue:i});"StringLiteral"!==i.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else if(r)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l,e.explicitDefault))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}}}generatePluralKeysForTrans(e,n,s,a,i,l){try{const o=a?"ordinal":"cardinal",r=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,u=this.config.extract.pluralSeparator??"_";let f,d;if(i&&(f=t.getObjectPropValue(i,`defaultValue${u}other`),d=t.getObjectPropValue(i,`defaultValue${u}ordinal${u}other`)),1===r.length&&"other"===r[0]){const o=i?t.getObjectPropValue(i,`defaultValue${u}other`):void 0,r="string"==typeof o?o:"string"==typeof n?n:e;return void this.pluginContext.addKey({key:e,ns:s,defaultValue:r,hasCount:!0,isOrdinal:a,explicitDefault:Boolean(l||"string"==typeof o||"string"==typeof f)})}for(const o of r){const r=a?`defaultValue${u}ordinal${u}${o}`:`defaultValue${u}${o}`,p=i?t.getObjectPropValue(i,r):void 0;let c;c="string"==typeof p?p:"one"===o&&"string"==typeof n?n:a&&"string"==typeof d?d:a||"string"!=typeof f?"string"==typeof n?n:e:f;const y=a?`${e}${u}ordinal${u}${o}`:`${e}${u}${o}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:c,hasCount:!0,isOrdinal:a,explicitDefault:Boolean(l||"string"==typeof p||"string"==typeof f)})}}catch(t){this.pluginContext.addKey({key:e,ns:s,defaultValue: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(".")}}};
1
+ "use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.JSXHandler=class{config;pluginContext;expressionResolver;getCurrentFile;getCurrentCode;constructor(e,t,n,s,o){this.config=e,this.pluginContext=t,this.expressionResolver=n,this.getCurrentFile=s,this.getCurrentCode=o}getLocationFromSpan(e){if(!e||"number"!=typeof e.start)return;const t=this.getCurrentCode(),n=e.start,s=t.substring(0,n).split("\n");return{line:s.length,column:s[s.length-1].length}}handleJSXElement(t,n){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e.extractFromTransComponent(t,this.config),o=[];if(s){if(s.keyExpression){const e=this.expressionResolver.resolvePossibleKeyStringValues(s.keyExpression);o.push(...e)}else o.push(s.serializedChildren);let e;const{contextExpression:i,optionsNode:a,defaultValue:l,hasCount:r,isOrdinal:u,serializedChildren:f}=s,c=t.span?this.getLocationFromSpan(t.span):void 0,d=c?[{file:this.getCurrentFile(),line:c.line,column:c.column}]:void 0;if(s.ns){const{ns:t}=s;e=o.map(e=>({key:e,ns:t,defaultValue:l||f,hasCount:r,isOrdinal:u,locations:d}))}else{e=o.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let s;[n,...s]=e.split(t),e=s.join(t)}return{key:e,ns:n,defaultValue:l||f,hasCount:r,isOrdinal:u,explicitDefault:s.explicitDefault,locations:d}});const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===i?.type&&"JSXExpressionContainer"===i.value?.type&&"Identifier"===i.value.expression.type){const t=n(i.value.expression.value);t?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=t.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),i&&r)if(this.config.extract.disablePlurals){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===i.type)for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue,locations:t.locations})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})});for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue,locations:t.locations})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n,o=this.expressionResolver.resolvePossibleContextStringValues(i),l=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,void 0,e.locations));for(const t of o)for(const n of e){const e=`${n.key}${l}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,s,a,n.explicitDefault,n.locations)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,e.explicitDefault,e.locations))}else if(i){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:o,defaultValue:i,locations:a}of e)this.pluginContext.addKey({key:`${t}${n}${s}`,ns:o,defaultValue:i,locations:a});"StringLiteral"!==i.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else if(r)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})});else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,e.explicitDefault,e.locations))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}}}generatePluralKeysForTrans(e,n,s,o,i,a,l){try{const r=o?"ordinal":"cardinal",u=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:r}).resolvedOptions().pluralCategories,f=this.config.extract.pluralSeparator??"_";let c,d;if(i&&(c=t.getObjectPropValue(i,`defaultValue${f}other`),d=t.getObjectPropValue(i,`defaultValue${f}ordinal${f}other`)),1===u.length&&"other"===u[0]){const r=i?t.getObjectPropValue(i,`defaultValue${f}other`):void 0,u="string"==typeof r?r:"string"==typeof n?n:e;return void this.pluginContext.addKey({key:e,ns:s,defaultValue:u,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(a||"string"==typeof r||"string"==typeof c),locations:l})}for(const r of u){const u=o?`defaultValue${f}ordinal${f}${r}`:`defaultValue${f}${r}`,p=i?t.getObjectPropValue(i,u):void 0;let g;g="string"==typeof p?p:"one"===r&&"string"==typeof n?n:o&&"string"==typeof d?d:o||"string"!=typeof c?"string"==typeof n?n:e:c;const y=o?`${e}${f}ordinal${f}${r}`:`${e}${f}${r}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:g,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(a||"string"==typeof p||"string"==typeof c),locations:l})}}catch(t){this.pluginContext.addKey({key:e,ns:s,defaultValue:n,locations:l})}}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(".")}}};
@@ -1 +1 @@
1
- "use strict";exports.createPluginContext=function(t,e,a,n){return{addKey:e=>{const n=!1===e.ns?void 0:e.ns,s=n??a.extract?.defaultNS??"translation",l=void 0===n,i=`${String(s)}:${e.key}`,u=e.defaultValue??e.key,o=t.get(i);if(o){const n=o.defaultValue===o.key||o.hasCount&&o.defaultValue&&o.key.includes("_")&&o.key.startsWith(o.defaultValue),r=u===e.key;n&&!r&&t.set(i,{...e,ns:s||a.extract?.defaultNS||"translation",nsIsImplicit:l,defaultValue:u})}else t.set(i,{...e,ns:s||a.extract?.defaultNS||"translation",nsIsImplicit:l,defaultValue:u})},config:Object.freeze({...a,plugins:[...e]}),logger:n,getVarFromScope:()=>{}}},exports.initializePlugins=async function(t){for(const e of t)await(e.setup?.())};
1
+ "use strict";exports.createPluginContext=function(t,e,a,n){return{addKey:e=>{const n=!1===e.ns?void 0:e.ns,s=n??a.extract?.defaultNS??"translation",l=void 0===n,o=`${String(s)}:${e.key}`,i=e.defaultValue??e.key,u=t.get(o);if(u){const n=u.defaultValue===u.key||u.hasCount&&u.defaultValue&&u.key.includes("_")&&u.key.startsWith(u.defaultValue),c=i===e.key;e.locations&&(u.locations=[...u.locations||[],...e.locations]),n&&!c&&t.set(o,{...e,ns:s||a.extract?.defaultNS||"translation",nsIsImplicit:l,defaultValue:i,locations:u.locations})}else t.set(o,{...e,ns:s||a.extract?.defaultNS||"translation",nsIsImplicit:l,defaultValue:i})},config:Object.freeze({...a,plugins:[...e]}),logger:n,getVarFromScope:()=>{}}},exports.initializePlugins=async function(t){for(const e of t)await(e.setup?.())};
@@ -1 +1 @@
1
- "use strict";var e=require("i18next-resources-for-ts"),t=require("glob"),r=require("ora"),s=require("chalk"),i=require("node:fs/promises"),n=require("node:path"),o=require("./utils/file-utils.js");exports.runTypesGenerator=async function(a){const c=r("Generating TypeScript types for translations...\n").start();try{a.extract.primaryLanguage||=a.locales[0]||"en";let r=a.extract.output||`locales/${a.extract.primaryLanguage}/*.json`;if(r=o.getOutputPath(r,a.extract.primaryLanguage||"en","*"),a.types||(a.types={input:r,output:"src/@types/i18next.d.ts"}),void 0===a.types.input&&(a.types.input=r),a.types.output||(a.types.output="src/@types/i18next.d.ts"),a.types.resourcesFile||(a.types.resourcesFile=n.join(n.dirname(a.types?.output),"resources.d.ts")),!a.types?.input||a.types?.input.length<0)return void console.log("No input defined!");const u=await t.glob(a.types?.input||[],{cwd:process.cwd()}),p=[];for(const e of u){const t=n.basename(e,n.extname(e)),r=await i.readFile(e,"utf-8"),s=JSON.parse(r);p.push({name:t,resources:s})}const l=[],y=a.types?.enableSelector||!1,d=e.mergeResourcesAsInterface(p,{optimize:!!y}),f=n.resolve(process.cwd(),a.types?.output||""),g=n.resolve(process.cwd(),a.types.resourcesFile);let m;await i.mkdir(n.dirname(g),{recursive:!0}),await i.writeFile(g,d),l.push(` ${s.green("✓")} Resources interface written to ${a.types.resourcesFile}`);try{await i.access(f),m=!0}catch(e){m=!1}if(!m){const e=`// This file is automatically generated by i18next-cli. Do not edit manually.\nimport Resources from './${n.relative(n.dirname(f),g).replace(/\\/g,"/").replace(/\.d\.ts$/,"")}';\n\ndeclare module 'i18next' {\n interface CustomTypeOptions {\n enableSelector: ${"string"==typeof y?`"${y}"`:y};\n defaultNS: '${a.extract.defaultNS||"translation"}';\n resources: Resources;\n }\n}`;await i.mkdir(n.dirname(f),{recursive:!0}),await i.writeFile(f,e),l.push(` ${s.green("✓")} TypeScript definitions written to ${a.types.output||""}`)}c.succeed(s.bold("TypeScript definitions generated successfully.")),l.forEach(e=>console.log(e))}catch(e){c.fail(s.red("Failed to generate TypeScript definitions.")),console.error(e)}};
1
+ "use strict";var e=require("i18next-resources-for-ts"),t=require("glob"),r=require("ora"),s=require("chalk"),i=require("node:fs/promises"),n=require("node:path"),a=require("./utils/file-utils.js");exports.runTypesGenerator=async function(o){const c=r("Generating TypeScript types for translations...\n").start();try{o.extract.primaryLanguage||=o.locales[0]||"en";let r=o.extract.output||`locales/${o.extract.primaryLanguage}/*.json`;if(r=a.getOutputPath(r,o.extract.primaryLanguage||"en","*"),o.types||(o.types={input:r,output:"src/@types/i18next.d.ts"}),void 0===o.types.input&&(o.types.input=r),o.types.output||(o.types.output="src/@types/i18next.d.ts"),o.types.resourcesFile||(o.types.resourcesFile=n.join(n.dirname(o.types?.output),"resources.d.ts")),!o.types?.input||o.types?.input.length<0)return void console.log("No input defined!");const u=await t.glob(o.types?.input||[],{cwd:process.cwd()}),p=[];for(const e of u){const t=n.basename(e,n.extname(e)),r=await i.readFile(e,"utf-8"),s=JSON.parse(r);p.push({name:t,resources:s})}const l=[],y=o.types?.enableSelector||!1,d=` // This file is automatically generated by i18next-cli. Do not edit manually.\n${e.mergeResourcesAsInterface(p,{optimize:!!y})}`,f=n.resolve(process.cwd(),o.types?.output||""),m=n.resolve(process.cwd(),o.types.resourcesFile);let g;await i.mkdir(n.dirname(m),{recursive:!0}),await i.writeFile(m,d),l.push(` ${s.green("✓")} Resources interface written to ${o.types.resourcesFile}`);try{await i.access(f),g=!0}catch(e){g=!1}if(!g){const e=`// This file is automatically generated by i18next-cli. Do not edit manually.\nimport Resources from './${n.relative(n.dirname(f),m).replace(/\\/g,"/").replace(/\.d\.ts$/,"")}';\n\ndeclare module 'i18next' {\n interface CustomTypeOptions {\n enableSelector: ${"string"==typeof y?`"${y}"`:y};\n defaultNS: '${o.extract.defaultNS||"translation"}';\n resources: Resources;\n }\n}`;await i.mkdir(n.dirname(f),{recursive:!0}),await i.writeFile(f,e),l.push(` ${s.green("✓")} TypeScript definitions written to ${o.types.output||""}`)}c.succeed(s.bold("TypeScript definitions generated successfully.")),l.forEach(e=>console.log(e))}catch(e){c.fail(s.red("Failed to generate TypeScript definitions.")),console.error(e)}};
package/dist/esm/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import{minimatch as i}from"minimatch";import n from"chalk";import{ensureConfig as a,loadConfig as r}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as s}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as l}from"./types-generator.js";import{runSyncer as p}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as f}from"./init.js";import{runLinterCli as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as u,runLocizeDownload as y,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.21.1"),w.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),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.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async t=>{try{const e=w.opts().config,n=await a(e),r=async()=>{const o=await s(n,{isWatchMode:!!t.watch,isDryRun:!!t.dryRun,syncPrimaryWithDefaults:!!t.syncPrimary});return t.ci&&!o?(console.log("✅ No files were updated."),process.exit(0)):t.ci&&o&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),o};if(await r(),t.watch){console.log("\nWatching for changes...");const t=await z(n.extract.input),e=x(n.extract.ignore),a=j(n.extract.output),c=[...e,...a].filter(Boolean),s=t.filter(t=>!c.some(o=>i(t,o,{dot:!0})));o.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),r()})}}catch(t){console.error("Error running extractor:",t),process.exit(1)}}),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(t,o)=>{const e=w.opts().config;let i=await r(e);if(!i){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await c();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 i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),i=t}await g(i,{detail:t,namespace:o.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 t=>{const e=w.opts().config,n=await a(e),r=()=>l(n);if(await r(),t.watch){console.log("\nWatching for changes...");const t=await z(n.types?.input||[]),e=[...x(n.extract?.ignore)].filter(Boolean),a=t.filter(t=>!e.some(o=>i(t,o,{dot:!0})));o.watch(a,{persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),r()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const t=w.opts().config,o=await a(t);await p(o)}),w.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async t=>{await m(t)}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(f),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async t=>{const e=w.opts().config,a=async()=>{let t=await r(e);if(!t){console.log(n.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(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!")),t=o}await d(t)};if(await a(),t.watch){console.log("\nWatching for changes...");const t=await r(e);if(t?.extract?.input){const e=await z(t.extract.input),n=[...x(t.extract.ignore),...j(t.extract.output)].filter(Boolean),r=e.filter(t=>!n.some(o=>i(t,o,{dot:!0})));o.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),a()})}}}),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 t=>{const o=w.opts().config,e=await a(o);await u(e,t)}),w.command("locize-download").description("Download all translations from your locize project.").action(async t=>{const o=w.opts().config,e=await a(o);await y(e,t)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async t=>{const o=w.opts().config,e=await a(o);await h(e,t)}),w.parse(process.argv);const x=t=>Array.isArray(t)?t:t?[t]:[],j=t=>t&&"string"==typeof t?[t.replace(/\{\{[^}]+\}\}/g,"*")]:[],z=async(t=[])=>{const o=x(t),i=await Promise.all(o.map(t=>e(t||"",{nodir:!0})));return Array.from(new Set(i.flat()))};
2
+ import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import{minimatch as i}from"minimatch";import n from"chalk";import{ensureConfig as a,loadConfig as r}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as s}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as l}from"./types-generator.js";import{runSyncer as p}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as f}from"./init.js";import{runLinterCli as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as u,runLocizeDownload as y,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.22.1"),w.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),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.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async t=>{try{const e=w.opts().config,n=await a(e),r=async()=>{const o=await s(n,{isWatchMode:!!t.watch,isDryRun:!!t.dryRun,syncPrimaryWithDefaults:!!t.syncPrimary});return t.ci&&!o?(console.log("✅ No files were updated."),process.exit(0)):t.ci&&o&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),o};if(await r(),t.watch){console.log("\nWatching for changes...");const t=await z(n.extract.input),e=x(n.extract.ignore),a=j(n.extract.output),c=[...e,...a].filter(Boolean),s=t.filter(t=>!c.some(o=>i(t,o,{dot:!0})));o.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),r()})}}catch(t){console.error("Error running extractor:",t),process.exit(1)}}),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(t,o)=>{const e=w.opts().config;let i=await r(e);if(!i){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await c();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 i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),i=t}await g(i,{detail:t,namespace:o.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 t=>{const e=w.opts().config,n=await a(e),r=()=>l(n);if(await r(),t.watch){console.log("\nWatching for changes...");const t=await z(n.types?.input||[]),e=[...x(n.extract?.ignore)].filter(Boolean),a=t.filter(t=>!e.some(o=>i(t,o,{dot:!0})));o.watch(a,{persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),r()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const t=w.opts().config,o=await a(t);await p(o)}),w.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async t=>{await m(t)}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(f),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async t=>{const e=w.opts().config,a=async()=>{let t=await r(e);if(!t){console.log(n.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(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!")),t=o}await d(t)};if(await a(),t.watch){console.log("\nWatching for changes...");const t=await r(e);if(t?.extract?.input){const e=await z(t.extract.input),n=[...x(t.extract.ignore),...j(t.extract.output)].filter(Boolean),r=e.filter(t=>!n.some(o=>i(t,o,{dot:!0})));o.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),a()})}}}),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 t=>{const o=w.opts().config,e=await a(o);await u(e,t)}),w.command("locize-download").description("Download all translations from your locize project.").action(async t=>{const o=w.opts().config,e=await a(o);await y(e,t)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async t=>{const o=w.opts().config,e=await a(o);await h(e,t)}),w.parse(process.argv);const x=t=>Array.isArray(t)?t:t?[t]:[],j=t=>t&&"string"==typeof t?[t.replace(/\{\{[^}]+\}\}/g,"*")]:[],z=async(t=[])=>{const o=x(t),i=await Promise.all(o.map(t=>e(t||"",{nodir:!0})));return Array.from(new Set(i.flat()))};
@@ -1 +1 @@
1
- import{ScopeManager as e}from"../parsers/scope-manager.js";import{ExpressionResolver as s}from"../parsers/expression-resolver.js";import{CallExpressionHandler as r}from"../parsers/call-expression-handler.js";import{JSXHandler as o}from"../parsers/jsx-handler.js";class a{pluginContext;config;logger;hooks;get objectKeys(){return this.callExpressionHandler.objectKeys}scopeManager;expressionResolver;callExpressionHandler;jsxHandler;constructor(a,t,i,n,l){this.pluginContext=t,this.config=a,this.logger=i,this.hooks={onBeforeVisitNode:n?.onBeforeVisitNode,onAfterVisitNode:n?.onAfterVisitNode,resolvePossibleKeyStringValues:n?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:n?.resolvePossibleContextStringValues},this.scopeManager=new e(a),this.expressionResolver=l??new s(this.hooks),this.callExpressionHandler=new r(a,t,i,this.expressionResolver),this.jsxHandler=new o(a,t,this.expressionResolver)}visit(e){this.scopeManager.reset(),this.expressionResolver.resetFileSymbols(),this.scopeManager.enterScope(),this.walk(e),this.scopeManager.exitScope()}walk(e){if(!e)return;let s=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.scopeManager.enterScope(),s=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);break;case"TSEnumDeclaration":case"TsEnumDeclaration":case"TsEnumDecl":this.expressionResolver.captureEnumDeclaration(e);break;case"CallExpression":this.callExpressionHandler.handleCallExpression(e,this.scopeManager.getVarFromScope.bind(this.scopeManager));break;case"JSXElement":this.jsxHandler.handleJSXElement(e,this.scopeManager.getVarFromScope.bind(this.scopeManager))}this.hooks.onAfterVisitNode?.(e);for(const s in e){if("span"===s)continue;const r=e[s];if(Array.isArray(r)){for(const e of r)if(e&&"object"==typeof e)if("VariableDeclarator"!==e.type){if(e&&e.id&&Array.isArray(e.members)&&this.expressionResolver.captureEnumDeclaration(e),"VariableDeclaration"===e.type&&Array.isArray(e.declarations))for(const s of e.declarations)s&&"object"==typeof s&&"VariableDeclarator"===s.type&&(this.scopeManager.handleVariableDeclarator(s),this.expressionResolver.captureVariableDeclarator(s))}else this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);for(const e of r)e&&"object"==typeof e&&this.walk(e)}else r&&"object"==typeof r&&this.walk(r)}s&&this.scopeManager.exitScope()}getVarFromScope(e){return this.scopeManager.getVarFromScope(e)}}export{a as ASTVisitors};
1
+ import{ScopeManager as e}from"../parsers/scope-manager.js";import{ExpressionResolver as r}from"../parsers/expression-resolver.js";import{CallExpressionHandler as s}from"../parsers/call-expression-handler.js";import{JSXHandler as t}from"../parsers/jsx-handler.js";class o{pluginContext;config;logger;hooks;get objectKeys(){return this.callExpressionHandler.objectKeys}scopeManager;expressionResolver;callExpressionHandler;jsxHandler;currentFile="";currentCode="";constructor(o,a,i,n,l){this.pluginContext=a,this.config=o,this.logger=i,this.hooks={onBeforeVisitNode:n?.onBeforeVisitNode,onAfterVisitNode:n?.onAfterVisitNode,resolvePossibleKeyStringValues:n?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:n?.resolvePossibleContextStringValues},this.scopeManager=new e(o),this.expressionResolver=l??new r(this.hooks),this.callExpressionHandler=new s(o,a,i,this.expressionResolver,()=>this.getCurrentFile(),()=>this.getCurrentCode()),this.jsxHandler=new t(o,a,this.expressionResolver,()=>this.getCurrentFile(),()=>this.getCurrentCode())}visit(e){this.scopeManager.reset(),this.expressionResolver.resetFileSymbols(),this.scopeManager.enterScope(),this.walk(e),this.scopeManager.exitScope()}walk(e){if(!e)return;let r=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.scopeManager.enterScope(),r=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);break;case"TSEnumDeclaration":case"TsEnumDeclaration":case"TsEnumDecl":this.expressionResolver.captureEnumDeclaration(e);break;case"CallExpression":this.callExpressionHandler.handleCallExpression(e,this.scopeManager.getVarFromScope.bind(this.scopeManager));break;case"JSXElement":this.jsxHandler.handleJSXElement(e,this.scopeManager.getVarFromScope.bind(this.scopeManager))}this.hooks.onAfterVisitNode?.(e);for(const r in e){if("span"===r)continue;const s=e[r];if(Array.isArray(s)){for(const e of s)if(e&&"object"==typeof e)if("VariableDeclarator"!==e.type){if(e&&e.id&&Array.isArray(e.members)&&this.expressionResolver.captureEnumDeclaration(e),"VariableDeclaration"===e.type&&Array.isArray(e.declarations))for(const r of e.declarations)r&&"object"==typeof r&&"VariableDeclarator"===r.type&&(this.scopeManager.handleVariableDeclarator(r),this.expressionResolver.captureVariableDeclarator(r))}else this.scopeManager.handleVariableDeclarator(e),this.expressionResolver.captureVariableDeclarator(e);for(const e of s)e&&"object"==typeof e&&this.walk(e)}else s&&"object"==typeof s&&this.walk(s)}r&&this.scopeManager.exitScope()}getVarFromScope(e){return this.scopeManager.getVarFromScope(e)}setCurrentFile(e,r){this.currentFile=e,this.currentCode=r}getCurrentFile(){return this.currentFile}getCurrentCode(){return this.currentCode}}export{o as ASTVisitors};
@@ -1 +1 @@
1
- import t from"ora";import a from"chalk";import{parse as e}from"@swc/core";import{mkdir as o,writeFile as r,readFile as n}from"node:fs/promises";import{dirname as s,extname as i}from"node:path";import{findKeys as c}from"./key-finder.js";import{getTranslations as l}from"./translation-manager.js";import{validateExtractorConfig as m,ExtractorError as f}from"../../utils/validation.js";import{extractKeysFromComments as p}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";import{serializeTranslationFile as y}from"../../utils/file-utils.js";import{shouldShowFunnel as d,recordFunnelShown as g}from"../../utils/funnel-msg-tracker.js";async function w(e,{isWatchMode:n=!1,isDryRun:i=!1,syncPrimaryWithDefaults:f=!1}={},p=new u){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(t=>t!==e?.extract?.primaryLanguage),e.extract.functions||=["t","*.t"],e.extract.transComponents||=["Trans"],m(e);const w=e.plugins||[],x=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:n}=await c(e,p);x.text=`Found ${t.size} unique keys. Updating translation files...`;const m=await l(t,n,e,{syncPrimaryWithDefaults:f});let u=!1;for(const t of m)if(t.updated&&(u=!0,!i)){const n=y(t.newTranslations,e.extract.outputFormat,e.extract.indentation);await o(s(t.path),{recursive:!0}),await r(t.path,n),p.info(a.green(`Updated: ${t.path}`))}if(w.length>0){x.text="Running post-extraction plugins...";for(const t of w)await(t.afterSync?.(m,e))}return x.succeed(a.bold("Extraction complete!")),u&&await async function(){if(!await d("extract"))return;return console.log(a.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${a.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${a.cyan("https://youtu.be/joPsZghT3wM")}`),g("extract")}(),u}catch(t){throw x.fail(a.red("Extraction failed.")),t}}async function x(t,a,o,r,s,c=new u){try{let l=await n(t,"utf-8");for(const e of a)try{const a=await(e.onLoad?.(l,t));void 0!==a&&(l=a)}catch(t){c.warn(`Plugin ${e.name} onLoad failed:`,t)}const m=i(t).toLowerCase(),u=".ts"===m||".tsx"===m||".mts"===m||".cts"===m,y=".tsx"===m;let d;try{d=await e(l,{syntax:u?"typescript":"ecmascript",tsx:y,decorators:!0,dynamicImport:!0,comments:!0})}catch(a){if(".ts"!==m||y)throw new f("Failed to process file",t,a);try{d=await e(l,{syntax:"typescript",tsx:!0,decorators:!0,dynamicImport:!0,comments:!0}),c.info?.(`Parsed ${t} using TSX fallback`)}catch(a){throw new f("Failed to process file",t,a)}}r.getVarFromScope=o.getVarFromScope.bind(o),o.visit(d),p(l,r,s,o.getVarFromScope.bind(o))}catch(a){throw new f("Failed to process file",t,a)}}async function h(t,{syncPrimaryWithDefaults:a=!1}={}){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(a=>a!==t?.extract?.primaryLanguage),t.extract.functions||=["t","*.t"],t.extract.transComponents||=["Trans"];const{allKeys:e,objectKeys:o}=await c(t);return l(e,o,t,{syncPrimaryWithDefaults:a})}export{h as extract,x as processFile,w as runExtractor};
1
+ import t from"ora";import a from"chalk";import{parse as e}from"@swc/core";import{mkdir as o,writeFile as r,readFile as n}from"node:fs/promises";import{dirname as s,extname as i}from"node:path";import{findKeys as c}from"./key-finder.js";import{getTranslations as l}from"./translation-manager.js";import{validateExtractorConfig as m,ExtractorError as f}from"../../utils/validation.js";import{extractKeysFromComments as p}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";import{serializeTranslationFile as y}from"../../utils/file-utils.js";import{shouldShowFunnel as d,recordFunnelShown as g}from"../../utils/funnel-msg-tracker.js";async function w(e,{isWatchMode:n=!1,isDryRun:i=!1,syncPrimaryWithDefaults:f=!1}={},p=new u){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(t=>t!==e?.extract?.primaryLanguage),e.extract.functions||=["t","*.t"],e.extract.transComponents||=["Trans"],m(e);const w=e.plugins||[],x=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:n}=await c(e,p);x.text=`Found ${t.size} unique keys. Updating translation files...`;const m=await l(t,n,e,{syncPrimaryWithDefaults:f});let u=!1;for(const t of m)if(t.updated&&(u=!0,!i)){const n=y(t.newTranslations,e.extract.outputFormat,e.extract.indentation);await o(s(t.path),{recursive:!0}),await r(t.path,n),p.info(a.green(`Updated: ${t.path}`))}if(w.length>0){x.text="Running post-extraction plugins...";for(const t of w)await(t.afterSync?.(m,e))}return x.succeed(a.bold("Extraction complete!")),u&&await async function(){if(!await d("extract"))return;return console.log(a.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${a.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${a.cyan("https://youtu.be/joPsZghT3wM")}`),g("extract")}(),u}catch(t){throw x.fail(a.red("Extraction failed.")),t}}async function x(t,a,o,r,s,c=new u){try{let l=await n(t,"utf-8");for(const e of a)try{const a=await(e.onLoad?.(l,t));void 0!==a&&(l=a)}catch(t){c.warn(`Plugin ${e.name} onLoad failed:`,t)}const m=i(t).toLowerCase(),u=".ts"===m||".tsx"===m||".mts"===m||".cts"===m,y=".tsx"===m;let d;try{d=await e(l,{syntax:u?"typescript":"ecmascript",tsx:y,decorators:!0,dynamicImport:!0,comments:!0})}catch(a){if(".ts"!==m||y)throw new f("Failed to process file",t,a);try{d=await e(l,{syntax:"typescript",tsx:!0,decorators:!0,dynamicImport:!0,comments:!0}),c.info?.(`Parsed ${t} using TSX fallback`)}catch(a){throw new f("Failed to process file",t,a)}}r.getVarFromScope=o.getVarFromScope.bind(o),o.setCurrentFile(t,l),o.visit(d),p(l,r,s,o.getVarFromScope.bind(o))}catch(a){throw new f("Failed to process file",t,a)}}async function h(t,{syncPrimaryWithDefaults:a=!1}={}){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(a=>a!==t?.extract?.primaryLanguage),t.extract.functions||=["t","*.t"],t.extract.transComponents||=["Trans"];const{allKeys:e,objectKeys:o}=await c(t);return l(e,o,t,{syncPrimaryWithDefaults:a})}export{h as extract,x as processFile,w as runExtractor};
@@ -1 +1 @@
1
- import{isSimpleTemplateLiteral as e,getObjectPropValue as t,getObjectProperty as r}from"./ast-utils.js";class n{pluginContext;config;logger;expressionResolver;objectKeys=new Set;constructor(e,t,r,n){this.config=e,this.pluginContext=t,this.logger=r,this.expressionResolver=n}handleCallExpression(n,s){const i=this.getFunctionName(n.callee);if(!i)return;const o=s(i),l=this.config.extract.functions||["t","*.t"];let a=void 0!==o;if(!a)for(const e of l)if(e.startsWith("*.")){if(i.endsWith(e.substring(1))){a=!0;break}}else if(e===i){a=!0;break}if(!a||0===n.arguments.length)return;const{keysToProcess:u,isSelectorAPI:f}=this.handleCallExpressionArgument(n,0);if(0===u.length)return;let p=!1;const c=this.config.extract.pluralSeparator??"_";for(let e=0;e<u.length;e++)u[e].endsWith(`${c}ordinal`)&&(p=!0,u[e]=u[e].slice(0,-8));let y,g;if(n.arguments.length>1){const t=n.arguments[1].expression;"ObjectExpression"===t.type?g=t:"StringLiteral"===t.type?y=t.value:"TemplateLiteral"===t.type&&e(t)&&(y=t.quasis[0].cooked)}if(n.arguments.length>2){const e=n.arguments[2].expression;"ObjectExpression"===e.type&&(g=e)}const h=g?t(g,"defaultValue"):void 0,d="string"==typeof h?h:y,x=e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1},k="string"==typeof d||x(g),v=x(g),$=Boolean(v||"string"==typeof d&&!("string"==typeof(m=d)&&/{{\s*count\s*}}/.test(m)));var m;for(let e=0;e<u.length;e++){let n,s=u[e];if(g){const e=t(g,"ns");"string"==typeof e&&(n=e)}const i=this.config.extract.nsSeparator??":";if(!n&&i&&s.includes(i)){const e=s.split(i);if(n=e.shift(),s=e.join(i),!s||""===s.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${n}${i}'`);continue}}!n&&o?.defaultNs&&(n=o.defaultNs),n||(n=this.config.extract.defaultNS);let l=s;if(o?.keyPrefix){const e=this.config.extract.keySeparator??".";if(l=!1!==e?o.keyPrefix.endsWith(e)?`${o.keyPrefix}${s}`:`${o.keyPrefix}${e}${s}`:`${o.keyPrefix}${s}`,!1!==e){if(l.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${l}' (keyPrefix: '${o.keyPrefix}', key: '${s}')`);continue}}}const a=e===u.length-1&&d||s;if(g){const e=r(g,"context"),s=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,r=this.config.extract.contextSeparator??"_";""!==t&&s.push({key:`${l}${r}${t}`,ns:n,defaultValue:a,explicitDefault:k})}else if(e?.value){const t=this.expressionResolver.resolvePossibleContextStringValues(e.value),r=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{s.push({key:`${l}${r}${e}`,ns:n,defaultValue:a,explicitDefault:k})}),s.push({key:l,ns:n,defaultValue:a,explicitDefault:k}))}const i=e=>{if(e){if("KeyValueProperty"===e.type&&e.key){if("Identifier"===e.key.type)return e.key.value;if("StringLiteral"===e.key.type)return e.key.value}return"KeyValueProperty"===e.type&&e.value&&"Identifier"===e.value.type?e.key&&"Identifier"===e.key.type?e.key.value:void 0:"ShorthandProperty"!==e.type&&"Identifier"!==e.type||!e.value?e.key&&"string"==typeof e.key?e.key:void 0:e.value}},o=(()=>{if(!g||!Array.isArray(g.properties))return!1;for(const e of g.properties){if("count"===i(e))return!0}return!1})(),u=(()=>{if(!g||!Array.isArray(g.properties))return!1;for(const e of g.properties){if("ordinal"===i(e))return!("KeyValueProperty"!==e.type||!e.value||"BooleanLiteral"!==e.value.type)&&Boolean(e.value.value)}return!1})();if(o||p){try{const e=p?"ordinal":"cardinal",t=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let r=!1;try{const n=new Intl.PluralRules(t,{type:e}).resolvedOptions().pluralCategories;1===n.length&&"other"===n[0]&&(r=!0)}catch{}if(!r){const t=new Set;for(const r of this.config.locales)try{new Intl.PluralRules(r,{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}catch{new Intl.PluralRules("en",{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}const n=Array.from(t).sort();1===n.length&&"other"===n[0]&&(r=!0)}if(r){if(s.length>0)for(const e of s)this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,hasCount:!0,isOrdinal:p});else this.pluginContext.addKey({key:l,ns:n,defaultValue:a,hasCount:!0,isOrdinal:p});continue}}catch(e){}this.config.extract.disablePlurals?s.length>0?s.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:l,ns:n,defaultValue:a,explicitDefault:k}):this.handlePluralKeys(l,n,g,u||p,d,$);continue}if(s.length>0){s.forEach(this.pluginContext.addKey);continue}!0===t(g,"returnObjects")&&this.objectKeys.add(l)}f&&this.objectKeys.add(l),this.pluginContext.addKey({key:l,ns:n,defaultValue:a,explicitDefault:k})}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,n=[];let s=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(n.push(e),s=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&n.push(...this.expressionResolver.resolvePossibleKeyStringValues(e.expression));else n.push(...this.expressionResolver.resolvePossibleKeyStringValues(r));return{keysToProcess:n.filter(e=>!!e),isSelectorAPI:s}}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 r=t;const n=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)n.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;n.unshift(e.expression.value)}r=r.object}if(n.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return n.join(t)}return null}handlePluralKeys(e,n,s,i,o,l){try{const a=i?"ordinal":"cardinal",u=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:a});t.resolvedOptions().pluralCategories.forEach(e=>u.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:a});t.resolvedOptions().pluralCategories.forEach(e=>u.add(e))}const f=Array.from(u).sort(),p=this.config.extract.pluralSeparator??"_",c=t(s,"defaultValue"),y=t(s,`defaultValue${p}other`),g=t(s,`defaultValue${p}ordinal${p}other`),h=r(s,"context"),d=[];if(h?.value){const t=this.expressionResolver.resolvePossibleContextStringValues(h.value);if(t.length>0)if("StringLiteral"===h.value.type)for(const r of t)r.length>0&&d.push({key:e,context:r});else{for(const r of t)r.length>0&&d.push({key:e,context:r});!1!==this.config.extract?.generateBasePluralForms&&d.push({key:e})}else d.push({key:e})}else d.push({key:e});const x=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let k=!1;try{const e=new Intl.PluralRules(x,{type:a}).resolvedOptions().pluralCategories;1===e.length&&"other"===e[0]&&(k=!0)}catch{k=!1}if(k||1===f.length&&"other"===f[0]){for(const{key:e,context:r}of d){const a=t(s,`defaultValue${p}other`);let u;u="string"==typeof a?a:"string"==typeof c?c:"string"==typeof o?o:r?`${e}_${r}`:e;const f=this.config.extract.contextSeparator??"_",y=r?`${e}${f}${r}`:e;this.pluginContext.addKey({key:y,ns:n,defaultValue:u,hasCount:!0,isOrdinal:i,explicitDefault:Boolean(l||"string"==typeof a)})}return}for(const{key:e,context:r}of d)for(const a of f){const u=t(s,i?`defaultValue${p}ordinal${p}${a}`:`defaultValue${p}${a}`);let f,h;if(f="string"==typeof u?u:"one"===a&&"string"==typeof c?c:"one"===a&&"string"==typeof o?o:i&&"string"==typeof g?g:i||"string"!=typeof y?"string"==typeof c?c:"string"==typeof o?o:e:y,r){const t=this.config.extract.contextSeparator??"_";h=i?`${e}${t}${r}${p}ordinal${p}${a}`:`${e}${t}${r}${p}${a}`}else h=i?`${e}${p}ordinal${p}${a}`:`${e}${p}${a}`;this.pluginContext.addKey({key:h,ns:n,defaultValue:f,hasCount:!0,isOrdinal:i,explicitDefault:Boolean(l||"string"==typeof u||"string"==typeof y)})}}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=o||t(s,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof i?i:e})}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}}export{n as CallExpressionHandler};
1
+ import{isSimpleTemplateLiteral as e,getObjectPropValue as t,getObjectProperty as r}from"./ast-utils.js";class n{pluginContext;config;logger;expressionResolver;objectKeys=new Set;getCurrentFile;getCurrentCode;constructor(e,t,r,n,i,s){this.config=e,this.pluginContext=t,this.logger=r,this.expressionResolver=n,this.getCurrentFile=i,this.getCurrentCode=s}getLocationFromSpan(e){if(!e||"number"!=typeof e.start)return;const t=this.getCurrentCode(),r=e.start,n=t.substring(0,r).split("\n");return{line:n.length,column:n[n.length-1].length}}handleCallExpression(n,i){const s=this.getFunctionName(n.callee);if(!s)return;const o=i(s),l=this.config.extract.functions||["t","*.t"];let a=void 0!==o;if(!a)for(const e of l)if(e.startsWith("*.")){if(s.endsWith(e.substring(1))){a=!0;break}}else if(e===s){a=!0;break}if(!a||0===n.arguments.length)return;const{keysToProcess:u,isSelectorAPI:f}=this.handleCallExpressionArgument(n,0);if(0===u.length)return;let p=!1;const c=this.config.extract.pluralSeparator??"_";for(let e=0;e<u.length;e++)u[e].endsWith(`${c}ordinal`)&&(p=!0,u[e]=u[e].slice(0,-8));let y,g;if(n.arguments.length>1){const t=n.arguments[1].expression;"ObjectExpression"===t.type?g=t:"StringLiteral"===t.type?y=t.value:"TemplateLiteral"===t.type&&e(t)&&(y=t.quasis[0].cooked)}if(n.arguments.length>2){const e=n.arguments[2].expression;"ObjectExpression"===e.type&&(g=e)}const h=g?t(g,"defaultValue"):void 0,d="string"==typeof h?h:y,x=e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1},k="string"==typeof d||x(g),v=x(g),$=Boolean(v||"string"==typeof d&&!("string"==typeof(m=d)&&/{{\s*count\s*}}/.test(m)));var m;for(let e=0;e<u.length;e++){let i,s=u[e];if(g){const e=t(g,"ns");"string"==typeof e&&(i=e)}const l=this.config.extract.nsSeparator??":";if(!i&&l&&s.includes(l)){const e=s.split(l);if(i=e.shift(),s=e.join(l),!s||""===s.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${i}${l}'`);continue}}!i&&o?.defaultNs&&(i=o.defaultNs),i||(i=this.config.extract.defaultNS);let a=s;if(o?.keyPrefix){const e=this.config.extract.keySeparator??".";if(a=!1!==e?o.keyPrefix.endsWith(e)?`${o.keyPrefix}${s}`:`${o.keyPrefix}${e}${s}`:`${o.keyPrefix}${s}`,!1!==e){if(a.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${a}' (keyPrefix: '${o.keyPrefix}', key: '${s}')`);continue}}}const c=e===u.length-1&&d||s;if(g){const e=r(g,"context"),n=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,r=this.config.extract.contextSeparator??"_";""!==t&&n.push({key:`${a}${r}${t}`,ns:i,defaultValue:c,explicitDefault:k})}else if(e?.value){const t=this.expressionResolver.resolvePossibleContextStringValues(e.value),r=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{n.push({key:`${a}${r}${e}`,ns:i,defaultValue:c,explicitDefault:k})}),n.push({key:a,ns:i,defaultValue:c,explicitDefault:k}))}const s=e=>{if(e){if("KeyValueProperty"===e.type&&e.key){if("Identifier"===e.key.type)return e.key.value;if("StringLiteral"===e.key.type)return e.key.value}return"KeyValueProperty"===e.type&&e.value&&"Identifier"===e.value.type?e.key&&"Identifier"===e.key.type?e.key.value:void 0:"ShorthandProperty"!==e.type&&"Identifier"!==e.type||!e.value?e.key&&"string"==typeof e.key?e.key:void 0:e.value}},o=(()=>{if(!g||!Array.isArray(g.properties))return!1;for(const e of g.properties){if("count"===s(e))return!0}return!1})(),l=(()=>{if(!g||!Array.isArray(g.properties))return!1;for(const e of g.properties){if("ordinal"===s(e))return!("KeyValueProperty"!==e.type||!e.value||"BooleanLiteral"!==e.value.type)&&Boolean(e.value.value)}return!1})();if(o||p){try{const e=p?"ordinal":"cardinal",t=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let r=!1;try{const n=new Intl.PluralRules(t,{type:e}).resolvedOptions().pluralCategories;1===n.length&&"other"===n[0]&&(r=!0)}catch{}if(!r){const t=new Set;for(const r of this.config.locales)try{new Intl.PluralRules(r,{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}catch{new Intl.PluralRules("en",{type:e}).resolvedOptions().pluralCategories.forEach(e=>t.add(e))}const n=Array.from(t).sort();1===n.length&&"other"===n[0]&&(r=!0)}if(r){if(n.length>0)for(const e of n)this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,hasCount:!0,isOrdinal:p});else this.pluginContext.addKey({key:a,ns:i,defaultValue:c,hasCount:!0,isOrdinal:p});continue}}catch(e){}this.config.extract.disablePlurals?n.length>0?n.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:a,ns:i,defaultValue:c,explicitDefault:k}):this.handlePluralKeys(a,i,g,l||p,d,$);continue}if(n.length>0){n.forEach(this.pluginContext.addKey);continue}!0===t(g,"returnObjects")&&this.objectKeys.add(a)}f&&this.objectKeys.add(a);{const e=n.span?this.getLocationFromSpan(n.span):void 0;this.pluginContext.addKey({key:a,ns:i,defaultValue:c,explicitDefault:k,locations:e?[{file:this.getCurrentFile(),line:e.line,column:e.column}]:void 0})}}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,n=[];let i=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(n.push(e),i=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&n.push(...this.expressionResolver.resolvePossibleKeyStringValues(e.expression));else n.push(...this.expressionResolver.resolvePossibleKeyStringValues(r));return{keysToProcess:n.filter(e=>!!e),isSelectorAPI:i}}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 r=t;const n=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)n.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;n.unshift(e.expression.value)}r=r.object}if(n.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return n.join(t)}return null}handlePluralKeys(e,n,i,s,o,l){try{const a=s?"ordinal":"cardinal",u=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:a});t.resolvedOptions().pluralCategories.forEach(e=>u.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:a});t.resolvedOptions().pluralCategories.forEach(e=>u.add(e))}const f=Array.from(u).sort(),p=this.config.extract.pluralSeparator??"_",c=t(i,"defaultValue"),y=t(i,`defaultValue${p}other`),g=t(i,`defaultValue${p}ordinal${p}other`),h=r(i,"context"),d=[];if(h?.value){const t=this.expressionResolver.resolvePossibleContextStringValues(h.value);if(t.length>0)if("StringLiteral"===h.value.type)for(const r of t)r.length>0&&d.push({key:e,context:r});else{for(const r of t)r.length>0&&d.push({key:e,context:r});!1!==this.config.extract?.generateBasePluralForms&&d.push({key:e})}else d.push({key:e})}else d.push({key:e});const x=this.config.extract?.primaryLanguage||(Array.isArray(this.config.locales)?this.config.locales[0]:void 0)||"en";let k=!1;try{const e=new Intl.PluralRules(x,{type:a}).resolvedOptions().pluralCategories;1===e.length&&"other"===e[0]&&(k=!0)}catch{k=!1}if(k||1===f.length&&"other"===f[0]){for(const{key:e,context:r}of d){const a=t(i,`defaultValue${p}other`);let u;u="string"==typeof a?a:"string"==typeof c?c:"string"==typeof o?o:r?`${e}_${r}`:e;const f=this.config.extract.contextSeparator??"_",y=r?`${e}${f}${r}`:e;this.pluginContext.addKey({key:y,ns:n,defaultValue:u,hasCount:!0,isOrdinal:s,explicitDefault:Boolean(l||"string"==typeof a)})}return}for(const{key:e,context:r}of d)for(const a of f){const u=t(i,s?`defaultValue${p}ordinal${p}${a}`:`defaultValue${p}${a}`);let f,h;if(f="string"==typeof u?u:"one"===a&&"string"==typeof c?c:"one"===a&&"string"==typeof o?o:s&&"string"==typeof g?g:s||"string"!=typeof y?"string"==typeof c?c:"string"==typeof o?o:e:y,r){const t=this.config.extract.contextSeparator??"_";h=s?`${e}${t}${r}${p}ordinal${p}${a}`:`${e}${t}${r}${p}${a}`}else h=s?`${e}${p}ordinal${p}${a}`:`${e}${p}${a}`;this.pluginContext.addKey({key:h,ns:n,defaultValue:f,hasCount:!0,isOrdinal:s,explicitDefault:Boolean(l||"string"==typeof u||"string"==typeof y)})}}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const s=o||t(i,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof s?s:e})}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}}export{n as CallExpressionHandler};
@@ -1 +1 @@
1
- import{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t}from"./ast-utils.js";class n{config;pluginContext;expressionResolver;constructor(e,t,n){this.config=e,this.pluginContext=t,this.expressionResolver=n}handleJSXElement(t,n){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e(t,this.config),a=[];if(s){if(s.keyExpression){const e=this.expressionResolver.resolvePossibleKeyStringValues(s.keyExpression);a.push(...e)}else a.push(s.serializedChildren);let e;const{contextExpression:i,optionsNode:l,defaultValue:o,hasCount:r,isOrdinal:u,serializedChildren:f}=s;if(s.ns){const{ns:t}=s;e=a.map(e=>({key:e,ns:t,defaultValue:o||f,hasCount:r,isOrdinal:u}))}else{e=a.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let s;[n,...s]=e.split(t),e=s.join(t)}return{key:e,ns:n,defaultValue:o||f,hasCount:r,isOrdinal:u,explicitDefault:s.explicitDefault}});const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===i?.type&&"JSXExpressionContainer"===i.value?.type&&"Identifier"===i.value.expression.type){const t=n(i.value.expression.value);t?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=t.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),i&&r)if(this.config.extract.disablePlurals){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===i.type)for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n,a=this.expressionResolver.resolvePossibleContextStringValues(i),o=this.config.extract.contextSeparator??"_";if(a.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l));for(const t of a)for(const n of e){const e=`${n.key}${o}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,s,l,n.explicitDefault)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l,e.explicitDefault))}else if(i){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:a,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${n}${s}`,ns:a,defaultValue:i});"StringLiteral"!==i.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else if(r)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,l,e.explicitDefault))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}}}generatePluralKeysForTrans(e,n,s,a,i,l){try{const o=a?"ordinal":"cardinal",r=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,u=this.config.extract.pluralSeparator??"_";let f,d;if(i&&(f=t(i,`defaultValue${u}other`),d=t(i,`defaultValue${u}ordinal${u}other`)),1===r.length&&"other"===r[0]){const o=i?t(i,`defaultValue${u}other`):void 0,r="string"==typeof o?o:"string"==typeof n?n:e;return void this.pluginContext.addKey({key:e,ns:s,defaultValue:r,hasCount:!0,isOrdinal:a,explicitDefault:Boolean(l||"string"==typeof o||"string"==typeof f)})}for(const o of r){const r=i?t(i,a?`defaultValue${u}ordinal${u}${o}`:`defaultValue${u}${o}`):void 0;let p;p="string"==typeof r?r:"one"===o&&"string"==typeof n?n:a&&"string"==typeof d?d:a||"string"!=typeof f?"string"==typeof n?n:e:f;const c=a?`${e}${u}ordinal${u}${o}`:`${e}${u}${o}`;this.pluginContext.addKey({key:c,ns:s,defaultValue:p,hasCount:!0,isOrdinal:a,explicitDefault:Boolean(l||"string"==typeof r||"string"==typeof f)})}}catch(t){this.pluginContext.addKey({key:e,ns:s,defaultValue: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(".")}}}export{n as JSXHandler};
1
+ import{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t}from"./ast-utils.js";class n{config;pluginContext;expressionResolver;getCurrentFile;getCurrentCode;constructor(e,t,n,s,o){this.config=e,this.pluginContext=t,this.expressionResolver=n,this.getCurrentFile=s,this.getCurrentCode=o}getLocationFromSpan(e){if(!e||"number"!=typeof e.start)return;const t=this.getCurrentCode(),n=e.start,s=t.substring(0,n).split("\n");return{line:s.length,column:s[s.length-1].length}}handleJSXElement(t,n){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e(t,this.config),o=[];if(s){if(s.keyExpression){const e=this.expressionResolver.resolvePossibleKeyStringValues(s.keyExpression);o.push(...e)}else o.push(s.serializedChildren);let e;const{contextExpression:i,optionsNode:a,defaultValue:l,hasCount:r,isOrdinal:u,serializedChildren:f}=s,c=t.span?this.getLocationFromSpan(t.span):void 0,d=c?[{file:this.getCurrentFile(),line:c.line,column:c.column}]:void 0;if(s.ns){const{ns:t}=s;e=o.map(e=>({key:e,ns:t,defaultValue:l||f,hasCount:r,isOrdinal:u,locations:d}))}else{e=o.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let s;[n,...s]=e.split(t),e=s.join(t)}return{key:e,ns:n,defaultValue:l||f,hasCount:r,isOrdinal:u,explicitDefault:s.explicitDefault,locations:d}});const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===i?.type&&"JSXExpressionContainer"===i.value?.type&&"Identifier"===i.value.expression.type){const t=n(i.value.expression.value);t?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=t.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),i&&r)if(this.config.extract.disablePlurals){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===i.type)for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue,locations:t.locations})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})});for(const s of t)for(const t of e){const e=`${t.key}${n}${s}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue,locations:t.locations})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n,o=this.expressionResolver.resolvePossibleContextStringValues(i),l=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,void 0,e.locations));for(const t of o)for(const n of e){const e=`${n.key}${l}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,s,a,n.explicitDefault,n.locations)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,e.explicitDefault,e.locations))}else if(i){const t=this.expressionResolver.resolvePossibleContextStringValues(i),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:o,defaultValue:i,locations:a}of e)this.pluginContext.addKey({key:`${t}${n}${s}`,ns:o,defaultValue:i,locations:a});"StringLiteral"!==i.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}else if(r)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})});else{const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,a,e.explicitDefault,e.locations))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue,locations:e.locations})})}}}generatePluralKeysForTrans(e,n,s,o,i,a,l){try{const r=o?"ordinal":"cardinal",u=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:r}).resolvedOptions().pluralCategories,f=this.config.extract.pluralSeparator??"_";let c,d;if(i&&(c=t(i,`defaultValue${f}other`),d=t(i,`defaultValue${f}ordinal${f}other`)),1===u.length&&"other"===u[0]){const r=i?t(i,`defaultValue${f}other`):void 0,u="string"==typeof r?r:"string"==typeof n?n:e;return void this.pluginContext.addKey({key:e,ns:s,defaultValue:u,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(a||"string"==typeof r||"string"==typeof c),locations:l})}for(const r of u){const u=i?t(i,o?`defaultValue${f}ordinal${f}${r}`:`defaultValue${f}${r}`):void 0;let p;p="string"==typeof u?u:"one"===r&&"string"==typeof n?n:o&&"string"==typeof d?d:o||"string"!=typeof c?"string"==typeof n?n:e:c;const y=o?`${e}${f}ordinal${f}${r}`:`${e}${f}${r}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:p,hasCount:!0,isOrdinal:o,explicitDefault:Boolean(a||"string"==typeof u||"string"==typeof c),locations:l})}}catch(t){this.pluginContext.addKey({key:e,ns:s,defaultValue:n,locations:l})}}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(".")}}}export{n as JSXHandler};
@@ -1 +1 @@
1
- async function t(t){for(const e of t)await(e.setup?.())}function e(t,e,a,n){return{addKey:e=>{const n=!1===e.ns?void 0:e.ns,l=n??a.extract?.defaultNS??"translation",s=void 0===n,u=`${String(l)}:${e.key}`,i=e.defaultValue??e.key,o=t.get(u);if(o){const n=o.defaultValue===o.key||o.hasCount&&o.defaultValue&&o.key.includes("_")&&o.key.startsWith(o.defaultValue),f=i===e.key;n&&!f&&t.set(u,{...e,ns:l||a.extract?.defaultNS||"translation",nsIsImplicit:s,defaultValue:i})}else t.set(u,{...e,ns:l||a.extract?.defaultNS||"translation",nsIsImplicit:s,defaultValue:i})},config:Object.freeze({...a,plugins:[...e]}),logger:n,getVarFromScope:()=>{}}}export{e as createPluginContext,t as initializePlugins};
1
+ async function t(t){for(const e of t)await(e.setup?.())}function e(t,e,a,n){return{addKey:e=>{const n=!1===e.ns?void 0:e.ns,l=n??a.extract?.defaultNS??"translation",o=void 0===n,s=`${String(l)}:${e.key}`,i=e.defaultValue??e.key,u=t.get(s);if(u){const n=u.defaultValue===u.key||u.hasCount&&u.defaultValue&&u.key.includes("_")&&u.key.startsWith(u.defaultValue),c=i===e.key;e.locations&&(u.locations=[...u.locations||[],...e.locations]),n&&!c&&t.set(s,{...e,ns:l||a.extract?.defaultNS||"translation",nsIsImplicit:o,defaultValue:i,locations:u.locations})}else t.set(s,{...e,ns:l||a.extract?.defaultNS||"translation",nsIsImplicit:o,defaultValue:i})},config:Object.freeze({...a,plugins:[...e]}),logger:n,getVarFromScope:()=>{}}}export{e as createPluginContext,t as initializePlugins};
@@ -1 +1 @@
1
- import{mergeResourcesAsInterface as e}from"i18next-resources-for-ts";import{glob as t}from"glob";import s from"ora";import r from"chalk";import{readFile as o,mkdir as i,writeFile as n,access as p}from"node:fs/promises";import{join as a,dirname as c,basename as u,extname as l,resolve as y,relative as f}from"node:path";import{getOutputPath as d}from"./utils/file-utils.js";async function m(m){const g=s("Generating TypeScript types for translations...\n").start();try{m.extract.primaryLanguage||=m.locales[0]||"en";let s=m.extract.output||`locales/${m.extract.primaryLanguage}/*.json`;if(s=d(s,m.extract.primaryLanguage||"en","*"),m.types||(m.types={input:s,output:"src/@types/i18next.d.ts"}),void 0===m.types.input&&(m.types.input=s),m.types.output||(m.types.output="src/@types/i18next.d.ts"),m.types.resourcesFile||(m.types.resourcesFile=a(c(m.types?.output),"resources.d.ts")),!m.types?.input||m.types?.input.length<0)return void console.log("No input defined!");const w=await t(m.types?.input||[],{cwd:process.cwd()}),x=[];for(const e of w){const t=u(e,l(e)),s=await o(e,"utf-8"),r=JSON.parse(s);x.push({name:t,resources:r})}const h=[],$=m.types?.enableSelector||!1,S=e(x,{optimize:!!$}),T=y(process.cwd(),m.types?.output||""),b=y(process.cwd(),m.types.resourcesFile);let F;await i(c(b),{recursive:!0}),await n(b,S),h.push(` ${r.green("✓")} Resources interface written to ${m.types.resourcesFile}`);try{await p(T),F=!0}catch(e){F=!1}if(!F){const e=`// This file is automatically generated by i18next-cli. Do not edit manually.\nimport Resources from './${f(c(T),b).replace(/\\/g,"/").replace(/\.d\.ts$/,"")}';\n\ndeclare module 'i18next' {\n interface CustomTypeOptions {\n enableSelector: ${"string"==typeof $?`"${$}"`:$};\n defaultNS: '${m.extract.defaultNS||"translation"}';\n resources: Resources;\n }\n}`;await i(c(T),{recursive:!0}),await n(T,e),h.push(` ${r.green("✓")} TypeScript definitions written to ${m.types.output||""}`)}g.succeed(r.bold("TypeScript definitions generated successfully.")),h.forEach(e=>console.log(e))}catch(e){g.fail(r.red("Failed to generate TypeScript definitions.")),console.error(e)}}export{m as runTypesGenerator};
1
+ import{mergeResourcesAsInterface as e}from"i18next-resources-for-ts";import{glob as t}from"glob";import s from"ora";import r from"chalk";import{readFile as o,mkdir as i,writeFile as n,access as a}from"node:fs/promises";import{join as p,dirname as c,basename as u,extname as l,resolve as y,relative as f}from"node:path";import{getOutputPath as d}from"./utils/file-utils.js";async function m(m){const g=s("Generating TypeScript types for translations...\n").start();try{m.extract.primaryLanguage||=m.locales[0]||"en";let s=m.extract.output||`locales/${m.extract.primaryLanguage}/*.json`;if(s=d(s,m.extract.primaryLanguage||"en","*"),m.types||(m.types={input:s,output:"src/@types/i18next.d.ts"}),void 0===m.types.input&&(m.types.input=s),m.types.output||(m.types.output="src/@types/i18next.d.ts"),m.types.resourcesFile||(m.types.resourcesFile=p(c(m.types?.output),"resources.d.ts")),!m.types?.input||m.types?.input.length<0)return void console.log("No input defined!");const w=await t(m.types?.input||[],{cwd:process.cwd()}),x=[];for(const e of w){const t=u(e,l(e)),s=await o(e,"utf-8"),r=JSON.parse(s);x.push({name:t,resources:r})}const h=[],$=m.types?.enableSelector||!1,S=` // This file is automatically generated by i18next-cli. Do not edit manually.\n${e(x,{optimize:!!$})}`,T=y(process.cwd(),m.types?.output||""),b=y(process.cwd(),m.types.resourcesFile);let F;await i(c(b),{recursive:!0}),await n(b,S),h.push(` ${r.green("✓")} Resources interface written to ${m.types.resourcesFile}`);try{await a(T),F=!0}catch(e){F=!1}if(!F){const e=`// This file is automatically generated by i18next-cli. Do not edit manually.\nimport Resources from './${f(c(T),b).replace(/\\/g,"/").replace(/\.d\.ts$/,"")}';\n\ndeclare module 'i18next' {\n interface CustomTypeOptions {\n enableSelector: ${"string"==typeof $?`"${$}"`:$};\n defaultNS: '${m.extract.defaultNS||"translation"}';\n resources: Resources;\n }\n}`;await i(c(T),{recursive:!0}),await n(T,e),h.push(` ${r.green("✓")} TypeScript definitions written to ${m.types.output||""}`)}g.succeed(r.bold("TypeScript definitions generated successfully.")),h.forEach(e=>console.log(e))}catch(e){g.fail(r.red("Failed to generate TypeScript definitions.")),console.error(e)}}export{m as runTypesGenerator};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.21.1",
3
+ "version": "1.22.1",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -22,7 +22,7 @@ const program = new Command()
22
22
  program
23
23
  .name('i18next-cli')
24
24
  .description('A unified, high-performance i18next CLI.')
25
- .version('1.21.1')
25
+ .version('1.22.1')
26
26
 
27
27
  // new: global config override option
28
28
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)')
@@ -41,6 +41,8 @@ export class ASTVisitors {
41
41
  private readonly expressionResolver: ExpressionResolver
42
42
  private readonly callExpressionHandler: CallExpressionHandler
43
43
  private readonly jsxHandler: JSXHandler
44
+ private currentFile: string = ''
45
+ private currentCode: string = ''
44
46
 
45
47
  /**
46
48
  * Creates a new AST visitor instance.
@@ -69,8 +71,21 @@ export class ASTVisitors {
69
71
  this.scopeManager = new ScopeManager(config)
70
72
  // use shared resolver when provided so captured enums/objects are visible across files
71
73
  this.expressionResolver = expressionResolver ?? new ExpressionResolver(this.hooks)
72
- this.callExpressionHandler = new CallExpressionHandler(config, pluginContext, logger, this.expressionResolver)
73
- this.jsxHandler = new JSXHandler(config, pluginContext, this.expressionResolver)
74
+ this.callExpressionHandler = new CallExpressionHandler(
75
+ config,
76
+ pluginContext,
77
+ logger,
78
+ this.expressionResolver,
79
+ () => this.getCurrentFile(),
80
+ () => this.getCurrentCode()
81
+ )
82
+ this.jsxHandler = new JSXHandler(
83
+ config,
84
+ pluginContext,
85
+ this.expressionResolver,
86
+ () => this.getCurrentFile(),
87
+ () => this.getCurrentCode()
88
+ )
74
89
  }
75
90
 
76
91
  /**
@@ -211,4 +226,35 @@ export class ASTVisitors {
211
226
  public getVarFromScope (name: string): ScopeInfo | undefined {
212
227
  return this.scopeManager.getVarFromScope(name)
213
228
  }
229
+
230
+ /**
231
+ * Sets the current file path used by the extractor.
232
+ *
233
+ * @param file - The file path (absolute or relative) to set as the current processing context.
234
+ * @remarks
235
+ * Updating the current file allows subsequent AST visitors and extraction logic to
236
+ * associate nodes, messages, and errors with the correct source file.
237
+ */
238
+ public setCurrentFile (file: string, code: string): void {
239
+ this.currentFile = file
240
+ this.currentCode = code
241
+ }
242
+
243
+ /**
244
+ * Returns the currently set file path.
245
+ *
246
+ * @returns The current file path as a string, or `undefined` if no file has been set.
247
+ * @remarks
248
+ * Use this to retrieve the file context that was previously set via `setCurrentFile`.
249
+ */
250
+ public getCurrentFile (): string {
251
+ return this.currentFile
252
+ }
253
+
254
+ /**
255
+ * @returns The full source code string for the file currently under processing.
256
+ */
257
+ public getCurrentCode (): string {
258
+ return this.currentCode
259
+ }
214
260
  }
@@ -194,6 +194,9 @@ export async function processFile (
194
194
  // This avoids a circular dependency while giving plugins access to the scope.
195
195
  pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors)
196
196
 
197
+ // Pass BOTH file and code
198
+ astVisitors.setCurrentFile(file, code)
199
+
197
200
  // 3. FIRST: Visit the AST to build scope information
198
201
  astVisitors.visit(ast)
199
202
 
@@ -9,17 +9,43 @@ export class CallExpressionHandler {
9
9
  private logger: Logger
10
10
  private expressionResolver: ExpressionResolver
11
11
  public objectKeys = new Set<string>()
12
+ private getCurrentFile: () => string
13
+ private getCurrentCode: () => string
12
14
 
13
15
  constructor (
14
16
  config: Omit<I18nextToolkitConfig, 'plugins'>,
15
17
  pluginContext: PluginContext,
16
18
  logger: Logger,
17
- expressionResolver: ExpressionResolver
19
+ expressionResolver: ExpressionResolver,
20
+ getCurrentFile: () => string,
21
+ getCurrentCode: () => string
18
22
  ) {
19
23
  this.config = config
20
24
  this.pluginContext = pluginContext
21
25
  this.logger = logger
22
26
  this.expressionResolver = expressionResolver
27
+ this.getCurrentFile = getCurrentFile
28
+ this.getCurrentCode = getCurrentCode
29
+ }
30
+
31
+ /**
32
+ * Helper method to calculate line and column from byte offset.
33
+ * SWC provides byte offsets in span.start, not line/column directly.
34
+ */
35
+ private getLocationFromSpan (span: any): { line: number, column: number } | undefined {
36
+ if (!span || typeof span.start !== 'number') return undefined
37
+
38
+ const code = this.getCurrentCode()
39
+ const offset = span.start
40
+
41
+ // Calculate line and column from byte offset
42
+ const upToOffset = code.substring(0, offset)
43
+ const lines = upToOffset.split('\n')
44
+
45
+ return {
46
+ line: lines.length,
47
+ column: lines[lines.length - 1].length
48
+ }
23
49
  }
24
50
 
25
51
  /**
@@ -363,7 +389,24 @@ export class CallExpressionHandler {
363
389
  }
364
390
 
365
391
  // 5. Default case: Add the simple key
366
- this.pluginContext.addKey({ key: finalKey, ns, defaultValue: dv, explicitDefault: explicitDefaultForBase })
392
+ {
393
+ // ✅ Use the helper method to calculate proper line/column
394
+ const location = node.span ? this.getLocationFromSpan(node.span) : undefined
395
+
396
+ this.pluginContext.addKey({
397
+ key: finalKey,
398
+ ns,
399
+ defaultValue: dv,
400
+ explicitDefault: explicitDefaultForBase,
401
+ locations: location
402
+ ? [{
403
+ file: this.getCurrentFile(),
404
+ line: location.line,
405
+ column: location.column
406
+ }]
407
+ : undefined
408
+ })
409
+ }
367
410
  }
368
411
  }
369
412
 
@@ -8,15 +8,39 @@ export class JSXHandler {
8
8
  private config: Omit<I18nextToolkitConfig, 'plugins'>
9
9
  private pluginContext: PluginContext
10
10
  private expressionResolver: ExpressionResolver
11
+ private getCurrentFile: () => string
12
+ private getCurrentCode: () => string // ✅ Add this
11
13
 
12
14
  constructor (
13
15
  config: Omit<I18nextToolkitConfig, 'plugins'>,
14
16
  pluginContext: PluginContext,
15
- expressionResolver: ExpressionResolver
17
+ expressionResolver: ExpressionResolver,
18
+ getCurrentFile: () => string,
19
+ getCurrentCode: () => string // ✅ Add parameter
16
20
  ) {
17
21
  this.config = config
18
22
  this.pluginContext = pluginContext
19
23
  this.expressionResolver = expressionResolver
24
+ this.getCurrentFile = getCurrentFile
25
+ this.getCurrentCode = getCurrentCode // ✅ Store it
26
+ }
27
+
28
+ /**
29
+ * Helper method to calculate line and column from byte offset.
30
+ */
31
+ private getLocationFromSpan (span: any): { line: number, column: number } | undefined {
32
+ if (!span || typeof span.start !== 'number') return undefined
33
+
34
+ const code = this.getCurrentCode()
35
+ const offset = span.start
36
+
37
+ const upToOffset = code.substring(0, offset)
38
+ const lines = upToOffset.split('\n')
39
+
40
+ return {
41
+ line: lines.length,
42
+ column: lines[lines.length - 1].length
43
+ }
20
44
  }
21
45
 
22
46
  /**
@@ -48,6 +72,16 @@ export class JSXHandler {
48
72
 
49
73
  const { contextExpression, optionsNode, defaultValue, hasCount, isOrdinal, serializedChildren } = extractedAttributes
50
74
 
75
+ // ✅ Extract location information using the helper method
76
+ const location = node.span ? this.getLocationFromSpan(node.span) : undefined
77
+ const locations = location
78
+ ? [{
79
+ file: this.getCurrentFile(),
80
+ line: location.line,
81
+ column: location.column
82
+ }]
83
+ : undefined
84
+
51
85
  // If ns is not explicitly set on the component, try to find it from the key
52
86
  // or the `t` prop
53
87
  if (!extractedAttributes.ns) {
@@ -71,6 +105,7 @@ export class JSXHandler {
71
105
  hasCount,
72
106
  isOrdinal,
73
107
  explicitDefault: extractedAttributes.explicitDefault,
108
+ locations
74
109
  }
75
110
  })
76
111
 
@@ -106,6 +141,7 @@ export class JSXHandler {
106
141
  defaultValue: defaultValue || serializedChildren,
107
142
  hasCount,
108
143
  isOrdinal,
144
+ locations
109
145
  }
110
146
  })
111
147
  }
@@ -133,7 +169,12 @@ export class JSXHandler {
133
169
  for (const context of contextValues) {
134
170
  for (const extractedKey of extractedKeys) {
135
171
  const contextKey = `${extractedKey.key}${contextSeparator}${context}`
136
- this.pluginContext.addKey({ key: contextKey, ns: extractedKey.ns, defaultValue: extractedKey.defaultValue })
172
+ this.pluginContext.addKey({
173
+ key: contextKey,
174
+ ns: extractedKey.ns,
175
+ defaultValue: extractedKey.defaultValue,
176
+ locations: extractedKey.locations
177
+ })
137
178
  }
138
179
  }
139
180
  } else {
@@ -142,13 +183,19 @@ export class JSXHandler {
142
183
  this.pluginContext.addKey({
143
184
  key: extractedKey.key,
144
185
  ns: extractedKey.ns,
145
- defaultValue: extractedKey.defaultValue
186
+ defaultValue: extractedKey.defaultValue,
187
+ locations: extractedKey.locations
146
188
  })
147
189
  })
148
190
  for (const context of contextValues) {
149
191
  for (const extractedKey of extractedKeys) {
150
192
  const contextKey = `${extractedKey.key}${contextSeparator}${context}`
151
- this.pluginContext.addKey({ key: contextKey, ns: extractedKey.ns, defaultValue: extractedKey.defaultValue })
193
+ this.pluginContext.addKey({
194
+ key: contextKey,
195
+ ns: extractedKey.ns,
196
+ defaultValue: extractedKey.defaultValue,
197
+ locations: extractedKey.locations
198
+ })
152
199
  }
153
200
  }
154
201
  }
@@ -158,7 +205,8 @@ export class JSXHandler {
158
205
  this.pluginContext.addKey({
159
206
  key: extractedKey.key,
160
207
  ns: extractedKey.ns,
161
- defaultValue: extractedKey.defaultValue
208
+ defaultValue: extractedKey.defaultValue,
209
+ locations: extractedKey.locations
162
210
  })
163
211
  })
164
212
  }
@@ -179,18 +227,18 @@ export class JSXHandler {
179
227
  // Generate all combinations of context and plural forms
180
228
  if (contextValues.length > 0) {
181
229
  // Generate base plural forms (no context)
182
- extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
230
+ extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, undefined, extractedKey.locations))
183
231
 
184
232
  // Generate context + plural combinations
185
233
  for (const context of contextValues) {
186
234
  for (const extractedKey of extractedKeys) {
187
235
  const contextKey = `${extractedKey.key}${contextSeparator}${context}`
188
- this.generatePluralKeysForTrans(contextKey, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault)
236
+ this.generatePluralKeysForTrans(contextKey, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault, extractedKey.locations)
189
237
  }
190
238
  }
191
239
  } else {
192
240
  // Fallback to just plural forms if context resolution fails
193
- extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault))
241
+ extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault, extractedKey.locations))
194
242
  }
195
243
  }
196
244
  } else if (contextExpression) {
@@ -200,8 +248,13 @@ export class JSXHandler {
200
248
  if (contextValues.length > 0) {
201
249
  // Add context variants
202
250
  for (const context of contextValues) {
203
- for (const { key, ns, defaultValue } of extractedKeys) {
204
- this.pluginContext.addKey({ key: `${key}${contextSeparator}${context}`, ns, defaultValue })
251
+ for (const { key, ns, defaultValue, locations } of extractedKeys) {
252
+ this.pluginContext.addKey({
253
+ key: `${key}${contextSeparator}${context}`,
254
+ ns,
255
+ defaultValue,
256
+ locations
257
+ })
205
258
  }
206
259
  }
207
260
  // Only add the base key as a fallback if the context is dynamic (i.e., not a simple string).
@@ -210,7 +263,8 @@ export class JSXHandler {
210
263
  this.pluginContext.addKey({
211
264
  key: extractedKey.key,
212
265
  ns: extractedKey.ns,
213
- defaultValue: extractedKey.defaultValue
266
+ defaultValue: extractedKey.defaultValue,
267
+ locations: extractedKey.locations
214
268
  })
215
269
  })
216
270
  }
@@ -220,7 +274,8 @@ export class JSXHandler {
220
274
  this.pluginContext.addKey({
221
275
  key: extractedKey.key,
222
276
  ns: extractedKey.ns,
223
- defaultValue: extractedKey.defaultValue
277
+ defaultValue: extractedKey.defaultValue,
278
+ locations: extractedKey.locations
224
279
  })
225
280
  })
226
281
  }
@@ -232,7 +287,8 @@ export class JSXHandler {
232
287
  this.pluginContext.addKey({
233
288
  key: extractedKey.key,
234
289
  ns: extractedKey.ns,
235
- defaultValue: extractedKey.defaultValue
290
+ defaultValue: extractedKey.defaultValue,
291
+ locations: extractedKey.locations
236
292
  })
237
293
  })
238
294
  } else {
@@ -246,7 +302,7 @@ export class JSXHandler {
246
302
  )
247
303
  const isOrdinal = !!ordinalAttr
248
304
 
249
- extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault))
305
+ extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode, extractedKey.explicitDefault, extractedKey.locations))
250
306
  }
251
307
  } else {
252
308
  // No count or context - just add the base keys
@@ -254,7 +310,8 @@ export class JSXHandler {
254
310
  this.pluginContext.addKey({
255
311
  key: extractedKey.key,
256
312
  ns: extractedKey.ns,
257
- defaultValue: extractedKey.defaultValue
313
+ defaultValue: extractedKey.defaultValue,
314
+ locations: extractedKey.locations
258
315
  })
259
316
  })
260
317
  }
@@ -270,8 +327,18 @@ export class JSXHandler {
270
327
  * @param ns - Namespace for the keys
271
328
  * @param isOrdinal - Whether to generate ordinal plural forms
272
329
  * @param optionsNode - Optional tOptions object expression for plural-specific defaults
330
+ * @param explicitDefaultFromSource - Whether the default was explicitly provided in source
331
+ * @param locations - Source location information for this key
273
332
  */
274
- private generatePluralKeysForTrans (key: string, defaultValue: string | undefined, ns: string | false | undefined, isOrdinal: boolean, optionsNode?: ObjectExpression, explicitDefaultFromSource?: boolean): void {
333
+ private generatePluralKeysForTrans (
334
+ key: string,
335
+ defaultValue: string | undefined,
336
+ ns: string | false | undefined,
337
+ isOrdinal: boolean,
338
+ optionsNode?: ObjectExpression,
339
+ explicitDefaultFromSource?: boolean,
340
+ locations?: Array<{ file: string, line?: number, column?: number }>
341
+ ): void {
275
342
  try {
276
343
  const type = isOrdinal ? 'ordinal' : 'cardinal'
277
344
  const pluralCategories = new Intl.PluralRules(this.config.extract?.primaryLanguage, { type }).resolvedOptions().pluralCategories
@@ -299,7 +366,8 @@ export class JSXHandler {
299
366
  defaultValue: finalDefault,
300
367
  hasCount: true,
301
368
  isOrdinal,
302
- explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string')
369
+ explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string'),
370
+ locations
303
371
  })
304
372
  return
305
373
  }
@@ -344,12 +412,18 @@ export class JSXHandler {
344
412
  // Only treat plural/context variant as explicit when:
345
413
  // - the extractor indicated the default was explicit on the source element
346
414
  // - OR a plural-specific default was provided in tOptions (specificDefault/otherDefault)
347
- explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string')
415
+ explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string'),
416
+ locations
348
417
  })
349
418
  }
350
419
  } catch (e) {
351
420
  // Fallback to a simple key if Intl API fails
352
- this.pluginContext.addKey({ key, ns, defaultValue })
421
+ this.pluginContext.addKey({
422
+ key,
423
+ ns,
424
+ defaultValue,
425
+ locations
426
+ })
353
427
  }
354
428
  }
355
429
 
@@ -39,7 +39,12 @@ export async function initializePlugins (plugins: any[]): Promise<void> {
39
39
  * })
40
40
  * ```
41
41
  */
42
- export function createPluginContext (allKeys: Map<string, ExtractedKey>, plugins: Plugin[], config: Omit<I18nextToolkitConfig, 'plugins'>, logger: Logger): PluginContext {
42
+ export function createPluginContext (
43
+ allKeys: Map<string, ExtractedKey>,
44
+ plugins: Plugin[],
45
+ config: Omit<I18nextToolkitConfig, 'plugins'>,
46
+ logger: Logger
47
+ ): PluginContext {
43
48
  const pluginContextConfig = Object.freeze({
44
49
  ...config,
45
50
  plugins: [...plugins],
@@ -74,14 +79,33 @@ export function createPluginContext (allKeys: Map<string, ExtractedKey>, plugins
74
79
 
75
80
  const isNewGenericFallback = defaultValue === keyInfo.key
76
81
 
82
+ // Merge locations
83
+ if (keyInfo.locations) {
84
+ existingKey.locations = [
85
+ ...(existingKey.locations || []),
86
+ ...keyInfo.locations
87
+ ]
88
+ }
89
+
77
90
  // If existing value is a generic fallback and new value is specific, replace it
78
91
  if (isExistingGenericFallback && !isNewGenericFallback) {
79
- allKeys.set(uniqueKey, { ...keyInfo, ns: storedNs || config.extract?.defaultNS || 'translation', nsIsImplicit, defaultValue })
92
+ allKeys.set(uniqueKey, {
93
+ ...keyInfo,
94
+ ns: storedNs || config.extract?.defaultNS || 'translation',
95
+ nsIsImplicit,
96
+ defaultValue,
97
+ locations: existingKey.locations // Preserve merged locations
98
+ })
80
99
  }
81
100
  // Otherwise keep the existing one
82
101
  } else {
83
102
  // New key, just add it
84
- allKeys.set(uniqueKey, { ...keyInfo, ns: storedNs || config.extract?.defaultNS || 'translation', nsIsImplicit, defaultValue })
103
+ allKeys.set(uniqueKey, {
104
+ ...keyInfo,
105
+ ns: storedNs || config.extract?.defaultNS || 'translation',
106
+ nsIsImplicit,
107
+ defaultValue
108
+ })
85
109
  }
86
110
  },
87
111
  config: pluginContextConfig,
@@ -77,7 +77,9 @@ export async function runTypesGenerator (config: I18nextToolkitConfig) {
77
77
  const logMessages: string[] = []
78
78
 
79
79
  const enableSelector = config.types?.enableSelector || false
80
- const interfaceDefinition = mergeResourcesAsInterface(resources, { optimize: !!enableSelector })
80
+ const interfaceDefinition = ` // This file is automatically generated by i18next-cli. Do not edit manually.
81
+ ${mergeResourcesAsInterface(resources, { optimize: !!enableSelector })}`
82
+
81
83
  const outputPath = resolve(process.cwd(), config.types?.output || '')
82
84
  const resourcesOutputPath = resolve(process.cwd(), config.types.resourcesFile)
83
85
  await mkdir(dirname(resourcesOutputPath), { recursive: true })
package/src/types.ts CHANGED
@@ -322,6 +322,13 @@ export interface ExtractedKey {
322
322
 
323
323
  /** True when the extractor returned an already-expanded plural form (e.g. "key_one") */
324
324
  isExpandedPlural?: boolean
325
+
326
+ /** Source locations where this key was found (optional, populated by plugins) */
327
+ locations?: Array<{
328
+ file: string
329
+ line?: number
330
+ column?: number
331
+ }>
325
332
  }
326
333
 
327
334
  /**
@@ -33,6 +33,8 @@ export declare class ASTVisitors {
33
33
  private readonly expressionResolver;
34
34
  private readonly callExpressionHandler;
35
35
  private readonly jsxHandler;
36
+ private currentFile;
37
+ private currentCode;
36
38
  /**
37
39
  * Creates a new AST visitor instance.
38
40
  *
@@ -72,5 +74,26 @@ export declare class ASTVisitors {
72
74
  * @private
73
75
  */
74
76
  getVarFromScope(name: string): ScopeInfo | undefined;
77
+ /**
78
+ * Sets the current file path used by the extractor.
79
+ *
80
+ * @param file - The file path (absolute or relative) to set as the current processing context.
81
+ * @remarks
82
+ * Updating the current file allows subsequent AST visitors and extraction logic to
83
+ * associate nodes, messages, and errors with the correct source file.
84
+ */
85
+ setCurrentFile(file: string, code: string): void;
86
+ /**
87
+ * Returns the currently set file path.
88
+ *
89
+ * @returns The current file path as a string, or `undefined` if no file has been set.
90
+ * @remarks
91
+ * Use this to retrieve the file context that was previously set via `setCurrentFile`.
92
+ */
93
+ getCurrentFile(): string;
94
+ /**
95
+ * @returns The full source code string for the file currently under processing.
96
+ */
97
+ getCurrentCode(): string;
75
98
  }
76
99
  //# sourceMappingURL=ast-visitors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAQ,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAInE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,KAAK,CAAiB;IAE9B,IAAW,UAAU,gBAEpB;IAED,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAmBzC;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAUjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAiGZ;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;CAG7D"}
1
+ {"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAQ,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAInE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,KAAK,CAAiB;IAE9B,IAAW,UAAU,gBAEpB;IAED,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAEhC;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAgCzC;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAUjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAiGZ;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI5D;;;;;;;OAOG;IACI,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxD;;;;;;OAMG;IACI,cAAc,IAAK,MAAM;IAIhC;;OAEG;IACI,cAAc,IAAK,MAAM;CAGjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,EACE,WAAmB,EACnB,QAAgB,EAChB,uBAA+B,EAChC,GAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC9B,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAyDlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CAiEf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,sDAO3I"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,EACE,WAAmB,EACnB,QAAgB,EAChB,uBAA+B,EAChC,GAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC9B,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAyDlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CAoEf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,sDAO3I"}
@@ -7,7 +7,14 @@ export declare class CallExpressionHandler {
7
7
  private logger;
8
8
  private expressionResolver;
9
9
  objectKeys: Set<string>;
10
- constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, logger: Logger, expressionResolver: ExpressionResolver);
10
+ private getCurrentFile;
11
+ private getCurrentCode;
12
+ constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, logger: Logger, expressionResolver: ExpressionResolver, getCurrentFile: () => string, getCurrentCode: () => string);
13
+ /**
14
+ * Helper method to calculate line and column from byte offset.
15
+ * SWC provides byte offsets in span.start, not line/column directly.
16
+ */
17
+ private getLocationFromSpan;
11
18
  /**
12
19
  * Processes function call expressions to extract translation keys.
13
20
  *
@@ -1 +1 @@
1
- {"version":3,"file":"call-expression-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/call-expression-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6C,MAAM,WAAW,CAAA;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG1D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,kBAAkB,CAAoB;IACvC,UAAU,cAAoB;gBAGnC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB;IAQxC;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI;IA2UxG;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IA2LxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
1
+ {"version":3,"file":"call-expression-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/call-expression-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6C,MAAM,WAAW,CAAA;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG1D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,kBAAkB,CAAoB;IACvC,UAAU,cAAoB;IACrC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAc;gBAGlC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,MAAM,MAAM,EAC5B,cAAc,EAAE,MAAM,MAAM;IAU9B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI;IA4VxG;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IA2LxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
@@ -5,7 +5,13 @@ export declare class JSXHandler {
5
5
  private config;
6
6
  private pluginContext;
7
7
  private expressionResolver;
8
- constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, expressionResolver: ExpressionResolver);
8
+ private getCurrentFile;
9
+ private getCurrentCode;
10
+ constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, expressionResolver: ExpressionResolver, getCurrentFile: () => string, getCurrentCode: () => string);
11
+ /**
12
+ * Helper method to calculate line and column from byte offset.
13
+ */
14
+ private getLocationFromSpan;
9
15
  /**
10
16
  * Processes JSX elements to extract translation keys from Trans components.
11
17
  *
@@ -27,6 +33,8 @@ export declare class JSXHandler {
27
33
  * @param ns - Namespace for the keys
28
34
  * @param isOrdinal - Whether to generate ordinal plural forms
29
35
  * @param optionsNode - Optional tOptions object expression for plural-specific defaults
36
+ * @param explicitDefaultFromSource - Whether the default was explicitly provided in source
37
+ * @param locations - Source location information for this key
30
38
  */
31
39
  private generatePluralKeysForTrans;
32
40
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"jsx-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,WAAW,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAgB,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAI1D,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,kBAAkB,CAAoB;gBAG5C,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB;IAOxC;;;;;;;;OAQG;IACH,gBAAgB,CAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IA0OjI;;;;;;;;OAQG;IACH,OAAO,CAAC,0BAA0B;IAkFlC;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;CAevB"}
1
+ {"version":3,"file":"jsx-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,WAAW,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAgB,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAI1D,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,kBAAkB,CAAoB;IAC9C,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAc;gBAGlC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,MAAM,MAAM,EAC5B,cAAc,EAAE,MAAM,MAAM;IAS9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;;;;;OAQG;IACH,gBAAgB,CAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IA2QjI;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAiGlC;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;CAevB"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjG;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAkDxK"}
1
+ {"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjG;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,EAAE,MAAM,GACb,aAAa,CAqEf"}
@@ -1 +1 @@
1
- {"version":3,"file":"types-generator.d.ts","sourceRoot":"","sources":["../src/types-generator.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAanD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CAAE,MAAM,EAAE,oBAAoB,iBA0EpE"}
1
+ {"version":3,"file":"types-generator.d.ts","sourceRoot":"","sources":["../src/types-generator.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAanD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CAAE,MAAM,EAAE,oBAAoB,iBA4EpE"}
package/types/types.d.ts CHANGED
@@ -264,6 +264,12 @@ export interface ExtractedKey {
264
264
  explicitDefault?: boolean;
265
265
  /** True when the extractor returned an already-expanded plural form (e.g. "key_one") */
266
266
  isExpandedPlural?: boolean;
267
+ /** Source locations where this key was found (optional, populated by plugins) */
268
+ locations?: Array<{
269
+ file: string;
270
+ line?: number;
271
+ column?: number;
272
+ }>;
267
273
  }
268
274
  /**
269
275
  * Result of processing translation files for a specific locale and namespace.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}