i18next-cli 1.5.11 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +6 -6
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/extractor.js +1 -1
- package/dist/cjs/extractor/core/key-finder.js +1 -1
- package/dist/cjs/extractor/parsers/comment-parser.js +1 -1
- package/dist/cjs/extractor/parsers/jsx-parser.js +1 -1
- package/dist/cjs/extractor/plugin-manager.js +1 -1
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/extractor.js +1 -1
- package/dist/esm/extractor/core/key-finder.js +1 -1
- package/dist/esm/extractor/parsers/comment-parser.js +1 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +1 -1
- package/dist/esm/extractor/plugin-manager.js +1 -1
- package/package.json +4 -1
- package/src/cli.ts +1 -1
- package/src/extractor/core/extractor.ts +18 -5
- package/src/extractor/core/key-finder.ts +9 -2
- package/src/extractor/parsers/ast-visitors.ts +2 -13
- package/src/extractor/parsers/comment-parser.ts +16 -3
- package/src/extractor/parsers/jsx-parser.ts +27 -0
- package/src/extractor/plugin-manager.ts +6 -2
- package/src/types.ts +25 -0
- package/types/extractor/core/extractor.d.ts.map +1 -1
- package/types/extractor/core/key-finder.d.ts.map +1 -1
- package/types/extractor/parsers/ast-visitors.d.ts +2 -2
- package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/comment-parser.d.ts +6 -3
- package/types/extractor/parsers/comment-parser.d.ts.map +1 -1
- package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
- package/types/extractor/plugin-manager.d.ts +2 -2
- package/types/extractor/plugin-manager.d.ts.map +1 -1
- package/types/types.d.ts +21 -0
- package/types/types.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ 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.6.0](https://github.com/i18next/i18next-cli/compare/v1.5.11...v1.6.0) - 2025-10-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Plugin System:** Enhanced plugin capabilities with improved context access and error handling. Plugins now receive richer context objects including:
|
|
12
|
+
- `getVarFromScope(name)`: Access to variable scope information for understanding `useTranslation` hooks, `getFixedT` calls, and namespace context
|
|
13
|
+
- Full configuration access via `context.config`
|
|
14
|
+
- Enhanced logging utilities via `context.logger`
|
|
15
|
+
- **Plugin System:** Added robust error handling for plugin hooks. Plugin failures (in `onLoad` and `onVisitNode`) no longer crash the extraction process but are logged as warnings, allowing extraction to continue gracefully
|
|
16
|
+
- **Plugin System:** Improved TypeScript support in plugins with better AST node access, enabling plugins to handle TypeScript-specific syntax like `satisfies` and `as` operators for advanced extraction patterns
|
|
17
|
+
|
|
18
|
+
### Enhanced
|
|
19
|
+
- **Extractor:** The `processFile` function now provides plugins with comprehensive access to the parsing context, enabling sophisticated custom extraction logic that can leverage variable scope analysis and TypeScript-aware parsing
|
|
20
|
+
- **Extractor (`<Trans>`):** Fixed namespace prefix duplication when both `ns` prop and namespace prefix in `i18nKey` are specified. When a `<Trans>` component has both `ns="form"` and `i18nKey="form:cost_question.description"`, the extractor now correctly removes the redundant namespace prefix and extracts `cost_question.description` to the `form.json` file, matching i18next and i18next-parser behavior. [#45](https://github.com/i18next/i18next-cli/issues/45)
|
|
21
|
+
- **Extractor (Comments):** Fixed namespace scope resolution for `t()` calls in comments. Commented translation calls like `// t("Private")` now correctly inherit the namespace from the surrounding `useTranslation('access')` scope instead of defaulting to the default namespace, matching i18next-parser behavior. This ensures consistency between commented and actual translation calls within the same component scope. [#44](https://github.com/i18next/i18next-cli/issues/44)
|
|
22
|
+
|
|
8
23
|
## [1.5.11](https://github.com/i18next/i18next-cli/compare/v1.5.10...v1.5.11) - 2025-10-05
|
|
9
24
|
|
|
10
25
|
- **Extractor:** Fixed handling of empty strings in template literals where conditional expressions could result in empty string concatenation (e.g., `` t(`key${condition ? '.suffix' : ''}`) ``). Empty strings are now properly filtered out during template literal resolution, ensuring only valid key variants are generated. [#41](https://github.com/i18next/i18next-cli/pull/41)
|
package/README.md
CHANGED
|
@@ -383,15 +383,15 @@ export default defineConfig({
|
|
|
383
383
|
|
|
384
384
|
### Plugin System
|
|
385
385
|
|
|
386
|
-
Create custom plugins to extend the capabilities of `i18next-cli`. The plugin system provides several hooks that allow you to tap into different stages of the extraction process.
|
|
386
|
+
Create custom plugins to extend the capabilities of `i18next-cli`. The plugin system provides several hooks that allow you to tap into different stages of the extraction process, with full access to the AST parsing context and variable scope information.
|
|
387
387
|
|
|
388
388
|
**Available Hooks:**
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
390
|
+
- `setup`: Runs once when the CLI is initialized. Use it for any setup tasks.
|
|
391
|
+
- `onLoad`: Runs for each file *before* it is parsed. You can use this to transform code (e.g., transpile a custom language to JavaScript).
|
|
392
|
+
- `onVisitNode`: Runs for every node in the Abstract Syntax Tree (AST) of a parsed JavaScript/TypeScript file. This provides access to the full parsing context, including variable scope and TypeScript-specific syntax like `satisfies` and `as` operators.
|
|
393
|
+
- `onEnd`: Runs after all JS/TS files have been parsed but *before* the final keys are compared with existing translation files. This is the ideal hook for parsing non-JavaScript files (like `.html`, `.vue`, or `.svelte`) and adding their keys to the collection.
|
|
394
|
+
- `afterSync`: Runs after the extractor has compared the found keys with your translation files and generated the final results. This is perfect for post-processing tasks, like generating a report of newly added keys.
|
|
395
395
|
|
|
396
396
|
**Example Plugin (`my-custom-plugin.mjs`):**
|
|
397
397
|
|
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"),n=require("glob"),o=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.
|
|
2
|
+
"use strict";var e=require("commander"),t=require("chokidar"),n=require("glob"),o=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.6.0"),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.").option("--dry-run","Run the extractor without writing any files to disk.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a,{isWatchMode:e.watch,isDryRun:e.dryRun});e.ci&&t&&(console.error(o.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(o.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${o.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await n.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 n=await i.loadConfig();if(!n){console.log(o.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(o.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${o.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(o.green("Project structure detected successfully!")),n=e}await g.runStatus(n,{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 o=await i.ensureConfig(),a=()=>c.runTypesGenerator(o);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await n.glob(o.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 [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await l.runMigrator(e)}),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.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async e=>{const r=async()=>{let e=await i.loadConfig();if(!e){console.log(o.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(o.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${o.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(o.green("Project structure detected successfully!")),e=t}await d.runLinter(e)};if(await r(),e.watch){console.log("\nWatching for changes...");const e=await i.loadConfig();if(e?.extract?.input){t.watch(await n.glob(e.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}}),f.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("ora"),t=require("chalk"),r=require("@swc/core"),a=require("node:fs/promises"),n=require("node:path"),o=require("./key-finder.js"),
|
|
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"),i=require("./translation-manager.js"),s=require("../../utils/validation.js"),c=require("../plugin-manager.js"),l=require("../parsers/comment-parser.js"),u=require("../../utils/logger.js"),g=require("../../utils/file-utils.js"),f=require("../../utils/funnel-msg-tracker.js");function d(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&&d(e,t,r,a);else o&&"object"==typeof o&&d(o,t,r,a)}}}exports.extract=async function(e){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(t=>t!==e?.extract?.primaryLanguage),e.extract.functions||=["t","*.t"],e.extract.transComponents||=["Trans"];const{allKeys:t,objectKeys:r}=await o.findKeys(e);return i.getTranslations(t,r,e)},exports.processFile=async function(e,t,n,o,i=new u.ConsoleLogger){try{let s=await a.readFile(e,"utf-8");for(const r of t.plugins||[])try{const t=await(r.onLoad?.(s,e));void 0!==t&&(s=t)}catch(e){i.warn(`Plugin ${r.name} onLoad failed:`,e)}const u=await r.parse(s,{syntax:"typescript",tsx:!0,decorators:!0,comments:!0}),g=c.createPluginContext(n,t,i);g.getVarFromScope=o.getVarFromScope.bind(o),l.extractKeysFromComments(s,g,t,o.getVarFromScope.bind(o)),o.visit(u),(t.plugins||[]).length>0&&d(u,t.plugins||[],g,i)}catch(t){throw new s.ExtractorError("Failed to process file",e,t)}},exports.runExtractor=async function(r,{isWatchMode:c=!1,isDryRun:l=!1}={},d=new u.ConsoleLogger){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(e=>e!==r?.extract?.primaryLanguage),r.extract.functions||=["t","*.t"],r.extract.transComponents||=["Trans"],s.validateExtractorConfig(r);const p=e("Running i18next key extractor...\n").start();try{const{allKeys:e,objectKeys:s}=await o.findKeys(r,d);p.text=`Found ${e.size} unique keys. Updating translation files...`;const c=await i.getTranslations(e,s,r);let u=!1;for(const e of c)if(e.updated&&(u=!0,!l)){const o=g.serializeTranslationFile(e.newTranslations,r.extract.outputFormat,r.extract.indentation);await a.mkdir(n.dirname(e.path),{recursive:!0}),await a.writeFile(e.path,o),d.info(t.green(`Updated: ${e.path}`))}if((r.plugins||[]).length>0){p.text="Running post-extraction plugins...";for(const e of r.plugins||[])await(e.afterSync?.(c,r))}return p.succeed(t.bold("Extraction complete!")),u&&await async function(){if(!await f.shouldShowFunnel("extract"))return;return console.log(t.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${t.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${t.cyan("https://youtu.be/joPsZghT3wM")}`),f.recordFunnelShown("extract")}(),u}catch(e){throw p.fail(t.red("Extraction failed.")),e}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("glob"),r=require("./extractor.js"),t=require("../../utils/logger.js"),i=require("../plugin-manager.js"),
|
|
1
|
+
"use strict";var e=require("glob"),r=require("./extractor.js"),t=require("../../utils/logger.js"),i=require("../plugin-manager.js"),o=require("../parsers/ast-visitors.js");exports.findKeys=async function(n,s=new t.ConsoleLogger){const a=await async function(r){const t=["node_modules/**"],i=Array.isArray(r.extract.ignore)?r.extract.ignore:r.extract.ignore?[r.extract.ignore]:[];return await e.glob(r.extract.input,{ignore:[...t,...i],cwd:process.cwd()})}(n),c=new Map,g=i.createPluginContext(c,n,s),u=new o.ASTVisitors(n,g,s);g.getVarFromScope=u.getVarFromScope.bind(u),await i.initializePlugins(n.plugins||[]);for(const e of a)await r.processFile(e,n,c,u,s);for(const e of n.plugins||[])await(e.onEnd?.(c));return{allKeys:c,objectKeys:u.objectKeys}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";function t(t){const e=/^\s*,\s*(['"])(.*?)\1/.exec(t);if(e)return e[2];const s=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(t);return s?s[2]:void 0}function e(t){const e=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(t);if(e)return e[2]}exports.extractKeysFromComments=function(s,n,c){const
|
|
1
|
+
"use strict";function t(t){const e=/^\s*,\s*(['"])(.*?)\1/.exec(t);if(e)return e[2];const s=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(t);return s?s[2]:void 0}function e(t){const e=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(t);if(e)return e[2]}exports.extractKeysFromComments=function(s,n,c,o){const r=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),u=function(t){const e=[],s=new Set,n=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let c;for(;null!==(c=n.exec(t));){const t=(c[1]??c[2]).trim();t&&!s.has(t)&&(s.add(t),e.push(t))}return e}(s);for(const s of u){let u;for(;null!==(u=r.exec(s));){let r,f=u[2];const l=s.slice(u.index+u[0].length),i=t(l);r=e(l);const a=c.extract.nsSeparator??":";if(!r&&a&&f.includes(a)){const t=f.split(a);r=t.shift(),f=t.join(a)}if(!r&&o){const t=o("t");t?.defaultNs&&(r=t.defaultNs)}r||(r=c.extract.defaultNS),n.addKey({key:f,ns:r,defaultValue:i??f})}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const u=!!a||!!p,
|
|
1
|
+
"use strict";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const u=!!a||!!p,l=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),o="JSXAttribute"===l?.type&&"JSXExpressionContainer"===l.value?.type&&"ObjectExpression"===l.value.expression.type?l.value.expression:void 0,y=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),v=!!y,f=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===f?.type&&"JSXExpressionContainer"===f.value?.type?f.value.expression:void 0;const d=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let S;if(S="JSXAttribute"===d?.type&&"StringLiteral"===d.value?.type?d.value.value:void 0,o&&(void 0===S&&(S=e.getObjectPropValue(o,"ns")),void 0===c)){const t=e.getObjectProperty(o,"context");t?.value&&(c=t.value)}const b=function(e,t){const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function i(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const n=e.expression;if("StringLiteral"===n.type)t+=n.value;else if("Identifier"===n.type)t+=`{{${n.value}}}`;else if("ObjectExpression"===n.type){const e=n.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=i(e.children);a&&n.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=i(e.children))}),t}return i(e).trim().replace(/\s{2,}/g," ")}(t.children,n);let x,m,J=n.extract.defaultValue||"";if("JSXAttribute"===r?.type&&"StringLiteral"===r.value?.type&&(J=r.value.value),"JSXAttribute"===i?.type){if("StringLiteral"===i.value?.type){if(x=i.value,m=x.value,S&&"StringLiteral"===x.type){const e=n.extract.nsSeparator??":",t=x.value;e&&t.startsWith(`${S}${e}`)&&(m=t.slice(`${S}${e}`.length),x={...x,value:m})}}else"JSXExpressionContainer"===i.value?.type&&"JSXEmptyExpression"!==i.value.expression.type&&(x=i.value.expression);if(!x)return null}return r||!m||b.trim()?!r&&b.trim()&&(J=b):J=m,{keyExpression:x,serializedChildren:b,ns:S,defaultValue:J,hasCount:u,isOrdinal:v,contextExpression:c,optionsNode:o}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";exports.createPluginContext=function(t){return{addKey:e=>{const n=`${e.ns??"translation"}:${e.key}`;if(!t.has(n)){const
|
|
1
|
+
"use strict";exports.createPluginContext=function(t,e,n){return{addKey:e=>{const n=`${e.ns??"translation"}:${e.key}`;if(!t.has(n)){const o=e.defaultValue??e.key;t.set(n,{...e,defaultValue:o})}},config:e,logger:n,getVarFromScope:()=>{}}},exports.initializePlugins=async function(t){for(const e of t)await(e.setup?.())};
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import n from"chalk";import{ensureConfig as i,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as f,runLocizeDownload as u,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.
|
|
2
|
+
import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import n from"chalk";import{ensureConfig as i,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as f,runLocizeDownload as u,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.6.0"),w.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").option("--dry-run","Run the extractor without writing any files to disk.").action(async t=>{const a=await i(),c=async()=>{const o=await r(a,{isWatchMode:t.watch,isDryRun:t.dryRun});t.ci&&o&&(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(),t.watch){console.log("\nWatching for changes...");o.watch(await e(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),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(t,o)=>{let e=await a();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await c();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await g(e,{detail:t,namespace:o.namespace})}),w.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async t=>{const n=await i(),a=()=>s(n);if(await a(),t.watch){console.log("\nWatching for changes...");o.watch(await e(n.types?.input||[]),{persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const t=await i();await l(t)}),w.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async t=>{await m(t)}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async t=>{const i=async()=>{let t=await a();if(!t){console.log(n.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),t=o}await d(t)};if(await i(),t.watch){console.log("\nWatching for changes...");const t=await a();if(t?.extract?.input){o.watch(await e(t.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),i()})}}}),w.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async t=>{const o=await i();await f(o,t)}),w.command("locize-download").description("Download all translations from your locize project.").action(async t=>{const o=await i();await u(o,t)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async t=>{const o=await i();await h(o,t)}),w.parse(process.argv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"ora";import o from"chalk";import{parse as e}from"@swc/core";import{mkdir as a,writeFile as n,readFile as r}from"node:fs/promises";import{dirname as i}from"node:path";import{findKeys as s}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as l,ExtractorError as f}from"../../utils/validation.js";import{createPluginContext as
|
|
1
|
+
import t from"ora";import o from"chalk";import{parse as e}from"@swc/core";import{mkdir as a,writeFile as n,readFile as r}from"node:fs/promises";import{dirname as i}from"node:path";import{findKeys as s}from"./key-finder.js";import{getTranslations as c}from"./translation-manager.js";import{validateExtractorConfig as l,ExtractorError as f}from"../../utils/validation.js";import{createPluginContext as m}from"../plugin-manager.js";import{extractKeysFromComments as p}from"../parsers/comment-parser.js";import{ConsoleLogger as u}from"../../utils/logger.js";import{serializeTranslationFile as g}from"../../utils/file-utils.js";import{shouldShowFunnel as y,recordFunnelShown as d}from"../../utils/funnel-msg-tracker.js";async function w(e,{isWatchMode:r=!1,isDryRun:f=!1}={},m=new u){e.extract.primaryLanguage||=e.locales[0]||"en",e.extract.secondaryLanguages||=e.locales.filter(t=>t!==e?.extract?.primaryLanguage),e.extract.functions||=["t","*.t"],e.extract.transComponents||=["Trans"],l(e);const p=t("Running i18next key extractor...\n").start();try{const{allKeys:t,objectKeys:r}=await s(e,m);p.text=`Found ${t.size} unique keys. Updating translation files...`;const l=await c(t,r,e);let u=!1;for(const t of l)if(t.updated&&(u=!0,!f)){const r=g(t.newTranslations,e.extract.outputFormat,e.extract.indentation);await a(i(t.path),{recursive:!0}),await n(t.path,r),m.info(o.green(`Updated: ${t.path}`))}if((e.plugins||[]).length>0){p.text="Running post-extraction plugins...";for(const t of e.plugins||[])await(t.afterSync?.(l,e))}return p.succeed(o.bold("Extraction complete!")),u&&await async function(){if(!await y("extract"))return;return console.log(o.yellow.bold("\n💡 Tip: Tired of running the extractor manually?")),console.log(' Discover a real-time "push" workflow with `saveMissing` and Locize AI,'),console.log(" where keys are created and translated automatically as you code."),console.log(` Learn more: ${o.cyan("https://www.locize.com/blog/i18next-savemissing-ai-automation")}`),console.log(` Watch the video: ${o.cyan("https://youtu.be/joPsZghT3wM")}`),d("extract")}(),u}catch(t){throw p.fail(o.red("Extraction failed.")),t}}async function x(t,o,a,n,i=new u){try{let s=await r(t,"utf-8");for(const e of o.plugins||[])try{const o=await(e.onLoad?.(s,t));void 0!==o&&(s=o)}catch(t){i.warn(`Plugin ${e.name} onLoad failed:`,t)}const c=await e(s,{syntax:"typescript",tsx:!0,decorators:!0,comments:!0}),l=m(a,o,i);l.getVarFromScope=n.getVarFromScope.bind(n),p(s,l,o,n.getVarFromScope.bind(n)),n.visit(c),(o.plugins||[]).length>0&&h(c,o.plugins||[],l,i)}catch(o){throw new f("Failed to process file",t,o)}}function h(t,o,e,a=new u){if(t&&"object"==typeof t){for(const n of o)try{n.onVisitNode?.(t,e)}catch(t){a.warn(`Plugin ${n.name} onVisitNode failed:`,t)}for(const n of Object.keys(t)){const r=t[n];if(Array.isArray(r))for(const t of r)t&&"object"==typeof t&&h(t,o,e,a);else r&&"object"==typeof r&&h(r,o,e,a)}}}async function j(t){t.extract.primaryLanguage||=t.locales[0]||"en",t.extract.secondaryLanguages||=t.locales.filter(o=>o!==t?.extract?.primaryLanguage),t.extract.functions||=["t","*.t"],t.extract.transComponents||=["Trans"];const{allKeys:o,objectKeys:e}=await s(t);return c(o,e,t)}export{j as extract,x as processFile,w as runExtractor};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{glob as r}from"glob";import{processFile as
|
|
1
|
+
import{glob as r}from"glob";import{processFile as o}from"./extractor.js";import{ConsoleLogger as t}from"../../utils/logger.js";import{createPluginContext as e,initializePlugins as n}from"../plugin-manager.js";import{ASTVisitors as a}from"../parsers/ast-visitors.js";async function i(i,s=new t){const c=await async function(o){const t=["node_modules/**"],e=Array.isArray(o.extract.ignore)?o.extract.ignore:o.extract.ignore?[o.extract.ignore]:[];return await r(o.extract.input,{ignore:[...t,...e],cwd:process.cwd()})}(i),p=new Map,g=e(p,i,s),m=new a(i,g,s);g.getVarFromScope=m.getVarFromScope.bind(m),await n(i.plugins||[]);for(const r of c)await o(r,i,p,m,s);for(const r of i.plugins||[])await(r.onEnd?.(p));return{allKeys:p,objectKeys:m.objectKeys}}export{i as findKeys};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function t(t,n,c,o){const u=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(t){const e=[],s=new Set,n=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let c;for(;null!==(c=n.exec(t));){const t=(c[1]??c[2]).trim();t&&!s.has(t)&&(s.add(t),e.push(t))}return e}(t);for(const t of f){let f;for(;null!==(f=u.exec(t));){let u,l=f[2];const r=t.slice(f.index+f[0].length),i=e(r);u=s(r);const a=c.extract.nsSeparator??":";if(!u&&a&&l.includes(a)){const t=l.split(a);u=t.shift(),l=t.join(a)}if(!u&&o){const t=o("t");t?.defaultNs&&(u=t.defaultNs)}u||(u=c.extract.defaultNS),n.addKey({key:l,ns:u,defaultValue:i??l})}}}function e(t){const e=/^\s*,\s*(['"])(.*?)\1/.exec(t);if(e)return e[2];const s=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(t);return s?s[2]:void 0}function s(t){const e=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(t);if(e)return e[2]}export{t as extractKeysFromComments};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getObjectProperty as e,getObjectPropValue as t}from"./ast-utils.js";function
|
|
1
|
+
import{getObjectProperty as e,getObjectPropValue as t}from"./ast-utils.js";function i(i,n){const r=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),a=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),s=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),p=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;s||"JSXAttribute"!==p?.type||"JSXExpressionContainer"!==p.value?.type||"ObjectExpression"!==p.value.expression.type||(u=e(p.value.expression,"count"));const l=!!s||!!u,o=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===o?.type&&"JSXExpressionContainer"===o.value?.type&&"ObjectExpression"===o.value.expression.type?o.value.expression:void 0,v=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),f=!!v,d=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:void 0;const S=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let m;if(m="JSXAttribute"===S?.type&&"StringLiteral"===S.value?.type?S.value.value:void 0,y&&(void 0===m&&(m=t(y,"ns")),void 0===c)){const t=e(y,"context");t?.value&&(c=t.value)}const x=function(e,t){const i=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function n(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const i=e.expression;if("StringLiteral"===i.type)t+=i.value;else if("Identifier"===i.type)t+=`{{${i.value}}}`;else if("ObjectExpression"===i.type){const e=i.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=n(e.children);a&&i.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=n(e.children))}),t}return n(e).trim().replace(/\s{2,}/g," ")}(i.children,n);let b,J,X=n.extract.defaultValue||"";if("JSXAttribute"===a?.type&&"StringLiteral"===a.value?.type&&(X=a.value.value),"JSXAttribute"===r?.type){if("StringLiteral"===r.value?.type){if(b=r.value,J=b.value,m&&"StringLiteral"===b.type){const e=n.extract.nsSeparator??":",t=b.value;e&&t.startsWith(`${m}${e}`)&&(J=t.slice(`${m}${e}`.length),b={...b,value:J})}}else"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(b=r.value.expression);if(!b)return null}return a||!J||x.trim()?!a&&x.trim()&&(X=x):X=J,{keyExpression:b,serializedChildren:x,ns:m,defaultValue:X,hasCount:l,isOrdinal:f,contextExpression:c,optionsNode:y}}export{i as extractFromTransComponent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
async function t(t){for(const
|
|
1
|
+
async function t(t){for(const e of t)await(e.setup?.())}function e(t,e,n){return{addKey:e=>{const n=`${e.ns??"translation"}:${e.key}`;if(!t.has(n)){const o=e.defaultValue??e.key;t.set(n,{...e,defaultValue:o})}},config:e,logger:n,getVarFromScope:()=>{}}}export{e as createPluginContext,t as initializePlugins};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A unified, high-performance i18next CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"main": "./dist/cjs/index.js",
|
|
10
10
|
"module": "./dist/esm/index.js",
|
|
11
11
|
"types": "./types/index.d.ts",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22"
|
|
14
|
+
},
|
|
12
15
|
"exports": {
|
|
13
16
|
"./package.json": "./package.json",
|
|
14
17
|
".": {
|
package/src/cli.ts
CHANGED
|
@@ -137,9 +137,17 @@ export async function processFile (
|
|
|
137
137
|
try {
|
|
138
138
|
let code = await readFile(file, 'utf-8')
|
|
139
139
|
|
|
140
|
-
// Run onLoad hooks from plugins
|
|
140
|
+
// Run onLoad hooks from plugins with error handling
|
|
141
141
|
for (const plugin of (config.plugins || [])) {
|
|
142
|
-
|
|
142
|
+
try {
|
|
143
|
+
const result = await plugin.onLoad?.(code, file)
|
|
144
|
+
if (result !== undefined) {
|
|
145
|
+
code = result
|
|
146
|
+
}
|
|
147
|
+
} catch (err) {
|
|
148
|
+
logger.warn(`Plugin ${plugin.name} onLoad failed:`, err)
|
|
149
|
+
// Continue with the original code if the plugin fails
|
|
150
|
+
}
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
const ast = await parse(code, {
|
|
@@ -149,10 +157,15 @@ export async function processFile (
|
|
|
149
157
|
comments: true
|
|
150
158
|
})
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
// 1. Create the base context with config and logger.
|
|
161
|
+
const pluginContext = createPluginContext(allKeys, config, logger)
|
|
162
|
+
|
|
163
|
+
// 2. "Wire up" the visitor's scope method to the context.
|
|
164
|
+
// This avoids a circular dependency while giving plugins access to the scope.
|
|
165
|
+
pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors)
|
|
153
166
|
|
|
154
|
-
// Extract keys from comments
|
|
155
|
-
extractKeysFromComments(code, pluginContext, config)
|
|
167
|
+
// Extract keys from comments with scope resolution
|
|
168
|
+
extractKeysFromComments(code, pluginContext, config, astVisitors.getVarFromScope.bind(astVisitors))
|
|
156
169
|
|
|
157
170
|
astVisitors.visit(ast)
|
|
158
171
|
|
|
@@ -40,8 +40,15 @@ export async function findKeys (
|
|
|
40
40
|
const sourceFiles = await processSourceFiles(config)
|
|
41
41
|
const allKeys = new Map<string, ExtractedKey>()
|
|
42
42
|
|
|
43
|
-
// Create
|
|
44
|
-
const
|
|
43
|
+
// 1. Create the base context with config and logger.
|
|
44
|
+
const pluginContext = createPluginContext(allKeys, config, logger)
|
|
45
|
+
|
|
46
|
+
// 2. Create the visitor instance, passing it the context.
|
|
47
|
+
const astVisitors = new ASTVisitors(config, pluginContext, logger)
|
|
48
|
+
|
|
49
|
+
// 3. "Wire up" the visitor's scope method to the context.
|
|
50
|
+
// This avoids a circular dependency while giving plugins access to the scope.
|
|
51
|
+
pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors)
|
|
45
52
|
|
|
46
53
|
await initializePlugins(config.plugins || [])
|
|
47
54
|
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import type { Module, Node, CallExpression, VariableDeclarator, JSXElement, ArrowFunctionExpression, ObjectExpression, Expression, TemplateLiteral } from '@swc/core'
|
|
2
|
-
import type { PluginContext, I18nextToolkitConfig, Logger, ExtractedKey } from '../../types'
|
|
2
|
+
import type { PluginContext, I18nextToolkitConfig, Logger, ExtractedKey, ScopeInfo } from '../../types'
|
|
3
3
|
import { extractFromTransComponent } from './jsx-parser'
|
|
4
4
|
import { getObjectProperty, getObjectPropValue } from './ast-utils'
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Represents variable scope information tracked during AST traversal.
|
|
8
|
-
* Used to maintain context about translation functions and their configuration.
|
|
9
|
-
*/
|
|
10
|
-
interface ScopeInfo {
|
|
11
|
-
/** Default namespace for translation calls in this scope */
|
|
12
|
-
defaultNs?: string;
|
|
13
|
-
/** Key prefix to prepend to all translation keys in this scope */
|
|
14
|
-
keyPrefix?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
6
|
interface UseTranslationHookConfig {
|
|
18
7
|
name: string;
|
|
19
8
|
nsArg: number;
|
|
@@ -190,7 +179,7 @@ export class ASTVisitors {
|
|
|
190
179
|
*
|
|
191
180
|
* @private
|
|
192
181
|
*/
|
|
193
|
-
|
|
182
|
+
public getVarFromScope (name: string): ScopeInfo | undefined {
|
|
194
183
|
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
|
|
195
184
|
if (this.scopeStack[i].has(name)) {
|
|
196
185
|
return this.scopeStack[i].get(name)
|
|
@@ -5,9 +5,9 @@ import type { PluginContext, I18nextToolkitConfig } from '../../types'
|
|
|
5
5
|
* Supports extraction from single-line (//) and multi-line comments.
|
|
6
6
|
*
|
|
7
7
|
* @param code - The source code to analyze
|
|
8
|
-
* @param functionNames - Array of function names to look for (e.g., ['t', 'i18n.t'])
|
|
9
8
|
* @param pluginContext - Context object with helper methods to add found keys
|
|
10
9
|
* @param config - Configuration object containing extraction settings
|
|
10
|
+
* @param scopeResolver - Function to resolve scope information for variables (optional)
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
@@ -17,14 +17,15 @@ import type { PluginContext, I18nextToolkitConfig } from '../../types'
|
|
|
17
17
|
* `
|
|
18
18
|
*
|
|
19
19
|
* const context = createPluginContext(allKeys)
|
|
20
|
-
* extractKeysFromComments(code,
|
|
20
|
+
* extractKeysFromComments(code, context, config, scopeResolver)
|
|
21
21
|
* // Extracts: user.name and app.title with their respective settings
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
export function extractKeysFromComments (
|
|
25
25
|
code: string,
|
|
26
26
|
pluginContext: PluginContext,
|
|
27
|
-
config: I18nextToolkitConfig
|
|
27
|
+
config: I18nextToolkitConfig,
|
|
28
|
+
scopeResolver?: (varName: string) => { defaultNs?: string; keyPrefix?: string } | undefined
|
|
28
29
|
): void {
|
|
29
30
|
// Hardcode the function name to 't' to prevent parsing other functions like 'test()'.
|
|
30
31
|
const functionNameToFind = 't'
|
|
@@ -42,6 +43,7 @@ export function extractKeysFromComments (
|
|
|
42
43
|
const remainder = text.slice(match.index + match[0].length)
|
|
43
44
|
|
|
44
45
|
const defaultValue = parseDefaultValueFromComment(remainder)
|
|
46
|
+
|
|
45
47
|
// 1. Check for namespace in options object first (e.g., { ns: 'common' })
|
|
46
48
|
ns = parseNsFromComment(remainder)
|
|
47
49
|
|
|
@@ -52,6 +54,17 @@ export function extractKeysFromComments (
|
|
|
52
54
|
ns = parts.shift()
|
|
53
55
|
key = parts.join(nsSeparator)
|
|
54
56
|
}
|
|
57
|
+
|
|
58
|
+
// 3. NEW: If no explicit namespace found, try to resolve from scope
|
|
59
|
+
// This allows commented t() calls to inherit namespace from useTranslation scope
|
|
60
|
+
if (!ns && scopeResolver) {
|
|
61
|
+
const scopeInfo = scopeResolver('t')
|
|
62
|
+
if (scopeInfo?.defaultNs) {
|
|
63
|
+
ns = scopeInfo.defaultNs
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 4. Final fallback to configured default namespace
|
|
55
68
|
if (!ns) ns = config.extract.defaultNS
|
|
56
69
|
|
|
57
70
|
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
@@ -158,9 +158,28 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
let keyExpression: Expression | undefined
|
|
161
|
+
let processedKeyValue: string | undefined
|
|
162
|
+
|
|
161
163
|
if (i18nKeyAttr?.type === 'JSXAttribute') {
|
|
162
164
|
if (i18nKeyAttr.value?.type === 'StringLiteral') {
|
|
163
165
|
keyExpression = i18nKeyAttr.value
|
|
166
|
+
processedKeyValue = keyExpression.value
|
|
167
|
+
|
|
168
|
+
// Handle namespace prefix removal when both ns and i18nKey are provided
|
|
169
|
+
if (ns && keyExpression.type === 'StringLiteral') {
|
|
170
|
+
const nsSeparator = config.extract.nsSeparator ?? ':'
|
|
171
|
+
const keyValue = keyExpression.value
|
|
172
|
+
|
|
173
|
+
// If the key starts with the namespace followed by the separator, remove the prefix
|
|
174
|
+
if (nsSeparator && keyValue.startsWith(`${ns}${nsSeparator}`)) {
|
|
175
|
+
processedKeyValue = keyValue.slice(`${ns}${nsSeparator}`.length)
|
|
176
|
+
// Create a new StringLiteral with the namespace prefix removed
|
|
177
|
+
keyExpression = {
|
|
178
|
+
...keyExpression,
|
|
179
|
+
value: processedKeyValue
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
164
183
|
} else if (
|
|
165
184
|
i18nKeyAttr.value?.type === 'JSXExpressionContainer' &&
|
|
166
185
|
i18nKeyAttr.value.expression.type !== 'JSXEmptyExpression'
|
|
@@ -171,6 +190,14 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
171
190
|
if (!keyExpression) return null
|
|
172
191
|
}
|
|
173
192
|
|
|
193
|
+
// If no explicit defaults provided and we have a processed key, use it as default value
|
|
194
|
+
// This matches the behavior of other similar tests in the codebase
|
|
195
|
+
if (!defaultsAttr && processedKeyValue && !serialized.trim()) {
|
|
196
|
+
defaultValue = processedKeyValue
|
|
197
|
+
} else if (!defaultsAttr && serialized.trim()) {
|
|
198
|
+
defaultValue = serialized
|
|
199
|
+
}
|
|
200
|
+
|
|
174
201
|
return {
|
|
175
202
|
keyExpression,
|
|
176
203
|
serializedChildren: serialized,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtractedKey, PluginContext } from '../types'
|
|
1
|
+
import type { ExtractedKey, PluginContext, I18nextToolkitConfig, Logger } from '../types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Initializes an array of plugins by calling their setup hooks.
|
|
@@ -39,7 +39,7 @@ export async function initializePlugins (plugins: any[]): Promise<void> {
|
|
|
39
39
|
* })
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
export function createPluginContext (allKeys: Map<string, ExtractedKey
|
|
42
|
+
export function createPluginContext (allKeys: Map<string, ExtractedKey>, config: I18nextToolkitConfig, logger: Logger): PluginContext {
|
|
43
43
|
return {
|
|
44
44
|
addKey: (keyInfo: ExtractedKey) => {
|
|
45
45
|
// Use namespace in the unique map key to avoid collisions across namespaces
|
|
@@ -50,5 +50,9 @@ export function createPluginContext (allKeys: Map<string, ExtractedKey>): Plugin
|
|
|
50
50
|
allKeys.set(uniqueKey, { ...keyInfo, defaultValue })
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
|
+
config,
|
|
54
|
+
logger,
|
|
55
|
+
// This will be attached later, so we provide a placeholder
|
|
56
|
+
getVarFromScope: () => undefined,
|
|
53
57
|
}
|
|
54
58
|
}
|
package/src/types.ts
CHANGED
|
@@ -363,4 +363,29 @@ export interface PluginContext {
|
|
|
363
363
|
* @param keyInfo - The extracted key information
|
|
364
364
|
*/
|
|
365
365
|
addKey: (keyInfo: ExtractedKey) => void;
|
|
366
|
+
|
|
367
|
+
/** The fully resolved i18next-cli configuration. */
|
|
368
|
+
config: I18nextToolkitConfig;
|
|
369
|
+
|
|
370
|
+
/** The shared logger instance. */
|
|
371
|
+
logger: Logger;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Retrieves variable information from the current scope chain.
|
|
375
|
+
* Searches from the innermost scope outwards.
|
|
376
|
+
* @param name - The variable name to look up (e.g., 't').
|
|
377
|
+
* @returns Scope information if found, otherwise undefined.
|
|
378
|
+
*/
|
|
379
|
+
getVarFromScope: (name: string) => ScopeInfo | undefined;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Represents variable scope information tracked during AST traversal.
|
|
384
|
+
* Used to maintain context about translation functions and their configuration.
|
|
385
|
+
*/
|
|
386
|
+
export interface ScopeInfo {
|
|
387
|
+
/** Default namespace for translation calls in this scope */
|
|
388
|
+
defaultNs?: string;
|
|
389
|
+
/** Key prefix to prepend to all translation keys in this scope */
|
|
390
|
+
keyPrefix?: string;
|
|
366
391
|
}
|
|
@@ -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;AAM5F,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAKrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,EACE,WAAmB,EACnB,QAAgB,EACjB,GAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACf,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAuDlB;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,
|
|
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;AAKrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,EACE,WAAmB,EACnB,QAAgB,EACjB,GAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACf,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAuDlB;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,CA2Cf;AAmCD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,sDAO1D"}
|
|
@@ -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;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,
|
|
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,CA0B1E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Module } from '@swc/core';
|
|
2
|
-
import type { PluginContext, I18nextToolkitConfig, Logger } from '../../types';
|
|
2
|
+
import type { PluginContext, I18nextToolkitConfig, Logger, ScopeInfo } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* AST visitor class that traverses JavaScript/TypeScript syntax trees to extract translation keys.
|
|
5
5
|
*
|
|
@@ -90,7 +90,7 @@ export declare class ASTVisitors {
|
|
|
90
90
|
*
|
|
91
91
|
* @private
|
|
92
92
|
*/
|
|
93
|
-
|
|
93
|
+
getVarFromScope(name: string): ScopeInfo | undefined;
|
|
94
94
|
/**
|
|
95
95
|
* Handles variable declarations that might define translation functions.
|
|
96
96
|
*
|
|
@@ -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,EAAgI,MAAM,WAAW,CAAA;AACrK,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAgI,MAAM,WAAW,CAAA;AACrK,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AAUvG;;;;;;;;;;;;;;;;;;;;;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;IAsDZ;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAMrB;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAS5D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAmChC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,8BAA8B;IAwDtC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IAgK5B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IA4DxB;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAkB9B;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IA4IxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,2BAA2B;IA4BnC;;;;;;;OAOG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|
|
@@ -4,9 +4,9 @@ import type { PluginContext, I18nextToolkitConfig } from '../../types';
|
|
|
4
4
|
* Supports extraction from single-line (//) and multi-line comments.
|
|
5
5
|
*
|
|
6
6
|
* @param code - The source code to analyze
|
|
7
|
-
* @param functionNames - Array of function names to look for (e.g., ['t', 'i18n.t'])
|
|
8
7
|
* @param pluginContext - Context object with helper methods to add found keys
|
|
9
8
|
* @param config - Configuration object containing extraction settings
|
|
9
|
+
* @param scopeResolver - Function to resolve scope information for variables (optional)
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```typescript
|
|
@@ -16,9 +16,12 @@ import type { PluginContext, I18nextToolkitConfig } from '../../types';
|
|
|
16
16
|
* `
|
|
17
17
|
*
|
|
18
18
|
* const context = createPluginContext(allKeys)
|
|
19
|
-
* extractKeysFromComments(code,
|
|
19
|
+
* extractKeysFromComments(code, context, config, scopeResolver)
|
|
20
20
|
* // Extracts: user.name and app.title with their respective settings
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function extractKeysFromComments(code: string, pluginContext: PluginContext, config: I18nextToolkitConfig
|
|
23
|
+
export declare function extractKeysFromComments(code: string, pluginContext: PluginContext, config: I18nextToolkitConfig, scopeResolver?: (varName: string) => {
|
|
24
|
+
defaultNs?: string;
|
|
25
|
+
keyPrefix?: string;
|
|
26
|
+
} | undefined): void;
|
|
24
27
|
//# sourceMappingURL=comment-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/comment-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"comment-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/comment-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,oBAAoB,EAC5B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAC1F,IAAI,CA4CN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAY,MAAM,WAAW,CAAA;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGvD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAY,MAAM,WAAW,CAAA;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGvD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,IAAI,CAoJxH"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtractedKey, PluginContext } from '../types';
|
|
1
|
+
import type { ExtractedKey, PluginContext, I18nextToolkitConfig, Logger } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Initializes an array of plugins by calling their setup hooks.
|
|
4
4
|
* This function should be called before starting the extraction process.
|
|
@@ -33,5 +33,5 @@ export declare function initializePlugins(plugins: any[]): Promise<void>;
|
|
|
33
33
|
* })
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
export declare function createPluginContext(allKeys: Map<string, ExtractedKey
|
|
36
|
+
export declare function createPluginContext(allKeys: Map<string, ExtractedKey>, config: I18nextToolkitConfig, logger: Logger): PluginContext;
|
|
37
37
|
//# sourceMappingURL=plugin-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEzF;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAgBpI"}
|
package/types/types.d.ts
CHANGED
|
@@ -309,5 +309,26 @@ export interface PluginContext {
|
|
|
309
309
|
* @param keyInfo - The extracted key information
|
|
310
310
|
*/
|
|
311
311
|
addKey: (keyInfo: ExtractedKey) => void;
|
|
312
|
+
/** The fully resolved i18next-cli configuration. */
|
|
313
|
+
config: I18nextToolkitConfig;
|
|
314
|
+
/** The shared logger instance. */
|
|
315
|
+
logger: Logger;
|
|
316
|
+
/**
|
|
317
|
+
* Retrieves variable information from the current scope chain.
|
|
318
|
+
* Searches from the innermost scope outwards.
|
|
319
|
+
* @param name - The variable name to look up (e.g., 't').
|
|
320
|
+
* @returns Scope information if found, otherwise undefined.
|
|
321
|
+
*/
|
|
322
|
+
getVarFromScope: (name: string) => ScopeInfo | undefined;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Represents variable scope information tracked during AST traversal.
|
|
326
|
+
* Used to maintain context about translation functions and their configuration.
|
|
327
|
+
*/
|
|
328
|
+
export interface ScopeInfo {
|
|
329
|
+
/** Default namespace for translation calls in this scope */
|
|
330
|
+
defaultNs?: string;
|
|
331
|
+
/** Key prefix to prepend to all translation keys in this scope */
|
|
332
|
+
keyPrefix?: string;
|
|
312
333
|
}
|
|
313
334
|
//# sourceMappingURL=types.d.ts.map
|
package/types/types.d.ts.map
CHANGED
|
@@ -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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"}
|