i18next-cli 0.9.6 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +8 -0
  3. package/dist/cjs/cli.js +1 -1
  4. package/dist/cjs/config.js +1 -1
  5. package/dist/cjs/extractor/core/extractor.js +1 -1
  6. package/dist/cjs/extractor/core/key-finder.js +1 -1
  7. package/dist/cjs/extractor/core/translation-manager.js +1 -1
  8. package/dist/cjs/extractor/parsers/ast-visitors.js +1 -1
  9. package/dist/cjs/status.js +1 -1
  10. package/dist/esm/cli.js +1 -1
  11. package/dist/esm/config.js +1 -1
  12. package/dist/esm/extractor/core/extractor.js +1 -1
  13. package/dist/esm/extractor/core/key-finder.js +1 -1
  14. package/dist/esm/extractor/core/translation-manager.js +1 -1
  15. package/dist/esm/extractor/parsers/ast-visitors.js +1 -1
  16. package/dist/esm/status.js +1 -1
  17. package/package.json +1 -1
  18. package/src/cli.ts +1 -1
  19. package/src/config.ts +10 -9
  20. package/src/extractor/core/extractor.ts +12 -18
  21. package/src/extractor/core/key-finder.ts +8 -4
  22. package/src/extractor/core/translation-manager.ts +7 -1
  23. package/src/extractor/parsers/ast-visitors.ts +86 -49
  24. package/src/status.ts +1 -1
  25. package/src/types.ts +2 -2
  26. package/types/config.d.ts +3 -3
  27. package/types/config.d.ts.map +1 -1
  28. package/types/extractor/core/extractor.d.ts +2 -1
  29. package/types/extractor/core/extractor.d.ts.map +1 -1
  30. package/types/extractor/core/key-finder.d.ts +4 -1
  31. package/types/extractor/core/key-finder.d.ts.map +1 -1
  32. package/types/extractor/core/translation-manager.d.ts +1 -1
  33. package/types/extractor/core/translation-manager.d.ts.map +1 -1
  34. package/types/extractor/parsers/ast-visitors.d.ts +1 -0
  35. package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
  36. package/types/types.d.ts +2 -2
  37. package/types/types.d.ts.map +1 -1
package/CHANGELOG.md CHANGED
@@ -5,10 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.0.0](https://github.com/i18next/i18next-cli/compare/v0.9.6...v1.0.0) - 2025-xx-yy
8
+ ## [1.0.0](https://github.com/i18next/i18next-cli/compare/v0.9.8...v1.0.0) - 2025-xx-yy
9
9
 
10
10
  - not yet released
11
11
 
12
+ ## [0.9.8] - 2025-09-25
13
+
14
+ - support t returnObjects
15
+
16
+ ## [0.9.7] - 2025-09-25
17
+
18
+ - support t key fallbacks
19
+
12
20
  ## [0.9.6] - 2025-09-25
13
21
 
14
22
  - show amount of namespaces in status output
package/README.md CHANGED
@@ -477,6 +477,14 @@ t('key', { ns: 'namespace' })
477
477
 
478
478
  // With interpolation
479
479
  t('key', { name: 'John' })
480
+
481
+ // With key fallbacks
482
+ t(['key.primary', 'key.fallback']);
483
+ t(['key.primary', 'key.fallback'], 'The fallback value');
484
+ t(['key.primary', 'key.fallback'], { defaultValue: 'The fallback value' });
485
+
486
+ // With structured content (returnObjects)
487
+ t('countries', { returnObjects: true });
480
488
  ```
481
489
 
482
490
  ### React Components
package/dist/cjs/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var e=require("commander"),t=require("chokidar"),o=require("glob"),n=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:fs/promises"),require("node:path");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.6"),f.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a);e.ci&&t&&(console.error(n.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(n.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${n.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}),f.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,t)=>{let o=await i.loadConfig();if(!o){console.log(n.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),o=e}await g.runStatus(o,{detail:e,namespace:t.namespace})}),f.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const n=await i.ensureConfig(),a=()=>c.runTypesGenerator(n);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(n.types?.input||[]),{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),f.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=await i.ensureConfig();await s.runSyncer(e)}),f.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await l.runMigrator()}),f.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(u.runInit),f.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let e=await i.loadConfig();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await d.runLinter(e)}),f.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
2
+ "use strict";var e=require("commander"),t=require("chokidar"),o=require("glob"),n=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:fs/promises"),require("node:path");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.8"),f.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a);e.ci&&t&&(console.error(n.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(n.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${n.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}),f.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,t)=>{let o=await i.loadConfig();if(!o){console.log(n.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),o=e}await g.runStatus(o,{detail:e,namespace:t.namespace})}),f.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const n=await i.ensureConfig(),a=()=>c.runTypesGenerator(n);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await o.glob(n.types?.input||[]),{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),f.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=await i.ensureConfig();await s.runSyncer(e)}),f.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await l.runMigrator()}),f.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(u.runInit),f.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let e=await i.loadConfig();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await d.runLinter(e)}),f.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
@@ -1 +1 @@
1
- "use strict";var e=require("node:path"),r=require("node:url"),n=require("node:fs/promises"),o=require("jiti"),t=require("inquirer"),i=require("chalk"),a=require("./init.js"),c="undefined"!=typeof document?document.currentScript:null;const s=["i18next.config.ts","i18next.config.js","i18next.config.mjs","i18next.config.cjs"];async function u(){const t=await async function(){for(const r of s){const o=e.resolve(process.cwd(),r);try{return await n.access(o),o}catch{}}return null}();if(!t)return null;try{let e;if(t.endsWith(".ts")){const r=o.createJiti("undefined"==typeof document?require("url").pathToFileURL(__filename).href:c&&"SCRIPT"===c.tagName.toUpperCase()&&c.src||new URL("config.js",document.baseURI).href),n=await r.import(t,{default:!0});e=n}else{const n=r.pathToFileURL(t).href,o=await import(`${n}?t=${Date.now()}`);e=o.default}return e?(e.extract||={},e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(r=>r!==e.extract.primaryLanguage),e):(console.error(`Error: No default export found in ${t}`),null)}catch(e){return console.error(`Error loading configuration from ${t}`),console.error(e),null}}exports.defineConfig=function(e){return e},exports.ensureConfig=async function(){let e=await u();if(e)return e;const{shouldInit:r}=await t.prompt([{type:"confirm",name:"shouldInit",message:i.yellow("Configuration file not found. Would you like to create one now?"),default:!0}]);if(r){if(await a.runInit(),console.log(i.green("Configuration created. Resuming command...")),e=await u(),e)return e;console.error(i.red("Error: Failed to load configuration after creation. Please try running the command again.")),process.exit(1)}else console.log("Operation cancelled. Please create a configuration file to proceed."),process.exit(0)},exports.loadConfig=u;
1
+ "use strict";var e=require("node:path"),r=require("node:url"),n=require("node:fs/promises"),t=require("jiti"),o=require("inquirer"),i=require("chalk"),a=require("./init.js"),c=require("./utils/logger.js"),u="undefined"!=typeof document?document.currentScript:null;const s=["i18next.config.ts","i18next.config.js","i18next.config.mjs","i18next.config.cjs"];async function l(o=new c.ConsoleLogger){const i=await async function(){for(const r of s){const t=e.resolve(process.cwd(),r);try{return await n.access(t),t}catch{}}return null}();if(!i)return null;try{let e;if(i.endsWith(".ts")){const r=t.createJiti("undefined"==typeof document?require("url").pathToFileURL(__filename).href:u&&"SCRIPT"===u.tagName.toUpperCase()&&u.src||new URL("config.js",document.baseURI).href),n=await r.import(i,{default:!0});e=n}else{const n=r.pathToFileURL(i).href,t=await import(`${n}?t=${Date.now()}`);e=t.default}return e?(e.extract||={},e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(r=>r!==e.extract.primaryLanguage),e):(o.error(`Error: No default export found in ${i}`),null)}catch(e){return o.error(`Error loading configuration from ${i}`),o.error(e),null}}exports.defineConfig=function(e){return e},exports.ensureConfig=async function(e=new c.ConsoleLogger){let r=await l();if(r)return r;const{shouldInit:n}=await o.prompt([{type:"confirm",name:"shouldInit",message:i.yellow("Configuration file not found. Would you like to create one now?"),default:!0}]);if(n){if(await a.runInit(),e.info(i.green("Configuration created. Resuming command...")),r=await l(),r)return r;e.error(i.red("Error: Failed to load configuration after creation. Please try running the command again.")),process.exit(1)}else e.info("Operation cancelled. Please create a configuration file to proceed."),process.exit(0)},exports.loadConfig=l;
@@ -1 +1 @@
1
- "use strict";var t=require("ora"),e=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),n=require("node:path"),s=require("./key-finder.js"),o=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../plugin-manager.js"),u=require("../parsers/comment-parser.js"),l=require("../parsers/ast-visitors.js"),g=require("../../utils/logger.js");function f(t,e,r){if(t&&"object"==typeof t){for(const a of e)try{a.onVisitNode?.(t,r)}catch(t){console.warn(`Plugin ${a.name} onVisitNode failed:`,t)}for(const a of Object.keys(t)){const n=t[a];if(Array.isArray(n))for(const t of n)t&&"object"==typeof t&&f(t,e,r);else n&&"object"==typeof n&&f(n,e,r)}}}exports.extract=async function(t){t.extract.primaryLanguage||(t.extract.primaryLanguage=t.locales[0]),t.extract.secondaryLanguages||(t.extract.secondaryLanguages=t.locales.filter(e=>e!==t?.extract?.primaryLanguage)),t.extract.functions||(t.extract.functions=["t"]),t.extract.transComponents||(t.extract.transComponents=["Trans"]);const e=await s.findKeys(t);return o.getTranslations(e,t)},exports.processFile=async function(t,e,n,s){try{let o=await a.readFile(t,"utf-8");for(const r of e.plugins||[])o=await(r.onLoad?.(o,t))??o;const i=await r.parse(o,{syntax:"typescript",tsx:!0,comments:!0}),g=c.createPluginContext(s);u.extractKeysFromComments(o,e.extract.functions||["t"],g,e);new l.ASTVisitors(e,g,n).visit(i),(e.plugins||[]).length>0&&f(i,e.plugins||[],g)}catch(e){throw new i.ExtractorError("Failed to process file",t,e)}},exports.runExtractor=async function(r,c=new g.ConsoleLogger){r.extract.primaryLanguage||(r.extract.primaryLanguage=r.locales[0]||"en"),r.extract.secondaryLanguages||(r.extract.secondaryLanguages=r.locales.filter(t=>t!==r?.extract?.primaryLanguage)),i.validateExtractorConfig(r);const u=t("Running i18next key extractor...\n").start();try{const t=await s.findKeys(r,c);u.text=`Found ${t.size} unique keys. Updating translation files...`;const i=await o.getTranslations(t,r);let l=!1;for(const t of i)t.updated&&(l=!0,await a.mkdir(n.dirname(t.path),{recursive:!0}),await a.writeFile(t.path,JSON.stringify(t.newTranslations,null,2)),c.info(e.green(`Updated: ${t.path}`)));return u.succeed(e.bold("Extraction complete!")),l}catch(t){throw u.fail(e.red("Extraction failed.")),t}};
1
+ "use strict";var e=require("ora"),t=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),n=require("node:path"),o=require("./key-finder.js"),s=require("./translation-manager.js"),i=require("../../utils/validation.js"),c=require("../plugin-manager.js"),l=require("../parsers/comment-parser.js"),u=require("../../utils/logger.js");function g(e,t,r,a=new u.ConsoleLogger){if(e&&"object"==typeof e){for(const n of t)try{n.onVisitNode?.(e,r)}catch(e){a.warn(`Plugin ${n.name} onVisitNode failed:`,e)}for(const n of Object.keys(e)){const o=e[n];if(Array.isArray(o))for(const e of o)e&&"object"==typeof e&&g(e,t,r,a);else o&&"object"==typeof o&&g(o,t,r,a)}}}exports.extract=async function(e){e.extract.primaryLanguage||(e.extract.primaryLanguage=e.locales[0]),e.extract.secondaryLanguages||(e.extract.secondaryLanguages=e.locales.filter(t=>t!==e?.extract?.primaryLanguage)),e.extract.functions||(e.extract.functions=["t"]),e.extract.transComponents||(e.extract.transComponents=["Trans"]);const{allKeys:t,objectKeys:r}=await o.findKeys(e);return s.getTranslations(t,r,e)},exports.processFile=async function(e,t,n,o,s=new u.ConsoleLogger){try{let i=await a.readFile(e,"utf-8");for(const r of t.plugins||[])i=await(r.onLoad?.(i,e))??i;const u=await r.parse(i,{syntax:"typescript",tsx:!0,comments:!0}),f=c.createPluginContext(n);l.extractKeysFromComments(i,t.extract.functions||["t"],f,t),o.visit(u),(t.plugins||[]).length>0&&g(u,t.plugins||[],f,s)}catch(t){throw new i.ExtractorError("Failed to process file",e,t)}},exports.runExtractor=async function(r,c=new u.ConsoleLogger){r.extract.primaryLanguage||(r.extract.primaryLanguage=r.locales[0]||"en"),r.extract.secondaryLanguages||(r.extract.secondaryLanguages=r.locales.filter(e=>e!==r?.extract?.primaryLanguage)),i.validateExtractorConfig(r);const l=e("Running i18next key extractor...\n").start();try{const{allKeys:e,objectKeys:i}=await o.findKeys(r,c);l.text=`Found ${e.size} unique keys. Updating translation files...`;const u=await s.getTranslations(e,i,r);let g=!1;for(const e of u)e.updated&&(g=!0,await a.mkdir(n.dirname(e.path),{recursive:!0}),await a.writeFile(e.path,JSON.stringify(e.newTranslations,null,2)),c.info(t.green(`Updated: ${e.path}`)));return l.succeed(t.bold("Extraction complete!")),g}catch(e){throw l.fail(t.red("Extraction failed.")),e}};
@@ -1 +1 @@
1
- "use strict";var e=require("glob"),n=require("./extractor.js"),r=require("../../utils/logger.js"),i=require("../plugin-manager.js");exports.findKeys=async function(o,s=new r.ConsoleLogger){const t=await async function(n){return await e.glob(n.extract.input,{ignore:"node_modules/**",cwd:process.cwd()})}(o),a=new Map;await i.initializePlugins(o.plugins||[]);for(const e of t)await n.processFile(e,o,s,a);for(const e of o.plugins||[])await(e.onEnd?.(a));return a};
1
+ "use strict";var e=require("glob"),r=require("./extractor.js"),s=require("../../utils/logger.js"),i=require("../plugin-manager.js"),t=require("../parsers/ast-visitors.js");exports.findKeys=async function(n,o=new s.ConsoleLogger){const a=await async function(r){return await e.glob(r.extract.input,{ignore:"node_modules/**",cwd:process.cwd()})}(n),u=new Map,c=new t.ASTVisitors(n,i.createPluginContext(u),o);await i.initializePlugins(n.plugins||[]);for(const e of a)await r.processFile(e,n,u,c,o);for(const e of n.plugins||[])await(e.onEnd?.(u));return{allKeys:u,objectKeys:c.objectKeys}};
@@ -1 +1 @@
1
- "use strict";var e=require("node:fs/promises"),t=require("node:path"),a=require("../../utils/nested-object.js"),s=require("../../utils/file-utils.js");function r(e){const t=`^${e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(t)}exports.getTranslations=async function(n,o){const c=o.extract.defaultNS??"translation",u=o.extract.keySeparator??".",l=(o.extract.preservePatterns??[]).map(r);o.extract.primaryLanguage||(o.extract.primaryLanguage=o.locales[0]||"en"),o.extract.secondaryLanguages||(o.extract.secondaryLanguages=o.locales.filter(e=>e!==o.extract.primaryLanguage));const i=new Map;for(const e of n.values()){const t=e.ns||c;i.has(t)||i.set(t,[]),i.get(t).push(e)}const g=[];for(const r of o.locales)for(const[n,c]of i.entries()){const i=s.getOutputPath(o.extract.output,r,n),p=t.resolve(process.cwd(),i);let f="",d={};try{f=await e.readFile(p,"utf-8"),d=JSON.parse(f)}catch(e){}const x={},y=a.getNestedKeys(d,u);for(const e of y)if(l.some(t=>t.test(e))){const t=a.getNestedValue(d,e,u);a.setNestedValue(x,e,t,u)}const m=!1===o.extract.sort?c:c.sort((e,t)=>e.key.localeCompare(t.key));for(const{key:e,defaultValue:t}of m){const s=a.getNestedValue(d,e,u)??(r===o.extract?.primaryLanguage?t:"");a.setNestedValue(x,e,s,u)}const N=o.extract.indentation??2,h=JSON.stringify(x,null,N);g.push({path:p,updated:h!==f,newTranslations:x,existingTranslations:d})}return g};
1
+ "use strict";var e=require("node:fs/promises"),t=require("node:path"),s=require("../../utils/nested-object.js"),a=require("../../utils/file-utils.js");function r(e){const t=`^${e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(t)}exports.getTranslations=async function(n,o,c){const u=c.extract.defaultNS??"translation",l=c.extract.keySeparator??".",i=[...c.extract.preservePatterns||[]];for(const e of o)i.push(`${e}.*`);const p=i.map(r);c.extract.primaryLanguage||(c.extract.primaryLanguage=c.locales[0]||"en"),c.extract.secondaryLanguages||(c.extract.secondaryLanguages=c.locales.filter(e=>e!==c.extract.primaryLanguage));const g=new Map;for(const e of n.values()){const t=e.ns||u;g.has(t)||g.set(t,[]),g.get(t).push(e)}const f=[];for(const r of c.locales)for(const[n,o]of g.entries()){const u=a.getOutputPath(c.extract.output,r,n),i=t.resolve(process.cwd(),u);let g="",d={};try{g=await e.readFile(i,"utf-8"),d=JSON.parse(g)}catch(e){}const x={},y=s.getNestedKeys(d,l);for(const e of y)if(p.some(t=>t.test(e))){const t=s.getNestedValue(d,e,l);s.setNestedValue(x,e,t,l)}const h=!1===c.extract.sort?o:o.sort((e,t)=>e.key.localeCompare(t.key));for(const{key:e,defaultValue:t}of h){const a=s.getNestedValue(d,e,l)??(r===c.extract?.primaryLanguage?t:"");s.setNestedValue(x,e,a,l)}const m=c.extract.indentation??2,N=JSON.stringify(x,null,m);f.push({path:i,updated:N!==g,newTranslations:x,existingTranslations:d})}return f};
@@ -1 +1 @@
1
- "use strict";var e=require("./jsx-parser.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;"ObjectExpression"===r?.type&&(s=this.getObjectPropValue(r,"keyPrefix")),this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=(this.config.extract.functions||[]).includes(t.value),i=this.getVarFromScope(t.value);if(!n&&!(void 0!==i))return;if(0===e.arguments.length)return;const r=e.arguments[0].expression;let s,a=null;if("StringLiteral"===r.type?a=r.value:"ArrowFunctionExpression"===r.type&&(a=this.extractKeyFromSelector(r)),!a)return;let o=a;const l=e.arguments.length>1?e.arguments[1].expression:void 0;"ObjectExpression"===l?.type&&(s=this.getObjectPropValue(l,"ns")),!s&&i?.defaultNs&&(s=i.defaultNs);const p=this.config.extract.nsSeparator??":",u=this.config.extract.contextSeparator??"_";if(!s&&p&&a.includes(p)){const e=a.split(p);s=e.shift(),a=e.join(p),o=a}if(s||(s=this.config.extract.defaultNS),i?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${i.keyPrefix}${e}${a}`}const c="StringLiteral"===r.type?this.getDefaultValue(e,a):a;if("ObjectExpression"===l?.type){const e=this.getObjectPropValue(l,"context");if(e)return void this.pluginContext.addKey({key:`${o}${u}${e}`,ns:s,defaultValue:c});if(void 0!==this.getObjectPropValue(l,"count"))return void this.handlePluralKeys(o,c,s)}this.pluginContext.addKey({key:o,ns:s,defaultValue:c})}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}getDefaultValue(e,t){if(e.arguments.length<=1)return t;const n=e.arguments[1].expression;return"StringLiteral"===n.type?n.value||t:"ObjectExpression"===n.type&&this.getObjectPropValue(n,"defaultValue")||t}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}n.ns||(n.ns=this.config.extract.defaultNS),n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}};
1
+ "use strict";var e=require("./jsx-parser.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||[]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;const s=e.arguments.length>1?e.arguments[1].expression:void 0,a=this.getDefaultValue(e,r[r.length-1]);for(let e=0;e<r.length;e++){let t,i=r[e];if("ObjectExpression"===s?.type){const e=this.getObjectPropValue(s,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const o=this.config.extract.nsSeparator??":";if(!t&&o&&i.includes(o)){const e=i.split(o);t=e.shift(),i=e.join(o)}t||(t=this.config.extract.defaultNS);let l=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";l=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1?a:i;let u=!1;if("ObjectExpression"===s?.type){const e=this.getObjectPropValue(s,"context");if("string"==typeof e&&e){const n=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${l}${n}${e}`,ns:t,defaultValue:p}),u=!0}u||void 0===this.getObjectPropValue(s,"count")||(this.handlePluralKeys(l,p,t),u=!0),u||!0!==this.getObjectPropValue(s,"returnObjects")||this.objectKeys.add(l)}u||this.pluginContext.addKey({key:l,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}getDefaultValue(e,t){if(e.arguments.length<=1)return t;const n=e.arguments[1].expression;if("StringLiteral"===n.type)return n.value||t;if("ObjectExpression"===n.type){const e=this.getObjectPropValue(n,"defaultValue");return"string"==typeof e?e||t:"number"==typeof e||"boolean"==typeof e?String(e):t}return t}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}n.ns||(n.ns=this.config.extract.defaultNS),n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}};
@@ -1 +1 @@
1
- "use strict";var e=require("chalk"),o=require("ora"),t=require("node:path"),a=require("node:fs/promises"),s=require("./extractor/core/key-finder.js"),n=require("./utils/nested-object.js"),r=require("./utils/file-utils.js");function l(o,t,a){const s=a>0?Math.round(t/a*100):100,n=c(s);console.log(`${e.bold(o)}: ${n} ${s}% (${t}/${a})`)}function c(o){const t=Math.round(o/100*20),a=20-t;return`[${e.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function i(){console.log(e.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${e.cyan("npx i18next-cli locize-migrate")} to get started.`)}exports.runStatus=async function(u,y={}){u.extract.primaryLanguage||(u.extract.primaryLanguage=u.locales[0]||"en"),u.extract.secondaryLanguages||(u.extract.secondaryLanguages=u.locales.filter(e=>e!==u?.extract?.primaryLanguage));const d=o("Analyzing project localization status...\n").start();try{const o=await async function(e){const o=await s.findKeys(e),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=e.extract,u=e.locales.filter(e=>e!==l),y=new Map;for(const e of o.values()){const o=e.ns||i;y.has(o)||y.set(o,[]),y.get(o).push(e)}const d={totalKeys:o.size,keysByNs:y,locales:new Map};for(const o of u){let s=0;const l=new Map;for(const[i,u]of y.entries()){const y=r.getOutputPath(e.extract.output,o,i);let d={};try{const e=await a.readFile(t.resolve(process.cwd(),y),"utf-8");d=JSON.parse(e)}catch{}let g=0;const f=u.map(({key:e})=>{const o=!!n.getNestedValue(d,e,c??".");return o&&g++,{key:e,isTranslated:o}});l.set(i,{totalKeys:u.length,translatedKeys:g,keyDetails:f}),s+=g}d.locales.set(o,{totalTranslated:s,namespaces:l})}return d}(u);d.succeed("Analysis complete."),function(o,t,a){a.detail?function(o,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(e.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(e.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=o.locales.get(a);if(!n)return void console.error(e.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(e.bold(`\nKey Status for "${e.cyan(a)}":`));const r=Array.from(o.keysByNs.values()).flat().length;l("Overall",n.totalTranslated,r);const c=s?[s]:Array.from(n.namespaces.keys()).sort();for(const o of c){const t=n.namespaces.get(o);t&&(console.log(e.cyan.bold(`\nNamespace: ${o}`)),l("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:o,isTranslated:t})=>{const a=t?e.green("✓"):e.red("✗");console.log(` ${a} ${o}`)}))}const u=r-n.totalTranslated;u>0?console.log(e.yellow.bold(`\nSummary: Found ${u} missing translations for "${a}".`)):console.log(e.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));i()}(o,t,a.detail,a.namespace):a.namespace?function(o,t,a){const s=o.keysByNs.get(a);if(!s)return void console.error(e.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(e.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[e,t]of o.locales.entries()){const o=t.namespaces.get(a);if(o){const t=o.totalKeys>0?Math.round(o.translatedKeys/o.totalKeys*100):100,a=c(t);console.log(`- ${e}: ${a} ${t}% (${o.translatedKeys}/${o.totalKeys} keys)`)}}i()}(o,0,a.namespace):function(o,t){const{primaryLanguage:a}=t.extract;console.log(e.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${e.bold(o.totalKeys)}`),console.log(`📚 Namespaces Found: ${e.bold(o.keysByNs.size)}`),console.log(`🌍 Locales: ${e.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${e.bold(a)}`),console.log("\nTranslation Progress:");for(const[e,t]of o.locales.entries()){const a=o.totalKeys>0?Math.round(t.totalTranslated/o.totalKeys*100):100,s=c(a);console.log(`- ${e}: ${s} ${a}% (${t.totalTranslated}/${o.totalKeys} keys)`)}i()}(o,t)}(o,u,y)}catch(e){d.fail("Failed to generate status report."),console.error(e)}};
1
+ "use strict";var e=require("chalk"),o=require("ora"),t=require("node:path"),a=require("node:fs/promises"),s=require("./extractor/core/key-finder.js"),n=require("./utils/nested-object.js"),r=require("./utils/file-utils.js");function l(o,t,a){const s=a>0?Math.round(t/a*100):100,n=c(s);console.log(`${e.bold(o)}: ${n} ${s}% (${t}/${a})`)}function c(o){const t=Math.round(o/100*20),a=20-t;return`[${e.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function i(){console.log(e.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${e.cyan("npx i18next-cli locize-migrate")} to get started.`)}exports.runStatus=async function(u,y={}){u.extract.primaryLanguage||(u.extract.primaryLanguage=u.locales[0]||"en"),u.extract.secondaryLanguages||(u.extract.secondaryLanguages=u.locales.filter(e=>e!==u?.extract?.primaryLanguage));const d=o("Analyzing project localization status...\n").start();try{const o=await async function(e){const{allKeys:o}=await s.findKeys(e),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=e.extract,u=e.locales.filter(e=>e!==l),y=new Map;for(const e of o.values()){const o=e.ns||i;y.has(o)||y.set(o,[]),y.get(o).push(e)}const d={totalKeys:o.size,keysByNs:y,locales:new Map};for(const o of u){let s=0;const l=new Map;for(const[i,u]of y.entries()){const y=r.getOutputPath(e.extract.output,o,i);let d={};try{const e=await a.readFile(t.resolve(process.cwd(),y),"utf-8");d=JSON.parse(e)}catch{}let g=0;const f=u.map(({key:e})=>{const o=!!n.getNestedValue(d,e,c??".");return o&&g++,{key:e,isTranslated:o}});l.set(i,{totalKeys:u.length,translatedKeys:g,keyDetails:f}),s+=g}d.locales.set(o,{totalTranslated:s,namespaces:l})}return d}(u);d.succeed("Analysis complete."),function(o,t,a){a.detail?function(o,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(e.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(e.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=o.locales.get(a);if(!n)return void console.error(e.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(e.bold(`\nKey Status for "${e.cyan(a)}":`));const r=Array.from(o.keysByNs.values()).flat().length;l("Overall",n.totalTranslated,r);const c=s?[s]:Array.from(n.namespaces.keys()).sort();for(const o of c){const t=n.namespaces.get(o);t&&(console.log(e.cyan.bold(`\nNamespace: ${o}`)),l("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:o,isTranslated:t})=>{const a=t?e.green("✓"):e.red("✗");console.log(` ${a} ${o}`)}))}const u=r-n.totalTranslated;u>0?console.log(e.yellow.bold(`\nSummary: Found ${u} missing translations for "${a}".`)):console.log(e.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));i()}(o,t,a.detail,a.namespace):a.namespace?function(o,t,a){const s=o.keysByNs.get(a);if(!s)return void console.error(e.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(e.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[e,t]of o.locales.entries()){const o=t.namespaces.get(a);if(o){const t=o.totalKeys>0?Math.round(o.translatedKeys/o.totalKeys*100):100,a=c(t);console.log(`- ${e}: ${a} ${t}% (${o.translatedKeys}/${o.totalKeys} keys)`)}}i()}(o,0,a.namespace):function(o,t){const{primaryLanguage:a}=t.extract;console.log(e.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${e.bold(o.totalKeys)}`),console.log(`📚 Namespaces Found: ${e.bold(o.keysByNs.size)}`),console.log(`🌍 Locales: ${e.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${e.bold(a)}`),console.log("\nTranslation Progress:");for(const[e,t]of o.locales.entries()){const a=o.totalKeys>0?Math.round(t.totalTranslated/o.totalKeys*100):100,s=c(a);console.log(`- ${e}: ${s} ${a}% (${t.totalTranslated}/${o.totalKeys} keys)`)}i()}(o,t)}(o,u,y)}catch(e){d.fail("Failed to generate status report."),console.error(e)}};
package/dist/esm/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as o}from"commander";import e from"chokidar";import{glob as t}from"glob";import i from"chalk";import{ensureConfig as n,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:fs/promises";import"node:path";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as f}from"./status.js";import{runLocizeSync as g,runLocizeDownload as u,runLocizeMigrate as y}from"./locize.js";const w=new o;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.6"),w.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async o=>{const a=await n(),c=async()=>{const e=await r(a);o.ci&&e&&(console.error(i.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(i.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${i.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),o.watch){console.log("\nWatching for changes...");e.watch(await t(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),c()})}}),w.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,e)=>{let t=await a();if(!t){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),t=o}await f(t,{detail:o,namespace:e.namespace})}),w.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const i=await n(),a=()=>s(i);if(await a(),o.watch){console.log("\nWatching for changes...");e.watch(await t(i.types?.input||[]),{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=await n();await l(o)}),w.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await m()}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let o=await a();if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await c();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=e}await d(o)}),w.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const e=await n();await g(e,o)}),w.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const e=await n();await u(e,o)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const e=await n();await y(e,o)}),w.parse(process.argv);
2
+ import{Command as o}from"commander";import e from"chokidar";import{glob as t}from"glob";import i from"chalk";import{ensureConfig as n,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:fs/promises";import"node:path";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as f}from"./status.js";import{runLocizeSync as g,runLocizeDownload as u,runLocizeMigrate as y}from"./locize.js";const w=new o;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("0.9.8"),w.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").action(async o=>{const a=await n(),c=async()=>{const e=await r(a);o.ci&&e&&(console.error(i.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(i.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${i.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),o.watch){console.log("\nWatching for changes...");e.watch(await t(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),c()})}}),w.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,e)=>{let t=await a();if(!t){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),t=o}await f(t,{detail:o,namespace:e.namespace})}),w.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const i=await n(),a=()=>s(i);if(await a(),o.watch){console.log("\nWatching for changes...");e.watch(await t(i.types?.input||[]),{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=await n();await l(o)}),w.command("migrate-config").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async()=>{await m()}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").action(async()=>{let o=await a();if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await c();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i1e-toolkit init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=e}await d(o)}),w.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const e=await n();await g(e,o)}),w.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const e=await n();await u(e,o)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const e=await n();await y(e,o)}),w.parse(process.argv);
@@ -1 +1 @@
1
- import{resolve as o}from"node:path";import{pathToFileURL as r}from"node:url";import{access as t}from"node:fs/promises";import{createJiti as e}from"jiti";import n from"inquirer";import i from"chalk";import{runInit as a}from"./init.js";const c=["i18next.config.ts","i18next.config.js","i18next.config.mjs","i18next.config.cjs"];function l(o){return o}async function s(){const n=await async function(){for(const r of c){const e=o(process.cwd(),r);try{return await t(e),e}catch{}}return null}();if(!n)return null;try{let o;if(n.endsWith(".ts")){const r=e(import.meta.url),t=await r.import(n,{default:!0});o=t}else{const t=r(n).href,e=await import(`${t}?t=${Date.now()}`);o=e.default}return o?(o.extract||={},o.extract.primaryLanguage||=o.locales[0]||"en",o.extract.secondaryLanguages||=o.locales.filter(r=>r!==o.extract.primaryLanguage),o):(console.error(`Error: No default export found in ${n}`),null)}catch(o){return console.error(`Error loading configuration from ${n}`),console.error(o),null}}async function f(){let o=await s();if(o)return o;const{shouldInit:r}=await n.prompt([{type:"confirm",name:"shouldInit",message:i.yellow("Configuration file not found. Would you like to create one now?"),default:!0}]);if(r){if(await a(),console.log(i.green("Configuration created. Resuming command...")),o=await s(),o)return o;console.error(i.red("Error: Failed to load configuration after creation. Please try running the command again.")),process.exit(1)}else console.log("Operation cancelled. Please create a configuration file to proceed."),process.exit(0)}export{l as defineConfig,f as ensureConfig,s as loadConfig};
1
+ import{resolve as r}from"node:path";import{pathToFileURL as t}from"node:url";import{access as o}from"node:fs/promises";import{createJiti as e}from"jiti";import n from"inquirer";import i from"chalk";import{runInit as a}from"./init.js";import{ConsoleLogger as c}from"./utils/logger.js";const f=["i18next.config.ts","i18next.config.js","i18next.config.mjs","i18next.config.cjs"];function l(r){return r}async function s(n=new c){const i=await async function(){for(const t of f){const e=r(process.cwd(),t);try{return await o(e),e}catch{}}return null}();if(!i)return null;try{let r;if(i.endsWith(".ts")){const t=e(import.meta.url),o=await t.import(i,{default:!0});r=o}else{const o=t(i).href,e=await import(`${o}?t=${Date.now()}`);r=e.default}return r?(r.extract||={},r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(t=>t!==r.extract.primaryLanguage),r):(n.error(`Error: No default export found in ${i}`),null)}catch(r){return n.error(`Error loading configuration from ${i}`),n.error(r),null}}async function u(r=new c){let t=await s();if(t)return t;const{shouldInit:o}=await n.prompt([{type:"confirm",name:"shouldInit",message:i.yellow("Configuration file not found. Would you like to create one now?"),default:!0}]);if(o){if(await a(),r.info(i.green("Configuration created. Resuming command...")),t=await s(),t)return t;r.error(i.red("Error: Failed to load configuration after creation. Please try running the command again.")),process.exit(1)}else r.info("Operation cancelled. Please create a configuration file to proceed."),process.exit(0)}export{l as defineConfig,u as ensureConfig,s as loadConfig};
@@ -1 +1 @@
1
- import t from"ora";import a from"chalk";import{parse as r}from"@swc/core";import{mkdir as o,writeFile as e,readFile as n}from"node:fs/promises";import{dirname as s}from"node:path";import{findKeys as i}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as f,ExtractorError as p}from"../../utils/validation.js";import{createPluginContext as m}from"../plugin-manager.js";import{extractKeysFromComments as l}from"../parsers/comment-parser.js";import{ASTVisitors as u}from"../parsers/ast-visitors.js";import{ConsoleLogger as g}from"../../utils/logger.js";async function y(r,n=new g){r.extract.primaryLanguage||(r.extract.primaryLanguage=r.locales[0]||"en"),r.extract.secondaryLanguages||(r.extract.secondaryLanguages=r.locales.filter(t=>t!==r?.extract?.primaryLanguage)),f(r);const p=t("Running i18next key extractor...\n").start();try{const t=await i(r,n);p.text=`Found ${t.size} unique keys. Updating translation files...`;const f=await c(t,r);let m=!1;for(const t of f)t.updated&&(m=!0,await o(s(t.path),{recursive:!0}),await e(t.path,JSON.stringify(t.newTranslations,null,2)),n.info(a.green(`Updated: ${t.path}`)));return p.succeed(a.bold("Extraction complete!")),m}catch(t){throw p.fail(a.red("Extraction failed.")),t}}async function d(t,a,o,e){try{let s=await n(t,"utf-8");for(const r of a.plugins||[])s=await(r.onLoad?.(s,t))??s;const i=await r(s,{syntax:"typescript",tsx:!0,comments:!0}),c=m(e);l(s,a.extract.functions||["t"],c,a);new u(a,c,o).visit(i),(a.plugins||[]).length>0&&x(i,a.plugins||[],c)}catch(a){throw new p("Failed to process file",t,a)}}function x(t,a,r){if(t&&"object"==typeof t){for(const o of a)try{o.onVisitNode?.(t,r)}catch(t){console.warn(`Plugin ${o.name} onVisitNode failed:`,t)}for(const o of Object.keys(t)){const e=t[o];if(Array.isArray(e))for(const t of e)t&&"object"==typeof t&&x(t,a,r);else e&&"object"==typeof e&&x(e,a,r)}}}async function w(t){t.extract.primaryLanguage||(t.extract.primaryLanguage=t.locales[0]),t.extract.secondaryLanguages||(t.extract.secondaryLanguages=t.locales.filter(a=>a!==t?.extract?.primaryLanguage)),t.extract.functions||(t.extract.functions=["t"]),t.extract.transComponents||(t.extract.transComponents=["Trans"]);const a=await i(t);return c(a,t)}export{w as extract,d as processFile,y as runExtractor};
1
+ import t from"ora";import a from"chalk";import{parse as e}from"@swc/core";import{mkdir as r,writeFile as o,readFile as n}from"node:fs/promises";import{dirname as s}from"node:path";import{findKeys as i}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as f,ExtractorError as l}from"../../utils/validation.js";import{createPluginContext as p}from"../plugin-manager.js";import{extractKeysFromComments as m}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";async function g(e,n=new u){e.extract.primaryLanguage||(e.extract.primaryLanguage=e.locales[0]||"en"),e.extract.secondaryLanguages||(e.extract.secondaryLanguages=e.locales.filter(t=>t!==e?.extract?.primaryLanguage)),f(e);const l=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:f}=await i(e,n);l.text=`Found ${t.size} unique keys. Updating translation files...`;const p=await c(t,f,e);let m=!1;for(const t of p)t.updated&&(m=!0,await r(s(t.path),{recursive:!0}),await o(t.path,JSON.stringify(t.newTranslations,null,2)),n.info(a.green(`Updated: ${t.path}`)));return l.succeed(a.bold("Extraction complete!")),m}catch(t){throw l.fail(a.red("Extraction failed.")),t}}async function y(t,a,r,o,s=new u){try{let i=await n(t,"utf-8");for(const e of a.plugins||[])i=await(e.onLoad?.(i,t))??i;const c=await e(i,{syntax:"typescript",tsx:!0,comments:!0}),f=p(r);m(i,a.extract.functions||["t"],f,a),o.visit(c),(a.plugins||[]).length>0&&d(c,a.plugins||[],f,s)}catch(a){throw new l("Failed to process file",t,a)}}function d(t,a,e,r=new u){if(t&&"object"==typeof t){for(const o of a)try{o.onVisitNode?.(t,e)}catch(t){r.warn(`Plugin ${o.name} onVisitNode failed:`,t)}for(const o of Object.keys(t)){const n=t[o];if(Array.isArray(n))for(const t of n)t&&"object"==typeof t&&d(t,a,e,r);else n&&"object"==typeof n&&d(n,a,e,r)}}}async function x(t){t.extract.primaryLanguage||(t.extract.primaryLanguage=t.locales[0]),t.extract.secondaryLanguages||(t.extract.secondaryLanguages=t.locales.filter(a=>a!==t?.extract?.primaryLanguage)),t.extract.functions||(t.extract.functions=["t"]),t.extract.transComponents||(t.extract.transComponents=["Trans"]);const{allKeys:a,objectKeys:e}=await i(t);return c(a,e,t)}export{x as extract,y as processFile,g as runExtractor};
@@ -1 +1 @@
1
- import{glob as o}from"glob";import{processFile as t}from"./extractor.js";import{ConsoleLogger as n}from"../../utils/logger.js";import{initializePlugins as r}from"../plugin-manager.js";async function a(a,i=new n){const s=await async function(t){return await o(t.extract.input,{ignore:"node_modules/**",cwd:process.cwd()})}(a),e=new Map;await r(a.plugins||[]);for(const o of s)await t(o,a,i,e);for(const o of a.plugins||[])await(o.onEnd?.(e));return e}export{a as findKeys};
1
+ import{glob as o}from"glob";import{processFile as t}from"./extractor.js";import{ConsoleLogger as r}from"../../utils/logger.js";import{initializePlugins as n,createPluginContext as s}from"../plugin-manager.js";import{ASTVisitors as a}from"../parsers/ast-visitors.js";async function e(e,i=new r){const c=await async function(t){return await o(t.extract.input,{ignore:"node_modules/**",cwd:process.cwd()})}(e),p=new Map,m=new a(e,s(p),i);await n(e.plugins||[]);for(const o of c)await t(o,e,p,m,i);for(const o of e.plugins||[])await(o.onEnd?.(p));return{allKeys:p,objectKeys:m.objectKeys}}export{e as findKeys};
@@ -1 +1 @@
1
- import{readFile as t}from"node:fs/promises";import{resolve as e}from"node:path";import{getNestedKeys as a,getNestedValue as r,setNestedValue as o}from"../../utils/nested-object.js";import{getOutputPath as s}from"../../utils/file-utils.js";function n(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}async function c(c,i){const p=i.extract.defaultNS??"translation",l=i.extract.keySeparator??".",f=(i.extract.preservePatterns??[]).map(n);i.extract.primaryLanguage||(i.extract.primaryLanguage=i.locales[0]||"en"),i.extract.secondaryLanguages||(i.extract.secondaryLanguages=i.locales.filter(t=>t!==i.extract.primaryLanguage));const u=new Map;for(const t of c.values()){const e=t.ns||p;u.has(e)||u.set(e,[]),u.get(e).push(t)}const g=[];for(const n of i.locales)for(const[c,p]of u.entries()){const u=s(i.extract.output,n,c),m=e(process.cwd(),u);let x="",y={};try{x=await t(m,"utf-8"),y=JSON.parse(x)}catch(t){}const d={},h=a(y,l);for(const t of h)if(f.some(e=>e.test(t))){const e=r(y,t,l);o(d,t,e,l)}const L=!1===i.extract.sort?p:p.sort((t,e)=>t.key.localeCompare(e.key));for(const{key:t,defaultValue:e}of L){const a=r(y,t,l)??(n===i.extract?.primaryLanguage?e:"");o(d,t,a,l)}const w=i.extract.indentation??2,k=JSON.stringify(d,null,w);g.push({path:m,updated:k!==x,newTranslations:d,existingTranslations:y})}return g}export{c as getTranslations};
1
+ import{readFile as t}from"node:fs/promises";import{resolve as e}from"node:path";import{getNestedKeys as a,getNestedValue as o,setNestedValue as r}from"../../utils/nested-object.js";import{getOutputPath as s}from"../../utils/file-utils.js";function n(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}async function c(c,i,p){const f=p.extract.defaultNS??"translation",l=p.extract.keySeparator??".",u=[...p.extract.preservePatterns||[]];for(const t of i)u.push(`${t}.*`);const g=u.map(n);p.extract.primaryLanguage||(p.extract.primaryLanguage=p.locales[0]||"en"),p.extract.secondaryLanguages||(p.extract.secondaryLanguages=p.locales.filter(t=>t!==p.extract.primaryLanguage));const m=new Map;for(const t of c.values()){const e=t.ns||f;m.has(e)||m.set(e,[]),m.get(e).push(t)}const x=[];for(const n of p.locales)for(const[c,i]of m.entries()){const f=s(p.extract.output,n,c),u=e(process.cwd(),f);let m="",y={};try{m=await t(u,"utf-8"),y=JSON.parse(m)}catch(t){}const d={},h=a(y,l);for(const t of h)if(g.some(e=>e.test(t))){const e=o(y,t,l);r(d,t,e,l)}const L=!1===p.extract.sort?i:i.sort((t,e)=>t.key.localeCompare(e.key));for(const{key:t,defaultValue:e}of L){const a=o(y,t,l)??(n===p.extract?.primaryLanguage?e:"");r(d,t,a,l)}const w=p.extract.indentation??2,$=JSON.stringify(d,null,w);x.push({path:u,updated:$!==m,newTranslations:d,existingTranslations:y})}return x}export{c as getTranslations};
@@ -1 +1 @@
1
- import{extractFromTransComponent as e}from"./jsx-parser.js";class t{pluginContext;config;logger;scopeStack=[];constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;"ObjectExpression"===r?.type&&(s=this.getObjectPropValue(r,"keyPrefix")),this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=(this.config.extract.functions||[]).includes(t.value),i=this.getVarFromScope(t.value);if(!n&&!(void 0!==i))return;if(0===e.arguments.length)return;const r=e.arguments[0].expression;let s,a=null;if("StringLiteral"===r.type?a=r.value:"ArrowFunctionExpression"===r.type&&(a=this.extractKeyFromSelector(r)),!a)return;let o=a;const l=e.arguments.length>1?e.arguments[1].expression:void 0;"ObjectExpression"===l?.type&&(s=this.getObjectPropValue(l,"ns")),!s&&i?.defaultNs&&(s=i.defaultNs);const p=this.config.extract.nsSeparator??":",u=this.config.extract.contextSeparator??"_";if(!s&&p&&a.includes(p)){const e=a.split(p);s=e.shift(),a=e.join(p),o=a}if(s||(s=this.config.extract.defaultNS),i?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${i.keyPrefix}${e}${a}`}const c="StringLiteral"===r.type?this.getDefaultValue(e,a):a;if("ObjectExpression"===l?.type){const e=this.getObjectPropValue(l,"context");if(e)return void this.pluginContext.addKey({key:`${o}${u}${e}`,ns:s,defaultValue:c});if(void 0!==this.getObjectPropValue(l,"count"))return void this.handlePluralKeys(o,c,s)}this.pluginContext.addKey({key:o,ns:s,defaultValue:c})}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}getDefaultValue(e,t){if(e.arguments.length<=1)return t;const n=e.arguments[1].expression;return"StringLiteral"===n.type?n.value||t:"ObjectExpression"===n.type&&this.getObjectPropValue(n,"defaultValue")||t}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}n.ns||(n.ns=this.config.extract.defaultNS),n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}}export{t as ASTVisitors};
1
+ import{extractFromTransComponent as e}from"./jsx-parser.js";class t{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&e.type&&this.walk(e);else n&&n.type&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){if("CallExpression"!==e.init?.type)return;const t=e.init.callee;"Identifier"===t.type&&(this.config.extract.useTranslationNames||["useTranslation","getT","useT"]).indexOf(t.value)>-1?this.handleUseTranslationDeclarator(e):"MemberExpression"===t.type&&"Identifier"===t.property.type&&"getFixedT"===t.property.value&&this.handleGetFixedTDeclarator(e)}handleUseTranslationDeclarator(e){if(!e.init||"CallExpression"!==e.init.type)return;let t;if("ArrayPattern"===e.id.type){const n=e.id.elements[0];"Identifier"===n?.type&&(t=n.value)}if("ObjectPattern"===e.id.type)for(const n of e.id.properties){if("AssignmentPatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value){t="t";break}if("KeyValuePatternProperty"===n.type&&"Identifier"===n.key.type&&"t"===n.key.value&&"Identifier"===n.value.type){t=n.value.value;break}}if(!t)return;const n=e.init.arguments?.[0]?.expression;let i;"StringLiteral"===n?.type?i=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(i=n.elements[0].expression.value);const r=e.init.arguments?.[1]?.expression;let s;if("ObjectExpression"===r?.type){const e=this.getObjectPropValue(r,"keyPrefix");s="string"==typeof e?e:void 0}this.setVarInScope(t,{defaultNs:i,keyPrefix:s})}handleGetFixedTDeclarator(e){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const t=e.id.value,n=e.init.arguments,i=n[1]?.expression,r=n[2]?.expression,s="StringLiteral"===i?.type?i.value:void 0,a="StringLiteral"===r?.type?r.value:void 0;(s||a)&&this.setVarInScope(t,{defaultNs:s,keyPrefix:a})}handleCallExpression(e){const t=e.callee;if("Identifier"!==t.type)return;const n=this.getVarFromScope(t.value);if(!((this.config.extract.functions||[]).includes(t.value)||void 0!==n)||0===e.arguments.length)return;const i=e.arguments[0].expression,r=[];if("StringLiteral"===i.type)r.push(i.value);else if("ArrowFunctionExpression"===i.type){const e=this.extractKeyFromSelector(i);e&&r.push(e)}else if("ArrayExpression"===i.type)for(const e of i.elements)"StringLiteral"===e?.expression.type&&r.push(e.expression.value);if(0===r.length)return;const s=e.arguments.length>1?e.arguments[1].expression:void 0,a=this.getDefaultValue(e,r[r.length-1]);for(let e=0;e<r.length;e++){let t,i=r[e];if("ObjectExpression"===s?.type){const e=this.getObjectPropValue(s,"ns");"string"==typeof e&&(t=e)}!t&&n?.defaultNs&&(t=n.defaultNs);const o=this.config.extract.nsSeparator??":";if(!t&&o&&i.includes(o)){const e=i.split(o);t=e.shift(),i=e.join(o)}t||(t=this.config.extract.defaultNS);let l=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";l=`${n.keyPrefix}${e}${i}`}const p=e===r.length-1?a:i;let u=!1;if("ObjectExpression"===s?.type){const e=this.getObjectPropValue(s,"context");if("string"==typeof e&&e){const n=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${l}${n}${e}`,ns:t,defaultValue:p}),u=!0}u||void 0===this.getObjectPropValue(s,"count")||(this.handlePluralKeys(l,p,t),u=!0),u||!0!==this.getObjectPropValue(s,"returnObjects")||this.objectKeys.add(l)}u||this.pluginContext.addKey({key:l,ns:t,defaultValue:p})}}handlePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,defaultValue:t,ns:n})}}getDefaultValue(e,t){if(e.arguments.length<=1)return t;const n=e.arguments[1].expression;if("StringLiteral"===n.type)return n.value||t;if("ObjectExpression"===n.type){const e=this.getObjectPropValue(n,"defaultValue");return"string"==typeof e?e||t:"number"==typeof e||"boolean"==typeof e?String(e):t}return t}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}n.ns||(n.ns=this.config.extract.defaultNS),n.hasCount?this.handlePluralKeys(n.key,n.defaultValue,n.ns):this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}getObjectPropValue(e,t){const n=e.properties.find(e=>"KeyValueProperty"===e.type&&("Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t));if("KeyValueProperty"===n?.type){const e=n.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}}export{t as ASTVisitors};
@@ -1 +1 @@
1
- import o from"chalk";import e from"ora";import{resolve as t}from"node:path";import{readFile as a}from"node:fs/promises";import{findKeys as s}from"./extractor/core/key-finder.js";import{getNestedValue as n}from"./utils/nested-object.js";import{getOutputPath as r}from"./utils/file-utils.js";async function l(l,d={}){l.extract.primaryLanguage||(l.extract.primaryLanguage=l.locales[0]||"en"),l.extract.secondaryLanguages||(l.extract.secondaryLanguages=l.locales.filter(o=>o!==l?.extract?.primaryLanguage));const g=e("Analyzing project localization status...\n").start();try{const e=await async function(o){const e=await s(o),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=o.extract,y=o.locales.filter(o=>o!==l),d=new Map;for(const o of e.values()){const e=o.ns||i;d.has(e)||d.set(e,[]),d.get(e).push(o)}const g={totalKeys:e.size,keysByNs:d,locales:new Map};for(const e of y){let s=0;const l=new Map;for(const[i,y]of d.entries()){const d=r(o.extract.output,e,i);let g={};try{const o=await a(t(process.cwd(),d),"utf-8");g=JSON.parse(o)}catch{}let u=0;const f=y.map(({key:o})=>{const e=!!n(g,o,c??".");return e&&u++,{key:o,isTranslated:e}});l.set(i,{totalKeys:y.length,translatedKeys:u,keyDetails:f}),s+=u}g.locales.set(e,{totalTranslated:s,namespaces:l})}return g}(l);g.succeed("Analysis complete."),function(e,t,a){a.detail?function(e,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(o.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(o.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=e.locales.get(a);if(!n)return void console.error(o.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(o.bold(`\nKey Status for "${o.cyan(a)}":`));const r=Array.from(e.keysByNs.values()).flat().length;c("Overall",n.totalTranslated,r);const l=s?[s]:Array.from(n.namespaces.keys()).sort();for(const e of l){const t=n.namespaces.get(e);t&&(console.log(o.cyan.bold(`\nNamespace: ${e}`)),c("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:e,isTranslated:t})=>{const a=t?o.green("✓"):o.red("✗");console.log(` ${a} ${e}`)}))}const i=r-n.totalTranslated;i>0?console.log(o.yellow.bold(`\nSummary: Found ${i} missing translations for "${a}".`)):console.log(o.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));y()}(e,t,a.detail,a.namespace):a.namespace?function(e,t,a){const s=e.keysByNs.get(a);if(!s)return void console.error(o.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(o.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[o,t]of e.locales.entries()){const e=t.namespaces.get(a);if(e){const t=e.totalKeys>0?Math.round(e.translatedKeys/e.totalKeys*100):100,a=i(t);console.log(`- ${o}: ${a} ${t}% (${e.translatedKeys}/${e.totalKeys} keys)`)}}y()}(e,0,a.namespace):function(e,t){const{primaryLanguage:a}=t.extract;console.log(o.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${o.bold(e.totalKeys)}`),console.log(`📚 Namespaces Found: ${o.bold(e.keysByNs.size)}`),console.log(`🌍 Locales: ${o.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${o.bold(a)}`),console.log("\nTranslation Progress:");for(const[o,t]of e.locales.entries()){const a=e.totalKeys>0?Math.round(t.totalTranslated/e.totalKeys*100):100,s=i(a);console.log(`- ${o}: ${s} ${a}% (${t.totalTranslated}/${e.totalKeys} keys)`)}y()}(e,t)}(e,l,d)}catch(o){g.fail("Failed to generate status report."),console.error(o)}}function c(e,t,a){const s=a>0?Math.round(t/a*100):100,n=i(s);console.log(`${o.bold(e)}: ${n} ${s}% (${t}/${a})`)}function i(e){const t=Math.round(e/100*20),a=20-t;return`[${o.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function y(){console.log(o.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${o.cyan("npx i18next-cli locize-migrate")} to get started.`)}export{l as runStatus};
1
+ import o from"chalk";import e from"ora";import{resolve as t}from"node:path";import{readFile as a}from"node:fs/promises";import{findKeys as s}from"./extractor/core/key-finder.js";import{getNestedValue as n}from"./utils/nested-object.js";import{getOutputPath as r}from"./utils/file-utils.js";async function l(l,d={}){l.extract.primaryLanguage||(l.extract.primaryLanguage=l.locales[0]||"en"),l.extract.secondaryLanguages||(l.extract.secondaryLanguages=l.locales.filter(o=>o!==l?.extract?.primaryLanguage));const g=e("Analyzing project localization status...\n").start();try{const e=await async function(o){const{allKeys:e}=await s(o),{primaryLanguage:l,keySeparator:c=".",defaultNS:i="translation"}=o.extract,y=o.locales.filter(o=>o!==l),d=new Map;for(const o of e.values()){const e=o.ns||i;d.has(e)||d.set(e,[]),d.get(e).push(o)}const g={totalKeys:e.size,keysByNs:d,locales:new Map};for(const e of y){let s=0;const l=new Map;for(const[i,y]of d.entries()){const d=r(o.extract.output,e,i);let g={};try{const o=await a(t(process.cwd(),d),"utf-8");g=JSON.parse(o)}catch{}let u=0;const f=y.map(({key:o})=>{const e=!!n(g,o,c??".");return e&&u++,{key:o,isTranslated:e}});l.set(i,{totalKeys:y.length,translatedKeys:u,keyDetails:f}),s+=u}g.locales.set(e,{totalTranslated:s,namespaces:l})}return g}(l);g.succeed("Analysis complete."),function(e,t,a){a.detail?function(e,t,a,s){if(a===t.extract.primaryLanguage)return void console.log(o.yellow(`Locale "${a}" is the primary language. All keys are considered present.`));if(!t.locales.includes(a))return void console.error(o.red(`Error: Locale "${a}" is not defined in your configuration.`));const n=e.locales.get(a);if(!n)return void console.error(o.red(`Error: Locale "${a}" is not a valid secondary language.`));console.log(o.bold(`\nKey Status for "${o.cyan(a)}":`));const r=Array.from(e.keysByNs.values()).flat().length;c("Overall",n.totalTranslated,r);const l=s?[s]:Array.from(n.namespaces.keys()).sort();for(const e of l){const t=n.namespaces.get(e);t&&(console.log(o.cyan.bold(`\nNamespace: ${e}`)),c("Namespace Progress",t.translatedKeys,t.totalKeys),t.keyDetails.forEach(({key:e,isTranslated:t})=>{const a=t?o.green("✓"):o.red("✗");console.log(` ${a} ${e}`)}))}const i=r-n.totalTranslated;i>0?console.log(o.yellow.bold(`\nSummary: Found ${i} missing translations for "${a}".`)):console.log(o.green.bold(`\nSummary: 🎉 All keys are translated for "${a}".`));y()}(e,t,a.detail,a.namespace):a.namespace?function(e,t,a){const s=e.keysByNs.get(a);if(!s)return void console.error(o.red(`Error: Namespace "${a}" was not found in your source code.`));console.log(o.cyan.bold(`\nStatus for Namespace: "${a}"`)),console.log("------------------------");for(const[o,t]of e.locales.entries()){const e=t.namespaces.get(a);if(e){const t=e.totalKeys>0?Math.round(e.translatedKeys/e.totalKeys*100):100,a=i(t);console.log(`- ${o}: ${a} ${t}% (${e.translatedKeys}/${e.totalKeys} keys)`)}}y()}(e,0,a.namespace):function(e,t){const{primaryLanguage:a}=t.extract;console.log(o.cyan.bold("\ni18next Project Status")),console.log("------------------------"),console.log(`🔑 Keys Found: ${o.bold(e.totalKeys)}`),console.log(`📚 Namespaces Found: ${o.bold(e.keysByNs.size)}`),console.log(`🌍 Locales: ${o.bold(t.locales.join(", "))}`),console.log(`✅ Primary Language: ${o.bold(a)}`),console.log("\nTranslation Progress:");for(const[o,t]of e.locales.entries()){const a=e.totalKeys>0?Math.round(t.totalTranslated/e.totalKeys*100):100,s=i(a);console.log(`- ${o}: ${s} ${a}% (${t.totalTranslated}/${e.totalKeys} keys)`)}y()}(e,t)}(e,l,d)}catch(o){g.fail("Failed to generate status report."),console.error(o)}}function c(e,t,a){const s=a>0?Math.round(t/a*100):100,n=i(s);console.log(`${o.bold(e)}: ${n} ${s}% (${t}/${a})`)}function i(e){const t=Math.round(e/100*20),a=20-t;return`[${o.green("".padStart(t,"■"))}${"".padStart(a,"□")}]`}function y(){console.log(o.yellow.bold("\n✨ Take your localization to the next level!")),console.log("Manage translations with your team in the cloud with locize => https://www.locize.com/docs/getting-started"),console.log(`Run ${o.cyan("npx i18next-cli locize-migrate")} to get started.`)}export{l as runStatus};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -21,7 +21,7 @@ const program = new Command()
21
21
  program
22
22
  .name('i18next-cli')
23
23
  .description('A unified, high-performance i18next CLI.')
24
- .version('0.9.6')
24
+ .version('0.9.8')
25
25
 
26
26
  program
27
27
  .command('extract')
package/src/config.ts CHANGED
@@ -4,8 +4,9 @@ import { access } from 'node:fs/promises'
4
4
  import { createJiti } from 'jiti'
5
5
  import inquirer from 'inquirer'
6
6
  import chalk from 'chalk'
7
- import type { I18nextToolkitConfig } from './types'
7
+ import type { I18nextToolkitConfig, Logger } from './types'
8
8
  import { runInit } from './init'
9
+ import { ConsoleLogger } from './utils/logger'
9
10
 
10
11
  /**
11
12
  * List of supported configuration file names in order of precedence
@@ -78,7 +79,7 @@ async function findConfigFile (): Promise<string | null> {
78
79
  * }
79
80
  * ```
80
81
  */
81
- export async function loadConfig (): Promise<I18nextToolkitConfig | null> {
82
+ export async function loadConfig (logger: Logger = new ConsoleLogger()): Promise<I18nextToolkitConfig | null> {
82
83
  const configPath = await findConfigFile()
83
84
 
84
85
  if (!configPath) {
@@ -101,7 +102,7 @@ export async function loadConfig (): Promise<I18nextToolkitConfig | null> {
101
102
  }
102
103
 
103
104
  if (!config) {
104
- console.error(`Error: No default export found in ${configPath}`)
105
+ logger.error(`Error: No default export found in ${configPath}`)
105
106
  return null
106
107
  }
107
108
 
@@ -112,8 +113,8 @@ export async function loadConfig (): Promise<I18nextToolkitConfig | null> {
112
113
 
113
114
  return config
114
115
  } catch (error) {
115
- console.error(`Error loading configuration from ${configPath}`)
116
- console.error(error)
116
+ logger.error(`Error loading configuration from ${configPath}`)
117
+ logger.error(error)
117
118
  return null
118
119
  }
119
120
  }
@@ -125,7 +126,7 @@ export async function loadConfig (): Promise<I18nextToolkitConfig | null> {
125
126
  * @returns A promise that resolves to a valid configuration object.
126
127
  * @throws Exits the process if the user declines to create a config or if loading fails after creation.
127
128
  */
128
- export async function ensureConfig (): Promise<I18nextToolkitConfig> {
129
+ export async function ensureConfig (logger: Logger = new ConsoleLogger()): Promise<I18nextToolkitConfig> {
129
130
  let config = await loadConfig()
130
131
 
131
132
  if (config) {
@@ -142,17 +143,17 @@ export async function ensureConfig (): Promise<I18nextToolkitConfig> {
142
143
 
143
144
  if (shouldInit) {
144
145
  await runInit() // Run the interactive setup wizard
145
- console.log(chalk.green('Configuration created. Resuming command...'))
146
+ logger.info(chalk.green('Configuration created. Resuming command...'))
146
147
  config = await loadConfig() // Try loading the newly created config
147
148
 
148
149
  if (config) {
149
150
  return config
150
151
  } else {
151
- console.error(chalk.red('Error: Failed to load configuration after creation. Please try running the command again.'))
152
+ logger.error(chalk.red('Error: Failed to load configuration after creation. Please try running the command again.'))
152
153
  process.exit(1)
153
154
  }
154
155
  } else {
155
- console.log('Operation cancelled. Please create a configuration file to proceed.')
156
+ logger.info('Operation cancelled. Please create a configuration file to proceed.')
156
157
  process.exit(0)
157
158
  }
158
159
  }
@@ -50,10 +50,10 @@ export async function runExtractor (
50
50
  const spinner = ora('Running i18next key extractor...\n').start()
51
51
 
52
52
  try {
53
- const allKeys = await findKeys(config, logger)
53
+ const { allKeys, objectKeys } = await findKeys(config, logger)
54
54
  spinner.text = `Found ${allKeys.size} unique keys. Updating translation files...`
55
55
 
56
- const results = await getTranslations(allKeys, config)
56
+ const results = await getTranslations(allKeys, objectKeys, config)
57
57
 
58
58
  let anyFileUpdated = false
59
59
  for (const result of results) {
@@ -97,8 +97,9 @@ export async function runExtractor (
97
97
  export async function processFile (
98
98
  file: string,
99
99
  config: I18nextToolkitConfig,
100
- logger: Logger,
101
- allKeys: Map<string, ExtractedKey>
100
+ allKeys: Map<string, ExtractedKey>,
101
+ astVisitors: ASTVisitors,
102
+ logger: Logger = new ConsoleLogger()
102
103
  ): Promise<void> {
103
104
  try {
104
105
  let code = await readFile(file, 'utf-8')
@@ -119,18 +120,11 @@ export async function processFile (
119
120
  // Extract keys from comments
120
121
  extractKeysFromComments(code, config.extract.functions || ['t'], pluginContext, config)
121
122
 
122
- // Extract keys from AST using visitors
123
- const astVisitors = new ASTVisitors(
124
- config,
125
- pluginContext,
126
- logger
127
- )
128
-
129
123
  astVisitors.visit(ast)
130
124
 
131
125
  // Run plugin visitors
132
126
  if ((config.plugins || []).length > 0) {
133
- traverseEveryNode(ast, (config.plugins || []), pluginContext)
127
+ traverseEveryNode(ast, (config.plugins || []), pluginContext, logger)
134
128
  }
135
129
  } catch (error) {
136
130
  throw new ExtractorError('Failed to process file', file, error as Error)
@@ -146,7 +140,7 @@ export async function processFile (
146
140
  *
147
141
  * @internal
148
142
  */
149
- function traverseEveryNode (node: any, plugins: any[], pluginContext: PluginContext): void {
143
+ function traverseEveryNode (node: any, plugins: any[], pluginContext: PluginContext, logger: Logger = new ConsoleLogger()): void {
150
144
  if (!node || typeof node !== 'object') return
151
145
 
152
146
  // Call plugins for this node
@@ -154,7 +148,7 @@ function traverseEveryNode (node: any, plugins: any[], pluginContext: PluginCont
154
148
  try {
155
149
  plugin.onVisitNode?.(node, pluginContext)
156
150
  } catch (err) {
157
- console.warn(`Plugin ${plugin.name} onVisitNode failed:`, err)
151
+ logger.warn(`Plugin ${plugin.name} onVisitNode failed:`, err)
158
152
  }
159
153
  }
160
154
 
@@ -162,10 +156,10 @@ function traverseEveryNode (node: any, plugins: any[], pluginContext: PluginCont
162
156
  const child = node[key]
163
157
  if (Array.isArray(child)) {
164
158
  for (const c of child) {
165
- if (c && typeof c === 'object') traverseEveryNode(c, plugins, pluginContext)
159
+ if (c && typeof c === 'object') traverseEveryNode(c, plugins, pluginContext, logger)
166
160
  }
167
161
  } else if (child && typeof child === 'object') {
168
- traverseEveryNode(child, plugins, pluginContext)
162
+ traverseEveryNode(child, plugins, pluginContext, logger)
169
163
  }
170
164
  }
171
165
  }
@@ -190,6 +184,6 @@ export async function extract (config: I18nextToolkitConfig) {
190
184
  if (!config.extract.secondaryLanguages) config.extract.secondaryLanguages = config.locales.filter((l: string) => l !== config?.extract?.primaryLanguage)
191
185
  if (!config.extract.functions) config.extract.functions = ['t']
192
186
  if (!config.extract.transComponents) config.extract.transComponents = ['Trans']
193
- const allKeys = await findKeys(config)
194
- return getTranslations(allKeys, config)
187
+ const { allKeys, objectKeys } = await findKeys(config)
188
+ return getTranslations(allKeys, objectKeys, config)
195
189
  }
@@ -2,7 +2,8 @@ import { glob } from 'glob'
2
2
  import type { ExtractedKey, Logger, I18nextToolkitConfig } from '../../types'
3
3
  import { processFile } from './extractor'
4
4
  import { ConsoleLogger } from '../../utils/logger'
5
- import { initializePlugins } from '../plugin-manager'
5
+ import { initializePlugins, createPluginContext } from '../plugin-manager'
6
+ import { ASTVisitors } from '../parsers/ast-visitors'
6
7
 
7
8
  /**
8
9
  * Main function for finding translation keys across all source files in a project.
@@ -35,14 +36,17 @@ import { initializePlugins } from '../plugin-manager'
35
36
  export async function findKeys (
36
37
  config: I18nextToolkitConfig,
37
38
  logger: Logger = new ConsoleLogger()
38
- ): Promise<Map<string, ExtractedKey>> {
39
+ ): Promise<{ allKeys: Map<string, ExtractedKey>, objectKeys: Set<string> }> {
39
40
  const sourceFiles = await processSourceFiles(config)
40
41
  const allKeys = new Map<string, ExtractedKey>()
41
42
 
43
+ // Create a single visitors instance to accumulate data across all files
44
+ const astVisitors = new ASTVisitors(config, createPluginContext(allKeys), logger)
45
+
42
46
  await initializePlugins(config.plugins || [])
43
47
 
44
48
  for (const file of sourceFiles) {
45
- await processFile(file, config, logger, allKeys)
49
+ await processFile(file, config, allKeys, astVisitors, logger)
46
50
  }
47
51
 
48
52
  // Run onEnd hooks
@@ -50,7 +54,7 @@ export async function findKeys (
50
54
  await plugin.onEnd?.(allKeys)
51
55
  }
52
56
 
53
- return allKeys
57
+ return { allKeys, objectKeys: astVisitors.objectKeys }
54
58
  }
55
59
 
56
60
  /**
@@ -45,11 +45,17 @@ function globToRegex (glob: string): RegExp {
45
45
  */
46
46
  export async function getTranslations (
47
47
  keys: Map<string, ExtractedKey>,
48
+ objectKeys: Set<string>,
48
49
  config: I18nextToolkitConfig
49
50
  ): Promise<TranslationResult[]> {
50
51
  const defaultNS = config.extract.defaultNS ?? 'translation'
51
52
  const keySeparator = config.extract.keySeparator ?? '.'
52
- const preservePatterns = (config.extract.preservePatterns ?? []).map(globToRegex)
53
+ const patternsToPreserve = [...(config.extract.preservePatterns || [])]
54
+ for (const key of objectKeys) {
55
+ // Convert the object key to a glob pattern to preserve all its children
56
+ patternsToPreserve.push(`${key}.*`)
57
+ }
58
+ const preservePatterns = patternsToPreserve.map(globToRegex)
53
59
  if (!config.extract.primaryLanguage) config.extract.primaryLanguage = config.locales[0] || 'en'
54
60
  if (!config.extract.secondaryLanguages) config.extract.secondaryLanguages = config.locales.filter((l: string) => l !== config.extract.primaryLanguage)
55
61
 
@@ -41,6 +41,8 @@ export class ASTVisitors {
41
41
  private readonly logger: Logger
42
42
  private scopeStack: Array<Map<string, ScopeInfo>> = []
43
43
 
44
+ public objectKeys = new Set<string>()
45
+
44
46
  /**
45
47
  * Creates a new AST visitor instance.
46
48
  *
@@ -275,7 +277,8 @@ export class ASTVisitors {
275
277
  const optionsArg = node.init.arguments?.[1]?.expression
276
278
  let keyPrefix: string | undefined
277
279
  if (optionsArg?.type === 'ObjectExpression') {
278
- keyPrefix = this.getObjectPropValue(optionsArg, 'keyPrefix')
280
+ const kp = this.getObjectPropValue(optionsArg, 'keyPrefix')
281
+ keyPrefix = typeof kp === 'string' ? kp : undefined
279
282
  }
280
283
 
281
284
  // Store the scope info for the declared variable
@@ -333,66 +336,91 @@ export class ASTVisitors {
333
336
  const callee = node.callee
334
337
  if (callee.type !== 'Identifier') return
335
338
 
336
- const isConfiguredFunction = (this.config.extract.functions || []).includes(callee.value)
337
339
  const scopeInfo = this.getVarFromScope(callee.value)
338
- const isScopedFunction = scopeInfo !== undefined
339
-
340
- if (!isConfiguredFunction && !isScopedFunction) return
341
- if (node.arguments.length === 0) return
340
+ const isFunctionToParse = (this.config.extract.functions || []).includes(callee.value) || scopeInfo !== undefined
341
+ if (!isFunctionToParse || node.arguments.length === 0) return
342
342
 
343
343
  const firstArg = node.arguments[0].expression
344
- let key: string | null = null
344
+ const keysToProcess: string[] = []
345
345
 
346
346
  if (firstArg.type === 'StringLiteral') {
347
- key = firstArg.value
347
+ keysToProcess.push(firstArg.value)
348
348
  } else if (firstArg.type === 'ArrowFunctionExpression') {
349
- key = this.extractKeyFromSelector(firstArg)
349
+ const key = this.extractKeyFromSelector(firstArg)
350
+ if (key) keysToProcess.push(key)
351
+ } else if (firstArg.type === 'ArrayExpression') {
352
+ for (const element of firstArg.elements) {
353
+ // We only extract static string literals from the array
354
+ if (element?.expression.type === 'StringLiteral') {
355
+ keysToProcess.push(element.expression.value)
356
+ }
357
+ }
350
358
  }
351
359
 
352
- if (!key) return // Could not statically extract a key
360
+ if (keysToProcess.length === 0) return
353
361
 
354
- let ns: string | undefined
355
- let finalKey = key
356
362
  const options = node.arguments.length > 1 ? node.arguments[1].expression : undefined
363
+ const defaultValue = this.getDefaultValue(node, keysToProcess[keysToProcess.length - 1])
357
364
 
358
- // Determine namespace (explicit ns > scope ns > ns:key > default)
359
- if (options?.type === 'ObjectExpression') ns = this.getObjectPropValue(options, 'ns')
360
- if (!ns && scopeInfo?.defaultNs) ns = scopeInfo.defaultNs
361
-
362
- const nsSeparator = this.config.extract.nsSeparator ?? ':'
363
- const contextSeparator = this.config.extract.contextSeparator ?? '_'
364
- if (!ns && nsSeparator && key.includes(nsSeparator)) {
365
- const parts = key.split(nsSeparator)
366
- ns = parts.shift()
367
- key = parts.join(nsSeparator)
368
- finalKey = key
369
- }
370
- if (!ns) ns = this.config.extract.defaultNS
365
+ // Loop through each key found (could be one or more) and process it
366
+ for (let i = 0; i < keysToProcess.length; i++) {
367
+ let key = keysToProcess[i]
368
+ let ns: string | undefined
371
369
 
372
- // Prepend keyPrefix from scope if it exists
373
- if (scopeInfo?.keyPrefix) {
374
- const keySeparator = this.config.extract.keySeparator ?? '.'
375
- finalKey = `${scopeInfo.keyPrefix}${keySeparator}${key}`
376
- }
370
+ // Determine namespace (explicit ns > scope ns > ns:key > default)
371
+ if (options?.type === 'ObjectExpression') {
372
+ const nsVal = this.getObjectPropValue(options, 'ns')
373
+ if (typeof nsVal === 'string') ns = nsVal
374
+ }
375
+ if (!ns && scopeInfo?.defaultNs) ns = scopeInfo.defaultNs
377
376
 
378
- // For selectors, defaultValue is the key. For strings, parse it.
379
- const defaultValue = (firstArg.type === 'StringLiteral') ? this.getDefaultValue(node, key) : key
377
+ const nsSeparator = this.config.extract.nsSeparator ?? ':'
378
+ if (!ns && nsSeparator && key.includes(nsSeparator)) {
379
+ const parts = key.split(nsSeparator)
380
+ ns = parts.shift()
381
+ key = parts.join(nsSeparator)
382
+ }
383
+ if (!ns) ns = this.config.extract.defaultNS
380
384
 
381
- // Plural/Context logic
382
- if (options?.type === 'ObjectExpression') {
383
- const contextValue = this.getObjectPropValue(options, 'context')
384
- if (contextValue) {
385
- this.pluginContext.addKey({ key: `${finalKey}${contextSeparator}${contextValue}`, ns, defaultValue })
386
- return
385
+ let finalKey = key
386
+ if (scopeInfo?.keyPrefix) {
387
+ const keySeparator = this.config.extract.keySeparator ?? '.'
388
+ finalKey = `${scopeInfo.keyPrefix}${keySeparator}${key}`
387
389
  }
388
- if (this.getObjectPropValue(options, 'count') !== undefined) {
389
- this.handlePluralKeys(finalKey, defaultValue, ns)
390
- return
390
+
391
+ // The explicit defaultValue only applies to the LAST key in the fallback array.
392
+ // For all preceding keys, their own key is their fallback.
393
+ const isLastKey = i === keysToProcess.length - 1
394
+ const dv = isLastKey ? defaultValue : key
395
+
396
+ // Handle plurals, context, and returnObjects
397
+ let keyHandled = false
398
+ if (options?.type === 'ObjectExpression') {
399
+ // Handle context
400
+ const contextValue = this.getObjectPropValue(options, 'context')
401
+ if (typeof contextValue === 'string' && contextValue) {
402
+ const contextSeparator = this.config.extract.contextSeparator ?? '_'
403
+ this.pluginContext.addKey({ key: `${finalKey}${contextSeparator}${contextValue}`, ns, defaultValue: dv })
404
+ keyHandled = true
405
+ }
406
+
407
+ // Handle plurals
408
+ if (!keyHandled && this.getObjectPropValue(options, 'count') !== undefined) {
409
+ this.handlePluralKeys(finalKey, dv, ns)
410
+ keyHandled = true
411
+ }
412
+
413
+ // Handle returnObjects
414
+ if (!keyHandled && this.getObjectPropValue(options, 'returnObjects') === true) {
415
+ this.objectKeys.add(finalKey)
416
+ // We still add the base key itself
417
+ }
391
418
  }
392
- }
393
419
 
394
- // Standard key
395
- this.pluginContext.addKey({ key: finalKey, ns, defaultValue })
420
+ if (!keyHandled) {
421
+ this.pluginContext.addKey({ key: finalKey, ns, defaultValue: dv })
422
+ }
423
+ }
396
424
  }
397
425
 
398
426
  /**
@@ -451,7 +479,10 @@ export class ASTVisitors {
451
479
  }
452
480
 
453
481
  if (secondArg.type === 'ObjectExpression') {
454
- return this.getObjectPropValue(secondArg, 'defaultValue') || fallback
482
+ const val = this.getObjectPropValue(secondArg, 'defaultValue')
483
+ if (typeof val === 'string') return val || fallback
484
+ if (typeof val === 'number' || typeof val === 'boolean') return String(val)
485
+ return fallback
455
486
  }
456
487
 
457
488
  return fallback
@@ -553,7 +584,7 @@ export class ASTVisitors {
553
584
  *
554
585
  * @private
555
586
  */
556
- private getObjectPropValue (object: ObjectExpression, propName: string): string | undefined {
587
+ private getObjectPropValue (object: ObjectExpression, propName: string): string | boolean | number | undefined {
557
588
  const prop = (object.properties).find(
558
589
  (p) =>
559
590
  p.type === 'KeyValueProperty' &&
@@ -565,12 +596,18 @@ export class ASTVisitors {
565
596
 
566
597
  if (prop?.type === 'KeyValueProperty') {
567
598
  const val = prop.value
568
- // Only return the value if it's a string, otherwise we just care that it exists (for `count`)
599
+ // Return concrete literal values when possible
569
600
  if (val.type === 'StringLiteral') {
570
601
  return val.value
571
602
  }
572
- // For properties like `count`, the value could be a number, but we just need to know it's there.
573
- // So we return a non-undefined value. An empty string is fine.
603
+ if (val.type === 'BooleanLiteral') {
604
+ return val.value
605
+ }
606
+ if (val.type === 'NumericLiteral') {
607
+ return val.value
608
+ }
609
+ // For other expression types (identifier, member expr, etc.) we only care that the prop exists.
610
+ // Return an empty string to indicate presence.
574
611
  return ''
575
612
  }
576
613
  return undefined
package/src/status.ts CHANGED
@@ -84,7 +84,7 @@ export async function runStatus (config: I18nextToolkitConfig, options: StatusOp
84
84
  * @throws {Error} When key extraction fails or configuration is invalid
85
85
  */
86
86
  async function generateStatusReport (config: I18nextToolkitConfig): Promise<StatusReport> {
87
- const allExtractedKeys = await findKeys(config)
87
+ const { allKeys: allExtractedKeys } = await findKeys(config)
88
88
  const { primaryLanguage, keySeparator = '.', defaultNS = 'translation' } = config.extract
89
89
  const secondaryLanguages = config.locales.filter(l => l !== primaryLanguage)
90
90
 
package/src/types.ts CHANGED
@@ -277,13 +277,13 @@ export interface Logger {
277
277
  * Logs a warning message.
278
278
  * @param message - The warning message to log
279
279
  */
280
- warn(message: string): void;
280
+ warn(message: string, more?: any): void;
281
281
 
282
282
  /**
283
283
  * Logs an error message.
284
284
  * @param message - The error message to log
285
285
  */
286
- error(message: string): void;
286
+ error(message: string | any): void;
287
287
  }
288
288
 
289
289
  /**
package/types/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { I18nextToolkitConfig } from './types';
1
+ import type { I18nextToolkitConfig, Logger } from './types';
2
2
  /**
3
3
  * A helper function for defining the i18next-cli config with type-safety.
4
4
  *
@@ -38,7 +38,7 @@ export declare function defineConfig(config: I18nextToolkitConfig): I18nextToolk
38
38
  * }
39
39
  * ```
40
40
  */
41
- export declare function loadConfig(): Promise<I18nextToolkitConfig | null>;
41
+ export declare function loadConfig(logger?: Logger): Promise<I18nextToolkitConfig | null>;
42
42
  /**
43
43
  * NEW: Ensures a configuration exists, prompting the user to create one if necessary.
44
44
  * This function is a wrapper around loadConfig that provides an interactive fallback.
@@ -46,5 +46,5 @@ export declare function loadConfig(): Promise<I18nextToolkitConfig | null>;
46
46
  * @returns A promise that resolves to a valid configuration object.
47
47
  * @throws Exits the process if the user declines to create a config or if loading fails after creation.
48
48
  */
49
- export declare function ensureConfig(): Promise<I18nextToolkitConfig>;
49
+ export declare function ensureConfig(logger?: Logger): Promise<I18nextToolkitConfig>;
50
50
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAanD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAE,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAEhF;AAqBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,IAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAsCxE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAK,OAAO,CAAC,oBAAoB,CAAC,CA8BnE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAc3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAE,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAEhF;AAqBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAE,MAAM,GAAE,MAA4B,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAsC5G;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAE,MAAM,GAAE,MAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8BvG"}
@@ -1,4 +1,5 @@
1
1
  import type { Logger, ExtractedKey, I18nextToolkitConfig } from '../../types';
2
+ import { ASTVisitors } from '../parsers/ast-visitors';
2
3
  /**
3
4
  * Main extractor function that runs the complete key extraction and file generation process.
4
5
  *
@@ -46,7 +47,7 @@ export declare function runExtractor(config: I18nextToolkitConfig, logger?: Logg
46
47
  *
47
48
  * @internal
48
49
  */
49
- export declare function processFile(file: string, config: I18nextToolkitConfig, logger: Logger, allKeys: Map<string, ExtractedKey>): Promise<void>;
50
+ export declare function processFile(file: string, config: I18nextToolkitConfig, allKeys: Map<string, ExtractedKey>, astVisitors: ASTVisitors, logger?: Logger): Promise<void>;
50
51
  /**
51
52
  * Simplified extraction function that returns translation results without file writing.
52
53
  * Used primarily for testing and programmatic access.
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAiB,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAS5F;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CA+BlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAoCf;AAmCD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,sDAO1D"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAiB,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAM5F,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAGrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CA+BlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,WAAW,EACxB,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAmCD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,sDAO1D"}
@@ -27,5 +27,8 @@ import type { ExtractedKey, Logger, I18nextToolkitConfig } from '../../types';
27
27
  * console.log(`Found ${keys.size} unique translation keys`)
28
28
  * ```
29
29
  */
30
- export declare function findKeys(config: I18nextToolkitConfig, logger?: Logger): Promise<Map<string, ExtractedKey>>;
30
+ export declare function findKeys(config: I18nextToolkitConfig, logger?: Logger): Promise<{
31
+ allKeys: Map<string, ExtractedKey>;
32
+ objectKeys: Set<string>;
33
+ }>;
31
34
  //# sourceMappingURL=key-finder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"key-finder.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/key-finder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAK7E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAgBpC"}
1
+ {"version":3,"file":"key-finder.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/key-finder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAM7E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAmB1E"}
@@ -27,5 +27,5 @@ import { TranslationResult, ExtractedKey, I18nextToolkitConfig } from '../../typ
27
27
  * // Results contain update status and new/existing translations for each locale
28
28
  * ```
29
29
  */
30
- export declare function getTranslations(keys: Map<string, ExtractedKey>, config: I18nextToolkitConfig): Promise<TranslationResult[]>;
30
+ export declare function getTranslations(keys: Map<string, ExtractedKey>, objectKeys: Set<string>, config: I18nextToolkitConfig): Promise<TranslationResult[]>;
31
31
  //# sourceMappingURL=translation-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAiBnF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkE9B"}
1
+ {"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAiBnF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAuE9B"}
@@ -27,6 +27,7 @@ export declare class ASTVisitors {
27
27
  private readonly config;
28
28
  private readonly logger;
29
29
  private scopeStack;
30
+ objectKeys: Set<string>;
30
31
  /**
31
32
  * Creates a new AST visitor instance.
32
33
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAmG,MAAM,WAAW,CAAA;AACxI,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAc9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAoC;IAEtD;;;;;;OAMG;gBAED,MAAM,EAAE,oBAAoB,EAC5B,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM;IAOhB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAiDZ;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAMrB;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IASvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAqBhC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,8BAA8B;IAiDtC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IAkE5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,sBAAsB;CA0C/B"}
1
+ {"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAmG,MAAM,WAAW,CAAA;AACxI,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAc9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAoC;IAE/C,UAAU,cAAoB;IAErC;;;;;;OAMG;gBAED,MAAM,EAAE,oBAAoB,EAC5B,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM;IAOhB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAiDZ;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAMrB;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IASvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAqBhC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,8BAA8B;IAkDtC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IA2F5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,eAAe;IAmBvB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IA6B1B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,sBAAsB;CA0C/B"}
package/types/types.d.ts CHANGED
@@ -233,12 +233,12 @@ export interface Logger {
233
233
  * Logs a warning message.
234
234
  * @param message - The warning message to log
235
235
  */
236
- warn(message: string): void;
236
+ warn(message: string, more?: any): void;
237
237
  /**
238
238
  * Logs an error message.
239
239
  * @param message - The error message to log
240
240
  */
241
- error(message: string): void;
241
+ error(message: string | any): void;
242
242
  }
243
243
  /**
244
244
  * Context object provided to plugins during AST traversal.
@@ -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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"}