i18next-cli 1.9.0 → 1.10.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 +9 -0
- package/README.md +5 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/parsers/ast-visitors.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/esm/cli.js +1 -1
- package/dist/esm/extractor/parsers/ast-visitors.js +1 -1
- package/dist/esm/extractor/parsers/comment-parser.js +1 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/extractor/parsers/ast-visitors.ts +180 -55
- package/src/extractor/parsers/comment-parser.ts +41 -18
- package/src/extractor/parsers/jsx-parser.ts +16 -1
- package/src/types.ts +4 -1
- package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/comment-parser.d.ts.map +1 -1
- package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
- package/types/types.d.ts +2 -4
- package/types/types.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ 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.10.0](https://github.com/i18next/i18next-cli/compare/v1.9.0...v1.10.0) - 2025-10-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Extractor:** Introduced `disablePlurals` configuration option to disable plural key generation when pluralization is handled by other systems or when you only need the base key for interpolation. When enabled, `t('item', { count: 5 })` will only generate `item` instead of `item_one`, `item_other`, etc. This is useful for projects using external pluralization libraries or custom count handling. [#55](https://github.com/i18next/i18next-cli/issues/55)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **Plugin System:** Fixed incorrect TypeScript type definition for the `keys` argument in the `Plugin.onEnd` hook. The keys parameter now correctly represents a `Map<string, ExtractedKey>` instead of the previous incorrect type, improving type safety for plugin development. [#56](https://github.com/i18next/i18next-cli/pull/56)
|
|
15
|
+
- **Extractor:** Fixed a critical bug where empty string keys (`""`) were being created in translation files under specific conditions. This occurred when namespace processing resulted in empty keys (e.g., `t('ns:')` with `keyPrefix` combinations) or when malformed key patterns created nested empty key structures. The extractor now validates and skips problematic key combinations that would result in empty keys, preventing corrupted translation files. [#54](https://github.com/i18next/i18next-cli/issues/54)
|
|
16
|
+
|
|
8
17
|
## [1.9.0](https://github.com/i18next/i18next-cli/compare/v1.8.0...v1.9.0) - 2025-10-07
|
|
9
18
|
|
|
10
19
|
### Enhanced
|
package/README.md
CHANGED
|
@@ -367,6 +367,11 @@ export default defineConfig({
|
|
|
367
367
|
// When false, t('key', { context: 'male', count: 1 }) will only generate
|
|
368
368
|
// key_male_one, key_male_other but NOT key_one, key_other
|
|
369
369
|
generateBasePluralForms: true, // Default: true
|
|
370
|
+
|
|
371
|
+
// Completely disable plural generation, even when count is present
|
|
372
|
+
// When true, t('key', { count: 1 }) will only generate 'key' (no _one, _other suffixes)
|
|
373
|
+
// The count option can still be used for {{count}} interpolation in the translation value
|
|
374
|
+
disablePlurals: false, // Default: false
|
|
370
375
|
},
|
|
371
376
|
|
|
372
377
|
// TypeScript type generation
|
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.10.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("./jsx-parser.js"),t=require("./ast-utils.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];hooks;objectKeys=new Set;scope=new Map;constructor(e,t,r,s){this.pluginContext=t,this.config=e,this.logger=r,this.hooks={onBeforeVisitNode:s?.onBeforeVisitNode,onAfterVisitNode:s?.onAfterVisitNode,resolvePossibleKeyStringValues:s?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:s?.resolvePossibleContextStringValues}}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),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const r=e[t];if(Array.isArray(r))for(const e of r)e&&"object"==typeof e&&this.walk(e);else r&&"object"==typeof r&&this.walk(r)}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)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const r="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!r)return;const s=r.callee;if("Identifier"===s.type){const t=this.getUseTranslationConfig(s.value);if(t)return this.handleUseTranslationDeclarator(e,r,t),void this.handleUseTranslationForComments(e,r,t)}"MemberExpression"===s.type&&"Identifier"===s.property.type&&"getFixedT"===s.property.value&&this.handleGetFixedTDeclarator(e,r)}handleUseTranslationForComments(e,t,r){let s;if("Identifier"===e.id.type&&(s=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(s=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){s="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){s=t.value.value;break}}if(!s)return;const n=t.arguments?.[r.nsArg]?.expression,i=t.arguments?.[r.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===n?.type?o=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(o=n.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(s,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,r,s){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const i=r.arguments?.[s.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=r.arguments?.[s.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t.getObjectPropValue(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(n,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const r=e.id.value,s=t.arguments,n=s[1]?.expression,i=s[2]?.expression,o="StringLiteral"===n?.type?n.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(r,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const r=this.getFunctionName(e.callee);if(!r)return;const s=this.getVarFromScope(r),n=this.config.extract.functions||["t","*.t"];let i=void 0!==s;if(!i)for(const e of n)if(e.startsWith("*.")){if(r.endsWith(e.substring(1))){i=!0;break}}else if(e===r){i=!0;break}if(!i||0===e.arguments.length)return;const{keysToProcess:o,isSelectorAPI:a}=this.handleCallExpressionArgument(e,0);if(0===o.length)return;let l=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${u}ordinal`)&&(l=!0,o[e]=o[e].slice(0,-8));let p,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(p=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const f=c?t.getObjectPropValue(c,"defaultValue"):void 0,y="string"==typeof f?f:p;for(let e=0;e<o.length;e++){let r,n=o[e];if(c){const e=t.getObjectPropValue(c,"ns");"string"==typeof e&&(r=e)}const i=this.config.extract.nsSeparator??":";if(!r&&i&&n.includes(i)){const e=n.split(i);r=e.shift(),n=e.join(i)}!r&&s?.defaultNs&&(r=s.defaultNs),r||(r=this.config.extract.defaultNS);let u=n;if(s?.keyPrefix){const e=this.config.extract.keySeparator??".";u=`${s.keyPrefix}${e}${n}`}const p=e===o.length-1&&y||n;if(c){const e=t.getObjectProperty(c,"context"),s=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,n=this.config.extract.contextSeparator??"_";""!==t&&s.push({key:`${u}${n}${t}`,ns:r,defaultValue:p})}else if(e?.value){const t=this.resolvePossibleContextStringValues(e.value),n=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{s.push({key:`${u}${n}${e}`,ns:r,defaultValue:p})}),s.push({key:u,ns:r,defaultValue:p}))}const n=void 0!==t.getObjectPropValue(c,"count"),i=!0===t.getObjectPropValue(c,"ordinal");if(n||l){if(s.length>0)for(const{key:e,ns:t}of s)this.handlePluralKeys(e,t,c,i||l,y);else this.handlePluralKeys(u,r,c,i||l,y);continue}if(s.length>0){s.forEach(this.pluginContext.addKey);continue}!0===t.getObjectPropValue(c,"returnObjects")&&this.objectKeys.add(u)}a&&this.objectKeys.add(u),this.pluginContext.addKey({key:u,ns:r,defaultValue:p})}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,s=[];let n=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(s.push(e),n=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&s.push(...this.resolvePossibleKeyStringValues(e.expression));else s.push(...this.resolvePossibleKeyStringValues(r));return{keysToProcess:s.filter(e=>!!e),isSelectorAPI:n}}handlePluralKeys(e,r,s,n,i){try{const o=n?"ordinal":"cardinal",a=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}const l=Array.from(a).sort(),u=this.config.extract.pluralSeparator??"_",p=t.getObjectPropValue(s,"defaultValue"),c=t.getObjectPropValue(s,`defaultValue${u}other`),f=t.getObjectPropValue(s,`defaultValue${u}ordinal${u}other`),y=t.getObjectPropValue(s,"count");let g;if("number"==typeof y)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";g=new Intl.PluralRules(e,{type:o}).select(y)}catch(e){}const h=t.getObjectPropValue(s,"context"),d="string"==typeof h&&h.length>0,x=[];if(d){x.push({key:e,context:h});!1!==this.config.extract?.generateBasePluralForms&&x.push({key:e})}else x.push({key:e});for(const{key:e,context:o}of x)for(const a of l){const l=n?`defaultValue${u}ordinal${u}${a}`:`defaultValue${u}${a}`,y=t.getObjectPropValue(s,l);let h,d;h="string"==typeof y?y:"one"===a&&"string"==typeof p?p:n&&"string"==typeof f?f:n||"string"!=typeof c?"string"==typeof p?p:i&&g===a?i:e:c,d=o?n?`${e}${u}${o}${u}ordinal${u}${a}`:`${e}${u}${o}${u}${a}`:n?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`,this.pluginContext.addKey({key:d,ns:r,defaultValue:h,hasCount:!0,isOrdinal:n})}}catch(n){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const o=i||t.getObjectPropValue(s,"defaultValue");this.pluginContext.addKey({key:e,ns:r,defaultValue:"string"==typeof o?o:e})}}handleJSXElement(t){const r=this.getElementName(t);if(r&&(this.config.extract.transComponents||["Trans"]).includes(r)){const r=e.extractFromTransComponent(t,this.config),s=[];if(r){if(r.keyExpression){const e=this.resolvePossibleKeyStringValues(r.keyExpression);s.push(...e)}else s.push(r.serializedChildren);let e;const{contextExpression:n,optionsNode:i,defaultValue:o,hasCount:a,isOrdinal:l,serializedChildren:u}=r;if(r.ns){const{ns:t}=r;e=s.map(e=>({key:e,ns:t,defaultValue:o||u,hasCount:a,isOrdinal:l}))}else{e=s.map(e=>{const t=this.config.extract.nsSeparator??":";let r;if(t&&e.includes(t)){let s;[r,...s]=e.split(t),e=s.join(t)}return{key:e,ns:r,defaultValue:o||u,hasCount:a,isOrdinal:l}});const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===r?.type&&"JSXExpressionContainer"===r.value?.type&&"Identifier"===r.value.expression.type){const t=r.value.expression.value,s=this.getVarFromScope(t);s?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=s.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),n&&a){const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!r,o=this.resolvePossibleContextStringValues(n),a=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i));for(const t of o)for(const r of e){const e=`${r.key}${a}${t}`;this.generatePluralKeysForTrans(e,r.defaultValue,r.ns,s,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i))}else if(n){const t=this.resolvePossibleContextStringValues(n),r=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:n,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${r}${s}`,ns:n,defaultValue:i});"StringLiteral"!==n.type&&e.forEach(this.pluginContext.addKey)}}else if(a){const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!r;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i))}else e.forEach(this.pluginContext.addKey)}}}generatePluralKeysForTrans(e,r,s,n,i){try{const o=n?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t.getObjectPropValue(i,`defaultValue${l}other`),p=t.getObjectPropValue(i,`defaultValue${l}ordinal${l}other`));for(const o of a){const a=n?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`,c=i?t.getObjectPropValue(i,a):void 0;let f;f="string"==typeof c?c:"one"===o&&"string"==typeof r?r:n&&"string"==typeof p?p:n||"string"!=typeof u?"string"==typeof r?r:e:u;const y=n?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:f,hasCount:!0,isOrdinal:n})}}catch(t){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,ns:s,defaultValue:r})}}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 r=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&r.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&r.unshift(t.value),r.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let r=t;const s=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)s.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;s.unshift(e.expression.value)}r=r.object}if(s.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return s.join(t)}return null}resolvePossibleContextStringValues(e){return[...this.hooks.resolvePossibleContextStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleKeyStringValues(e){return[...this.hooks.resolvePossibleKeyStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleStringValuesFromExpression(e,t=!1){if("StringLiteral"===e.type)return e.value||t?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValuesFromExpression(e.consequent,t),...this.resolvePossibleStringValuesFromExpression(e.alternate,t)]}if("Identifier"===e.type&&"undefined"===e.value)return[];if("TemplateLiteral"===e.type)return this.resolvePossibleStringValuesFromTemplateString(e);if("NumericLiteral"===e.type||"BooleanLiteral"===e.type)return[`${e.value}`];if("TsSatisfiesExpression"===e.type||"TsAsExpression"===e.type){const r=e.typeAnnotation;return this.resolvePossibleStringValuesFromType(r,t)}return[]}resolvePossibleStringValuesFromType(e,t=!1){if("TsUnionType"===e.type)return e.types.flatMap(e=>this.resolvePossibleStringValuesFromType(e,t));if("TsLiteralType"===e.type){if("StringLiteral"===e.literal.type)return e.literal.value||t?[e.literal.value]:[];if("TemplateLiteral"===e.literal.type)return this.resolvePossibleStringValuesFromTemplateLiteralType(e.literal);if("NumericLiteral"===e.literal.type||"BooleanLiteral"===e.literal.type)return[`${e.literal.value}`]}return[]}resolvePossibleStringValuesFromTemplateString(e){if(1===e.quasis.length&&0===e.expressions.length)return[e.quasis[0].cooked||""];const[t,...r]=e.quasis;return e.expressions.reduce((e,t,s)=>e.flatMap(e=>{const n=r[s]?.cooked??"";return this.resolvePossibleStringValuesFromExpression(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}resolvePossibleStringValuesFromTemplateLiteralType(e){if(1===e.quasis.length&&0===e.types.length)return[e.quasis[0].cooked||""];const[t,...r]=e.quasis;return e.types.reduce((e,t,s)=>e.flatMap(e=>{const n=r[s]?.cooked??"";return this.resolvePossibleStringValuesFromType(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const r of t){if("string"==typeof r&&r===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof r&&r.name===e)return{name:r.name,nsArg:r.nsArg??0,keyPrefixArg:r.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}};
|
|
1
|
+
"use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];hooks;objectKeys=new Set;scope=new Map;constructor(e,t,s,r){this.pluginContext=t,this.config=e,this.logger=s,this.hooks={onBeforeVisitNode:r?.onBeforeVisitNode,onAfterVisitNode:r?.onAfterVisitNode,resolvePossibleKeyStringValues:r?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:r?.resolvePossibleContextStringValues}}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),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const s=e[t];if(Array.isArray(s))for(const e of s)e&&"object"==typeof e&&this.walk(e);else s&&"object"==typeof s&&this.walk(s)}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)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const s="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!s)return;const r=s.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return this.handleUseTranslationDeclarator(e,s,t),void this.handleUseTranslationForComments(e,s,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,s)}handleUseTranslationForComments(e,t,s){let r;if("Identifier"===e.id.type&&(r=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(r=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){r="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){r=t.value.value;break}}if(!r)return;const n=t.arguments?.[s.nsArg]?.expression,i=t.arguments?.[s.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===n?.type?o=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(o=n.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(r,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,s,r){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const i=s.arguments?.[r.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=s.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t.getObjectPropValue(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(n,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const s=e.id.value,r=t.arguments,n=r[1]?.expression,i=r[2]?.expression,o="StringLiteral"===n?.type?n.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(s,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const s=this.getFunctionName(e.callee);if(!s)return;const r=this.getVarFromScope(s),n=this.config.extract.functions||["t","*.t"];let i=void 0!==r;if(!i)for(const e of n)if(e.startsWith("*.")){if(s.endsWith(e.substring(1))){i=!0;break}}else if(e===s){i=!0;break}if(!i||0===e.arguments.length)return;const{keysToProcess:o,isSelectorAPI:a}=this.handleCallExpressionArgument(e,0);if(0===o.length)return;let l=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${u}ordinal`)&&(l=!0,o[e]=o[e].slice(0,-8));let p,f;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?f=t:"StringLiteral"===t.type&&(p=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(f=t)}const c=f?t.getObjectPropValue(f,"defaultValue"):void 0,y="string"==typeof c?c:p;for(let e=0;e<o.length;e++){let s,n=o[e];if(f){const e=t.getObjectPropValue(f,"ns");"string"==typeof e&&(s=e)}const i=this.config.extract.nsSeparator??":";if(!s&&i&&n.includes(i)){const e=n.split(i);if(s=e.shift(),n=e.join(i),!n||""===n.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${s}${i}'`);continue}}!s&&r?.defaultNs&&(s=r.defaultNs),s||(s=this.config.extract.defaultNS);let u=n;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";if(u=!1!==e?r.keyPrefix.endsWith(e)?`${r.keyPrefix}${n}`:`${r.keyPrefix}${e}${n}`:`${r.keyPrefix}${n}`,!1!==e){if(u.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${u}' (keyPrefix: '${r.keyPrefix}', key: '${n}')`);continue}}}const p=e===o.length-1&&y||n;if(f){const e=t.getObjectProperty(f,"context"),r=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,n=this.config.extract.contextSeparator??"_";""!==t&&r.push({key:`${u}${n}${t}`,ns:s,defaultValue:p})}else if(e?.value){const t=this.resolvePossibleContextStringValues(e.value),n=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{r.push({key:`${u}${n}${e}`,ns:s,defaultValue:p})}),r.push({key:u,ns:s,defaultValue:p}))}const n=void 0!==t.getObjectPropValue(f,"count"),i=!0===t.getObjectPropValue(f,"ordinal");if(n||l){this.config.extract.disablePlurals?r.length>0?r.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:u,ns:s,defaultValue:p}):this.handlePluralKeys(u,s,f,i||l,y);continue}if(r.length>0){r.forEach(this.pluginContext.addKey);continue}!0===t.getObjectPropValue(f,"returnObjects")&&this.objectKeys.add(u)}a&&this.objectKeys.add(u),this.pluginContext.addKey({key:u,ns:s,defaultValue:p})}}handleCallExpressionArgument(e,t){const s=e.arguments[t].expression,r=[];let n=!1;if("ArrowFunctionExpression"===s.type){const e=this.extractKeyFromSelector(s);e&&(r.push(e),n=!0)}else if("ArrayExpression"===s.type)for(const e of s.elements)e?.expression&&r.push(...this.resolvePossibleKeyStringValues(e.expression));else r.push(...this.resolvePossibleKeyStringValues(s));return{keysToProcess:r.filter(e=>!!e),isSelectorAPI:n}}handlePluralKeys(e,s,r,n,i){try{const o=n?"ordinal":"cardinal",a=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}const l=Array.from(a).sort(),u=this.config.extract.pluralSeparator??"_",p=t.getObjectPropValue(r,"defaultValue"),f=t.getObjectPropValue(r,`defaultValue${u}other`),c=t.getObjectPropValue(r,`defaultValue${u}ordinal${u}other`),y=t.getObjectPropValue(r,"count");let g;if("number"==typeof y)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";g=new Intl.PluralRules(e,{type:o}).select(y)}catch(e){}const d=t.getObjectProperty(r,"context"),h=[];if(d?.value){const t=this.resolvePossibleContextStringValues(d.value);if(t.length>0){for(const s of t)s.length>0&&h.push({key:e,context:s});!1!==this.config.extract?.generateBasePluralForms&&h.push({key:e})}else h.push({key:e})}else h.push({key:e});for(const{key:e,context:o}of h)for(const a of l){const l=n?`defaultValue${u}ordinal${u}${a}`:`defaultValue${u}${a}`,y=t.getObjectPropValue(r,l);let d,h;if(d="string"==typeof y?y:"one"===a&&"string"==typeof p?p:n&&"string"==typeof c?c:n||"string"!=typeof f?"string"==typeof p?p:i&&g===a?i:e:f,o){const t=this.config.extract.contextSeparator??"_";h=n?`${e}${t}${o}${u}ordinal${u}${a}`:`${e}${t}${o}${u}${a}`}else h=n?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;this.pluginContext.addKey({key:h,ns:s,defaultValue:d,hasCount:!0,isOrdinal:n})}}catch(n){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const o=i||t.getObjectPropValue(r,"defaultValue");this.pluginContext.addKey({key:e,ns:s,defaultValue:"string"==typeof o?o:e})}}handleJSXElement(t){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e.extractFromTransComponent(t,this.config),r=[];if(s){if(s.keyExpression){const e=this.resolvePossibleKeyStringValues(s.keyExpression);r.push(...e)}else r.push(s.serializedChildren);let e;const{contextExpression:n,optionsNode:i,defaultValue:o,hasCount:a,isOrdinal:l,serializedChildren:u}=s;if(s.ns){const{ns:t}=s;e=r.map(e=>({key:e,ns:t,defaultValue:o||u,hasCount:a,isOrdinal:l}))}else{e=r.map(e=>{const t=this.config.extract.nsSeparator??":";let s;if(t&&e.includes(t)){let r;[s,...r]=e.split(t),e=r.join(t)}return{key:e,ns:s,defaultValue:o||u,hasCount:a,isOrdinal:l}});const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===s?.type&&"JSXExpressionContainer"===s.value?.type&&"Identifier"===s.value.expression.type){const t=s.value.expression.value,r=this.getVarFromScope(t);r?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=r.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),n&&a)if(this.config.extract.disablePlurals){const t=this.resolvePossibleContextStringValues(n),s=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===n.type)for(const r of t)for(const t of e){const e=`${t.key}${s}${r}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});for(const r of t)for(const t of e){const e=`${t.key}${s}${r}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else{const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!s,o=this.resolvePossibleContextStringValues(n),a=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i));for(const t of o)for(const s of e){const e=`${s.key}${a}${t}`;this.generatePluralKeysForTrans(e,s.defaultValue,s.ns,r,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else if(n){const t=this.resolvePossibleContextStringValues(n),s=this.config.extract.contextSeparator??"_";if(t.length>0){for(const r of t)for(const{key:t,ns:n,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${s}${r}`,ns:n,defaultValue:i});"StringLiteral"!==n.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else if(a)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});else{const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!s;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}}}generatePluralKeysForTrans(e,s,r,n,i){try{const o=n?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t.getObjectPropValue(i,`defaultValue${l}other`),p=t.getObjectPropValue(i,`defaultValue${l}ordinal${l}other`));for(const o of a){const a=n?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`,f=i?t.getObjectPropValue(i,a):void 0;let c;c="string"==typeof f?f:"one"===o&&"string"==typeof s?s:n&&"string"==typeof p?p:n||"string"!=typeof u?"string"==typeof s?s:e:u;const y=n?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:y,ns:r,defaultValue:c,hasCount:!0,isOrdinal:n})}}catch(t){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,ns:r,defaultValue:s})}}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 s=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&s.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&s.unshift(t.value),s.join(".")}}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 s=t;const r=[];for(;"MemberExpression"===s.type;){const e=s.property;if("Identifier"===e.type)r.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;r.unshift(e.expression.value)}s=s.object}if(r.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return r.join(t)}return null}resolvePossibleContextStringValues(e){return[...this.hooks.resolvePossibleContextStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleKeyStringValues(e){return[...this.hooks.resolvePossibleKeyStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleStringValuesFromExpression(e,t=!1){if("StringLiteral"===e.type)return e.value||t?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValuesFromExpression(e.consequent,t),...this.resolvePossibleStringValuesFromExpression(e.alternate,t)]}if("Identifier"===e.type&&"undefined"===e.value)return[];if("TemplateLiteral"===e.type)return this.resolvePossibleStringValuesFromTemplateString(e);if("NumericLiteral"===e.type||"BooleanLiteral"===e.type)return[`${e.value}`];if("TsSatisfiesExpression"===e.type||"TsAsExpression"===e.type){const s=e.typeAnnotation;return this.resolvePossibleStringValuesFromType(s,t)}return[]}resolvePossibleStringValuesFromType(e,t=!1){if("TsUnionType"===e.type)return e.types.flatMap(e=>this.resolvePossibleStringValuesFromType(e,t));if("TsLiteralType"===e.type){if("StringLiteral"===e.literal.type)return e.literal.value||t?[e.literal.value]:[];if("TemplateLiteral"===e.literal.type)return this.resolvePossibleStringValuesFromTemplateLiteralType(e.literal);if("NumericLiteral"===e.literal.type||"BooleanLiteral"===e.literal.type)return[`${e.literal.value}`]}return[]}resolvePossibleStringValuesFromTemplateString(e){if(1===e.quasis.length&&0===e.expressions.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.expressions.reduce((e,t,r)=>e.flatMap(e=>{const n=s[r]?.cooked??"";return this.resolvePossibleStringValuesFromExpression(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}resolvePossibleStringValuesFromTemplateLiteralType(e){if(1===e.quasis.length&&0===e.types.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.types.reduce((e,t,r)=>e.flatMap(e=>{const n=s[r]?.cooked??"";return this.resolvePossibleStringValuesFromType(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const s of t){if("string"==typeof s&&s===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof s&&s.name===e)return{name:s.name,nsArg:s.nsArg??0,keyPrefixArg:s.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let s=e;for(;"MemberExpression"===s.type;){if("Identifier"!==s.property.type)return null;t.unshift(s.property.value),s=s.object}if("ThisExpression"===s.type)t.unshift("this");else{if("Identifier"!==s.type)return null;t.unshift(s.value)}return t.join(".")}return null}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";function e(e,t,n,s,a,r=!1){try{const o=r?"ordinal":"cardinal",l=new Set;for(const e of a.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}const c=Array.from(l).sort(),u=a.extract.pluralSeparator??"_";for(const a of c){const o=r?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;s.addKey({key:o,ns:n,defaultValue:t,hasCount:!0,isOrdinal:r})}}catch(a){s.addKey({key:e,ns:n,defaultValue:t})}}function t(e,t,n,s,a,r,o=!1){try{const l=o?"ordinal":"cardinal",c=new Set;for(const e of r.locales)try{const t=new Intl.PluralRules(e,{type:l});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}catch(e){const t=new Intl.PluralRules(r.extract.primaryLanguage||"en",{type:l});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}const u=Array.from(c).sort(),d=r.extract.pluralSeparator??"_";for(const r of u){const l=o?`${e}_${s}${d}ordinal${d}${r}`:`${e}_${s}${d}${r}`;a.addKey({key:l,ns:n,defaultValue:t,hasCount:!0,isOrdinal:o})}}catch(r){a.addKey({key:`${e}_${s}`,ns:n,defaultValue:t})}}function n(e){const t=/^\s*,\s*(['"])(.*?)\1/.exec(e);if(t)return t[2];const n=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(e);return n?n[2]:void 0}function s(e){const t=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function a(e){const t=/^\s*,\s*\{[^}]*context\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function r(e){const t=/^\s*,\s*\{[^}]*count\s*:\s*(\d+)/.exec(e);if(t)return parseInt(t[1],10)}function o(e){const t=/^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(e);if(t)return"true"===t[1]}exports.extractKeysFromComments=function(l,c,u,d){const i=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(e){const t=[],n=new Set,s=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let a;for(;null!==(a=s.exec(e));){const e=(a[1]??a[2]).trim();e&&!n.has(e)&&(n.add(e),t.push(e))}return t}(l);for(const l of f){let f;for(;null!==(f=i.exec(l));){let i,y=f[2];const p=l.slice(f.index+f[0].length),$=n(p),x=a(p),h=r(p),g=o(p);let
|
|
1
|
+
"use strict";function e(e,t,n,s,a,r=!1){try{const o=r?"ordinal":"cardinal",l=new Set;for(const e of a.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}const c=Array.from(l).sort(),u=a.extract.pluralSeparator??"_";for(const a of c){const o=r?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;s.addKey({key:o,ns:n,defaultValue:t,hasCount:!0,isOrdinal:r})}}catch(a){s.addKey({key:e,ns:n,defaultValue:t})}}function t(e,t,n,s,a,r,o=!1){try{const l=o?"ordinal":"cardinal",c=new Set;for(const e of r.locales)try{const t=new Intl.PluralRules(e,{type:l});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}catch(e){const t=new Intl.PluralRules(r.extract.primaryLanguage||"en",{type:l});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}const u=Array.from(c).sort(),d=r.extract.pluralSeparator??"_";for(const r of u){const l=o?`${e}_${s}${d}ordinal${d}${r}`:`${e}_${s}${d}${r}`;a.addKey({key:l,ns:n,defaultValue:t,hasCount:!0,isOrdinal:o})}}catch(r){a.addKey({key:`${e}_${s}`,ns:n,defaultValue:t})}}function n(e){const t=/^\s*,\s*(['"])(.*?)\1/.exec(e);if(t)return t[2];const n=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(e);return n?n[2]:void 0}function s(e){const t=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function a(e){const t=/^\s*,\s*\{[^}]*context\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function r(e){const t=/^\s*,\s*\{[^}]*count\s*:\s*(\d+)/.exec(e);if(t)return parseInt(t[1],10)}function o(e){const t=/^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(e);if(t)return"true"===t[1]}exports.extractKeysFromComments=function(l,c,u,d){const i=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(e){const t=[],n=new Set,s=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let a;for(;null!==(a=s.exec(e));){const e=(a[1]??a[2]).trim();e&&!n.has(e)&&(n.add(e),t.push(e))}return t}(l);for(const l of f){let f;for(;null!==(f=i.exec(l));){let i,y=f[2];if(!y||""===y.trim())continue;const p=l.slice(f.index+f[0].length),$=n(p),x=a(p),h=r(p),g=o(p);let m=!1;const K=u.extract.pluralSeparator??"_";if(y.endsWith(`${K}ordinal`)&&(m=!0,y=y.slice(0,-(K.length+7)),!y||""===y.trim()))continue;const V=!0===g||m;i=s(p);const k=u.extract.nsSeparator??":";if(!i&&k&&y.includes(k)){const e=y.split(k);i=e.shift(),y=e.join(k)}if(!i&&d){const e=d("t");e?.defaultNs&&(i=e.defaultNs)}if(i||(i=u.extract.defaultNS),u.extract.disablePlurals)x?c.addKey({key:`${y}_${x}`,ns:i,defaultValue:$??y}):c.addKey({key:y,ns:i,defaultValue:$??y});else if(x&&h){t(y,$??y,i,x,c,u,V);!1!==u.extract?.generateBasePluralForms&&e(y,$??y,i,c,u,V)}else x?(c.addKey({key:y,ns:i,defaultValue:$??y}),c.addKey({key:`${y}_${x}`,ns:i,defaultValue:$??y})):h?e(y,$??y,i,c,u,V):c.addKey({key:y,ns:i,defaultValue:$??y})}}};
|
|
@@ -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
|
|
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 o=!!a||!!p,l=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),u="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,c=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let f="JSXAttribute"===c?.type&&"JSXExpressionContainer"===c.value?.type?c.value.expression:"JSXAttribute"===c?.type&&"StringLiteral"===c.value?.type?c.value:void 0;const d=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let m;if(m="JSXAttribute"===d?.type&&"StringLiteral"===d.value?.type?d.value.value:void 0,u&&(void 0===m&&(m=e.getObjectPropValue(u,"ns")),void 0===f)){const t=e.getObjectProperty(u,"context");t?.value&&(f=t.value)}const S=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 b,g,x;if("JSXAttribute"===r?.type&&"StringLiteral"===r.value?.type)b=r.value.value;else{const e=n.extract.defaultValue;b="string"==typeof e?e:""}if("JSXAttribute"===i?.type){if("StringLiteral"===i.value?.type){if(g=i.value,x=g.value,!x||""===x.trim())return console.warn("Ignoring Trans component with empty i18nKey"),null;if(m&&"StringLiteral"===g.type){const e=n.extract.nsSeparator??":",t=g.value;if(e&&t.startsWith(`${m}${e}`)){if(x=t.slice(`${m}${e}`.length),!x||""===x.trim())return console.warn("Ignoring Trans component with i18nKey that becomes empty after namespace removal"),null;g={...g,value:x}}}}else"JSXExpressionContainer"===i.value?.type&&"JSXEmptyExpression"!==i.value.expression.type&&(g=i.value.expression);if(!g)return null}return r||!x||S.trim()?!r&&S.trim()&&(b=S):b=x,{keyExpression:g,serializedChildren:S,ns:m,defaultValue:b,hasCount:o,isOrdinal:v,contextExpression:f,optionsNode:u}};
|
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.10.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{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t,getObjectProperty as s}from"./ast-utils.js";class r{pluginContext;config;logger;scopeStack=[];hooks;objectKeys=new Set;scope=new Map;constructor(e,t,s,r){this.pluginContext=t,this.config=e,this.logger=s,this.hooks={onBeforeVisitNode:r?.onBeforeVisitNode,onAfterVisitNode:r?.onAfterVisitNode,resolvePossibleKeyStringValues:r?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:r?.resolvePossibleContextStringValues}}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),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const s=e[t];if(Array.isArray(s))for(const e of s)e&&"object"==typeof e&&this.walk(e);else s&&"object"==typeof s&&this.walk(s)}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)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const s="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!s)return;const r=s.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return this.handleUseTranslationDeclarator(e,s,t),void this.handleUseTranslationForComments(e,s,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,s)}handleUseTranslationForComments(e,t,s){let r;if("Identifier"===e.id.type&&(r=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(r=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){r="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){r=t.value.value;break}}if(!r)return;const n=t.arguments?.[s.nsArg]?.expression,i=t.arguments?.[s.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===n?.type?o=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(o=n.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(r,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,s,r){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const i=s.arguments?.[r.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=s.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(n,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const s=e.id.value,r=t.arguments,n=r[1]?.expression,i=r[2]?.expression,o="StringLiteral"===n?.type?n.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(s,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const r=this.getFunctionName(e.callee);if(!r)return;const n=this.getVarFromScope(r),i=this.config.extract.functions||["t","*.t"];let o=void 0!==n;if(!o)for(const e of i)if(e.startsWith("*.")){if(r.endsWith(e.substring(1))){o=!0;break}}else if(e===r){o=!0;break}if(!o||0===e.arguments.length)return;const{keysToProcess:a,isSelectorAPI:l}=this.handleCallExpressionArgument(e,0);if(0===a.length)return;let u=!1;const p=this.config.extract.pluralSeparator??"_";for(let e=0;e<a.length;e++)a[e].endsWith(`${p}ordinal`)&&(u=!0,a[e]=a[e].slice(0,-8));let f,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(f=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const y=c?t(c,"defaultValue"):void 0,g="string"==typeof y?y:f;for(let e=0;e<a.length;e++){let r,i=a[e];if(c){const e=t(c,"ns");"string"==typeof e&&(r=e)}const o=this.config.extract.nsSeparator??":";if(!r&&o&&i.includes(o)){const e=i.split(o);r=e.shift(),i=e.join(o)}!r&&n?.defaultNs&&(r=n.defaultNs),r||(r=this.config.extract.defaultNS);let p=i;if(n?.keyPrefix){const e=this.config.extract.keySeparator??".";p=`${n.keyPrefix}${e}${i}`}const f=e===a.length-1&&g||i;if(c){const e=s(c,"context"),n=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,s=this.config.extract.contextSeparator??"_";""!==t&&n.push({key:`${p}${s}${t}`,ns:r,defaultValue:f})}else if(e?.value){const t=this.resolvePossibleContextStringValues(e.value),s=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{n.push({key:`${p}${s}${e}`,ns:r,defaultValue:f})}),n.push({key:p,ns:r,defaultValue:f}))}const i=void 0!==t(c,"count"),o=!0===t(c,"ordinal");if(i||u){if(n.length>0)for(const{key:e,ns:t}of n)this.handlePluralKeys(e,t,c,o||u,g);else this.handlePluralKeys(p,r,c,o||u,g);continue}if(n.length>0){n.forEach(this.pluginContext.addKey);continue}!0===t(c,"returnObjects")&&this.objectKeys.add(p)}l&&this.objectKeys.add(p),this.pluginContext.addKey({key:p,ns:r,defaultValue:f})}}handleCallExpressionArgument(e,t){const s=e.arguments[t].expression,r=[];let n=!1;if("ArrowFunctionExpression"===s.type){const e=this.extractKeyFromSelector(s);e&&(r.push(e),n=!0)}else if("ArrayExpression"===s.type)for(const e of s.elements)e?.expression&&r.push(...this.resolvePossibleKeyStringValues(e.expression));else r.push(...this.resolvePossibleKeyStringValues(s));return{keysToProcess:r.filter(e=>!!e),isSelectorAPI:n}}handlePluralKeys(e,s,r,n,i){try{const o=n?"ordinal":"cardinal",a=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>a.add(e))}const l=Array.from(a).sort(),u=this.config.extract.pluralSeparator??"_",p=t(r,"defaultValue"),f=t(r,`defaultValue${u}other`),c=t(r,`defaultValue${u}ordinal${u}other`),y=t(r,"count");let g;if("number"==typeof y)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";g=new Intl.PluralRules(e,{type:o}).select(y)}catch(e){}const h=t(r,"context"),d="string"==typeof h&&h.length>0,x=[];if(d){x.push({key:e,context:h});!1!==this.config.extract?.generateBasePluralForms&&x.push({key:e})}else x.push({key:e});for(const{key:e,context:o}of x)for(const a of l){const l=t(r,n?`defaultValue${u}ordinal${u}${a}`:`defaultValue${u}${a}`);let y,h;y="string"==typeof l?l:"one"===a&&"string"==typeof p?p:n&&"string"==typeof c?c:n||"string"!=typeof f?"string"==typeof p?p:i&&g===a?i:e:f,h=o?n?`${e}${u}${o}${u}ordinal${u}${a}`:`${e}${u}${o}${u}${a}`:n?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`,this.pluginContext.addKey({key:h,ns:s,defaultValue:y,hasCount:!0,isOrdinal:n})}}catch(n){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const o=i||t(r,"defaultValue");this.pluginContext.addKey({key:e,ns:s,defaultValue:"string"==typeof o?o:e})}}handleJSXElement(t){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e(t,this.config),r=[];if(s){if(s.keyExpression){const e=this.resolvePossibleKeyStringValues(s.keyExpression);r.push(...e)}else r.push(s.serializedChildren);let e;const{contextExpression:n,optionsNode:i,defaultValue:o,hasCount:a,isOrdinal:l,serializedChildren:u}=s;if(s.ns){const{ns:t}=s;e=r.map(e=>({key:e,ns:t,defaultValue:o||u,hasCount:a,isOrdinal:l}))}else{e=r.map(e=>{const t=this.config.extract.nsSeparator??":";let s;if(t&&e.includes(t)){let r;[s,...r]=e.split(t),e=r.join(t)}return{key:e,ns:s,defaultValue:o||u,hasCount:a,isOrdinal:l}});const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===s?.type&&"JSXExpressionContainer"===s.value?.type&&"Identifier"===s.value.expression.type){const t=s.value.expression.value,r=this.getVarFromScope(t);r?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=r.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),n&&a){const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!s,o=this.resolvePossibleContextStringValues(n),a=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i));for(const t of o)for(const s of e){const e=`${s.key}${a}${t}`;this.generatePluralKeysForTrans(e,s.defaultValue,s.ns,r,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else if(n){const t=this.resolvePossibleContextStringValues(n),s=this.config.extract.contextSeparator??"_";if(t.length>0){for(const r of t)for(const{key:t,ns:n,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${s}${r}`,ns:n,defaultValue:i});"StringLiteral"!==n.type&&e.forEach(this.pluginContext.addKey)}}else if(a){const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!s;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else e.forEach(this.pluginContext.addKey)}}}generatePluralKeysForTrans(e,s,r,n,i){try{const o=n?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t(i,`defaultValue${l}other`),p=t(i,`defaultValue${l}ordinal${l}other`));for(const o of a){const a=i?t(i,n?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`):void 0;let f;f="string"==typeof a?a:"one"===o&&"string"==typeof s?s:n&&"string"==typeof p?p:n||"string"!=typeof u?"string"==typeof s?s:e:u;const c=n?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:c,ns:r,defaultValue:f,hasCount:!0,isOrdinal:n})}}catch(t){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,ns:r,defaultValue:s})}}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 s=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&s.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&s.unshift(t.value),s.join(".")}}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 s=t;const r=[];for(;"MemberExpression"===s.type;){const e=s.property;if("Identifier"===e.type)r.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;r.unshift(e.expression.value)}s=s.object}if(r.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return r.join(t)}return null}resolvePossibleContextStringValues(e){return[...this.hooks.resolvePossibleContextStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleKeyStringValues(e){return[...this.hooks.resolvePossibleKeyStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleStringValuesFromExpression(e,t=!1){if("StringLiteral"===e.type)return e.value||t?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValuesFromExpression(e.consequent,t),...this.resolvePossibleStringValuesFromExpression(e.alternate,t)]}if("Identifier"===e.type&&"undefined"===e.value)return[];if("TemplateLiteral"===e.type)return this.resolvePossibleStringValuesFromTemplateString(e);if("NumericLiteral"===e.type||"BooleanLiteral"===e.type)return[`${e.value}`];if("TsSatisfiesExpression"===e.type||"TsAsExpression"===e.type){const s=e.typeAnnotation;return this.resolvePossibleStringValuesFromType(s,t)}return[]}resolvePossibleStringValuesFromType(e,t=!1){if("TsUnionType"===e.type)return e.types.flatMap(e=>this.resolvePossibleStringValuesFromType(e,t));if("TsLiteralType"===e.type){if("StringLiteral"===e.literal.type)return e.literal.value||t?[e.literal.value]:[];if("TemplateLiteral"===e.literal.type)return this.resolvePossibleStringValuesFromTemplateLiteralType(e.literal);if("NumericLiteral"===e.literal.type||"BooleanLiteral"===e.literal.type)return[`${e.literal.value}`]}return[]}resolvePossibleStringValuesFromTemplateString(e){if(1===e.quasis.length&&0===e.expressions.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.expressions.reduce((e,t,r)=>e.flatMap(e=>{const n=s[r]?.cooked??"";return this.resolvePossibleStringValuesFromExpression(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}resolvePossibleStringValuesFromTemplateLiteralType(e){if(1===e.quasis.length&&0===e.types.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.types.reduce((e,t,r)=>e.flatMap(e=>{const n=s[r]?.cooked??"";return this.resolvePossibleStringValuesFromType(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const s of t){if("string"==typeof s&&s===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof s&&s.name===e)return{name:s.name,nsArg:s.nsArg??0,keyPrefixArg:s.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let s=e;for(;"MemberExpression"===s.type;){if("Identifier"!==s.property.type)return null;t.unshift(s.property.value),s=s.object}if("ThisExpression"===s.type)t.unshift("this");else{if("Identifier"!==s.type)return null;t.unshift(s.value)}return t.join(".")}return null}}export{r as ASTVisitors};
|
|
1
|
+
import{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t,getObjectProperty as s}from"./ast-utils.js";class n{pluginContext;config;logger;scopeStack=[];hooks;objectKeys=new Set;scope=new Map;constructor(e,t,s,n){this.pluginContext=t,this.config=e,this.logger=s,this.hooks={onBeforeVisitNode:n?.onBeforeVisitNode,onAfterVisitNode:n?.onAfterVisitNode,resolvePossibleKeyStringValues:n?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:n?.resolvePossibleContextStringValues}}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),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const s=e[t];if(Array.isArray(s))for(const e of s)e&&"object"==typeof e&&this.walk(e);else s&&"object"==typeof s&&this.walk(s)}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)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const s="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!s)return;const n=s.callee;if("Identifier"===n.type){const t=this.getUseTranslationConfig(n.value);if(t)return this.handleUseTranslationDeclarator(e,s,t),void this.handleUseTranslationForComments(e,s,t)}"MemberExpression"===n.type&&"Identifier"===n.property.type&&"getFixedT"===n.property.value&&this.handleGetFixedTDeclarator(e,s)}handleUseTranslationForComments(e,t,s){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const r=t.arguments?.[s.nsArg]?.expression,i=t.arguments?.[s.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===r?.type?o=r.value:"ArrayExpression"===r?.type&&"StringLiteral"===r.elements[0]?.expression.type&&(o=r.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(n,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,s,n){let r;if("Identifier"===e.id.type&&(r=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(r=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){r="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){r=t.value.value;break}}if(!r)return;const i=s.arguments?.[n.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=s.arguments?.[n.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(r,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const s=e.id.value,n=t.arguments,r=n[1]?.expression,i=n[2]?.expression,o="StringLiteral"===r?.type?r.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(s,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const n=this.getFunctionName(e.callee);if(!n)return;const r=this.getVarFromScope(n),i=this.config.extract.functions||["t","*.t"];let o=void 0!==r;if(!o)for(const e of i)if(e.startsWith("*.")){if(n.endsWith(e.substring(1))){o=!0;break}}else if(e===n){o=!0;break}if(!o||0===e.arguments.length)return;const{keysToProcess:a,isSelectorAPI:l}=this.handleCallExpressionArgument(e,0);if(0===a.length)return;let u=!1;const p=this.config.extract.pluralSeparator??"_";for(let e=0;e<a.length;e++)a[e].endsWith(`${p}ordinal`)&&(u=!0,a[e]=a[e].slice(0,-8));let f,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(f=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const y=c?t(c,"defaultValue"):void 0,d="string"==typeof y?y:f;for(let e=0;e<a.length;e++){let n,i=a[e];if(c){const e=t(c,"ns");"string"==typeof e&&(n=e)}const o=this.config.extract.nsSeparator??":";if(!n&&o&&i.includes(o)){const e=i.split(o);if(n=e.shift(),i=e.join(o),!i||""===i.trim()){this.logger.warn(`Skipping key that became empty after namespace removal: '${n}${o}'`);continue}}!n&&r?.defaultNs&&(n=r.defaultNs),n||(n=this.config.extract.defaultNS);let p=i;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";if(p=!1!==e?r.keyPrefix.endsWith(e)?`${r.keyPrefix}${i}`:`${r.keyPrefix}${e}${i}`:`${r.keyPrefix}${i}`,!1!==e){if(p.split(e).some(e=>""===e.trim())){this.logger.warn(`Skipping key with empty segments: '${p}' (keyPrefix: '${r.keyPrefix}', key: '${i}')`);continue}}}const f=e===a.length-1&&d||i;if(c){const e=s(c,"context"),r=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,s=this.config.extract.contextSeparator??"_";""!==t&&r.push({key:`${p}${s}${t}`,ns:n,defaultValue:f})}else if(e?.value){const t=this.resolvePossibleContextStringValues(e.value),s=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{r.push({key:`${p}${s}${e}`,ns:n,defaultValue:f})}),r.push({key:p,ns:n,defaultValue:f}))}const i=void 0!==t(c,"count"),o=!0===t(c,"ordinal");if(i||u){this.config.extract.disablePlurals?r.length>0?r.forEach(this.pluginContext.addKey):this.pluginContext.addKey({key:p,ns:n,defaultValue:f}):this.handlePluralKeys(p,n,c,o||u,d);continue}if(r.length>0){r.forEach(this.pluginContext.addKey);continue}!0===t(c,"returnObjects")&&this.objectKeys.add(p)}l&&this.objectKeys.add(p),this.pluginContext.addKey({key:p,ns:n,defaultValue:f})}}handleCallExpressionArgument(e,t){const s=e.arguments[t].expression,n=[];let r=!1;if("ArrowFunctionExpression"===s.type){const e=this.extractKeyFromSelector(s);e&&(n.push(e),r=!0)}else if("ArrayExpression"===s.type)for(const e of s.elements)e?.expression&&n.push(...this.resolvePossibleKeyStringValues(e.expression));else n.push(...this.resolvePossibleKeyStringValues(s));return{keysToProcess:n.filter(e=>!!e),isSelectorAPI:r}}handlePluralKeys(e,n,r,i,o){try{const a=i?"ordinal":"cardinal",l=new Set;for(const e of this.config.locales)try{const t=new Intl.PluralRules(e,{type:a});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:a});t.resolvedOptions().pluralCategories.forEach(e=>l.add(e))}const u=Array.from(l).sort(),p=this.config.extract.pluralSeparator??"_",f=t(r,"defaultValue"),c=t(r,`defaultValue${p}other`),y=t(r,`defaultValue${p}ordinal${p}other`),d=t(r,"count");let g;if("number"==typeof d)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";g=new Intl.PluralRules(e,{type:a}).select(d)}catch(e){}const h=s(r,"context"),x=[];if(h?.value){const t=this.resolvePossibleContextStringValues(h.value);if(t.length>0){for(const s of t)s.length>0&&x.push({key:e,context:s});!1!==this.config.extract?.generateBasePluralForms&&x.push({key:e})}else x.push({key:e})}else x.push({key:e});for(const{key:e,context:s}of x)for(const a of u){const l=t(r,i?`defaultValue${p}ordinal${p}${a}`:`defaultValue${p}${a}`);let u,d;if(u="string"==typeof l?l:"one"===a&&"string"==typeof f?f:i&&"string"==typeof y?y:i||"string"!=typeof c?"string"==typeof f?f:o&&g===a?o:e:c,s){const t=this.config.extract.contextSeparator??"_";d=i?`${e}${t}${s}${p}ordinal${p}${a}`:`${e}${t}${s}${p}${a}`}else d=i?`${e}${p}ordinal${p}${a}`:`${e}${p}${a}`;this.pluginContext.addKey({key:d,ns:n,defaultValue:u,hasCount:!0,isOrdinal:i})}}catch(s){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=o||t(r,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof i?i:e})}}handleJSXElement(t){const s=this.getElementName(t);if(s&&(this.config.extract.transComponents||["Trans"]).includes(s)){const s=e(t,this.config),n=[];if(s){if(s.keyExpression){const e=this.resolvePossibleKeyStringValues(s.keyExpression);n.push(...e)}else n.push(s.serializedChildren);let e;const{contextExpression:r,optionsNode:i,defaultValue:o,hasCount:a,isOrdinal:l,serializedChildren:u}=s;if(s.ns){const{ns:t}=s;e=n.map(e=>({key:e,ns:t,defaultValue:o||u,hasCount:a,isOrdinal:l}))}else{e=n.map(e=>{const t=this.config.extract.nsSeparator??":";let s;if(t&&e.includes(t)){let n;[s,...n]=e.split(t),e=n.join(t)}return{key:e,ns:s,defaultValue:o||u,hasCount:a,isOrdinal:l}});const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===s?.type&&"JSXExpressionContainer"===s.value?.type&&"Identifier"===s.value.expression.type){const t=s.value.expression.value,n=this.getVarFromScope(t);n?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=n.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),r&&a)if(this.config.extract.disablePlurals){const t=this.resolvePossibleContextStringValues(r),s=this.config.extract.contextSeparator??"_";if(t.length>0)if("StringLiteral"===r.type)for(const n of t)for(const t of e){const e=`${t.key}${s}${n}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}else{e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});for(const n of t)for(const t of e){const e=`${t.key}${s}${n}`;this.pluginContext.addKey({key:e,ns:t.ns,defaultValue:t.defaultValue})}}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else{const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),n=!!s,o=this.resolvePossibleContextStringValues(r),a=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,n,i));for(const t of o)for(const s of e){const e=`${s.key}${a}${t}`;this.generatePluralKeysForTrans(e,s.defaultValue,s.ns,n,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,n,i))}else if(r){const t=this.resolvePossibleContextStringValues(r),s=this.config.extract.contextSeparator??"_";if(t.length>0){for(const n of t)for(const{key:t,ns:r,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${s}${n}`,ns:r,defaultValue:i});"StringLiteral"!==r.type&&e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}else if(a)if(this.config.extract.disablePlurals)e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})});else{const s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),n=!!s;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,n,i))}else e.forEach(e=>{this.pluginContext.addKey({key:e.key,ns:e.ns,defaultValue:e.defaultValue})})}}}generatePluralKeysForTrans(e,s,n,r,i){try{const o=r?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t(i,`defaultValue${l}other`),p=t(i,`defaultValue${l}ordinal${l}other`));for(const o of a){const a=i?t(i,r?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`):void 0;let f;f="string"==typeof a?a:"one"===o&&"string"==typeof s?s:r&&"string"==typeof p?p:r||"string"!=typeof u?"string"==typeof s?s:e:u;const c=r?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:c,ns:n,defaultValue:f,hasCount:!0,isOrdinal:r})}}catch(t){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,ns:n,defaultValue:s})}}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 s=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&s.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&s.unshift(t.value),s.join(".")}}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 s=t;const n=[];for(;"MemberExpression"===s.type;){const e=s.property;if("Identifier"===e.type)n.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;n.unshift(e.expression.value)}s=s.object}if(n.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return n.join(t)}return null}resolvePossibleContextStringValues(e){return[...this.hooks.resolvePossibleContextStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleKeyStringValues(e){return[...this.hooks.resolvePossibleKeyStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleStringValuesFromExpression(e,t=!1){if("StringLiteral"===e.type)return e.value||t?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValuesFromExpression(e.consequent,t),...this.resolvePossibleStringValuesFromExpression(e.alternate,t)]}if("Identifier"===e.type&&"undefined"===e.value)return[];if("TemplateLiteral"===e.type)return this.resolvePossibleStringValuesFromTemplateString(e);if("NumericLiteral"===e.type||"BooleanLiteral"===e.type)return[`${e.value}`];if("TsSatisfiesExpression"===e.type||"TsAsExpression"===e.type){const s=e.typeAnnotation;return this.resolvePossibleStringValuesFromType(s,t)}return[]}resolvePossibleStringValuesFromType(e,t=!1){if("TsUnionType"===e.type)return e.types.flatMap(e=>this.resolvePossibleStringValuesFromType(e,t));if("TsLiteralType"===e.type){if("StringLiteral"===e.literal.type)return e.literal.value||t?[e.literal.value]:[];if("TemplateLiteral"===e.literal.type)return this.resolvePossibleStringValuesFromTemplateLiteralType(e.literal);if("NumericLiteral"===e.literal.type||"BooleanLiteral"===e.literal.type)return[`${e.literal.value}`]}return[]}resolvePossibleStringValuesFromTemplateString(e){if(1===e.quasis.length&&0===e.expressions.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.expressions.reduce((e,t,n)=>e.flatMap(e=>{const r=s[n]?.cooked??"";return this.resolvePossibleStringValuesFromExpression(t,!0).map(t=>`${e}${t}${r}`)}),[t.cooked??""])}resolvePossibleStringValuesFromTemplateLiteralType(e){if(1===e.quasis.length&&0===e.types.length)return[e.quasis[0].cooked||""];const[t,...s]=e.quasis;return e.types.reduce((e,t,n)=>e.flatMap(e=>{const r=s[n]?.cooked??"";return this.resolvePossibleStringValuesFromType(t,!0).map(t=>`${e}${t}${r}`)}),[t.cooked??""])}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const s of t){if("string"==typeof s&&s===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof s&&s.name===e)return{name:s.name,nsArg:s.nsArg??0,keyPrefixArg:s.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let s=e;for(;"MemberExpression"===s.type;){if("Identifier"!==s.property.type)return null;t.unshift(s.property.value),s=s.object}if("ThisExpression"===s.type)t.unshift("this");else{if("Identifier"!==s.type)return null;t.unshift(s.value)}return t.join(".")}return null}}export{n as ASTVisitors};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,c,u,d){const
|
|
1
|
+
function e(e,c,u,d){const i=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(e){const t=[],n=new Set,s=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let a;for(;null!==(a=s.exec(e));){const e=(a[1]??a[2]).trim();e&&!n.has(e)&&(n.add(e),t.push(e))}return t}(e);for(const e of f){let f;for(;null!==(f=i.exec(e));){let i,y=f[2];if(!y||""===y.trim())continue;const p=e.slice(f.index+f[0].length),$=s(p),x=r(p),h=l(p),g=o(p);let V=!1;const k=u.extract.pluralSeparator??"_";if(y.endsWith(`${k}ordinal`)&&(V=!0,y=y.slice(0,-(k.length+7)),!y||""===y.trim()))continue;const K=!0===g||V;i=a(p);const S=u.extract.nsSeparator??":";if(!i&&S&&y.includes(S)){const e=y.split(S);i=e.shift(),y=e.join(S)}if(!i&&d){const e=d("t");e?.defaultNs&&(i=e.defaultNs)}if(i||(i=u.extract.defaultNS),u.extract.disablePlurals)x?c.addKey({key:`${y}_${x}`,ns:i,defaultValue:$??y}):c.addKey({key:y,ns:i,defaultValue:$??y});else if(x&&h){n(y,$??y,i,x,c,u,K);!1!==u.extract?.generateBasePluralForms&&t(y,$??y,i,c,u,K)}else x?(c.addKey({key:y,ns:i,defaultValue:$??y}),c.addKey({key:`${y}_${x}`,ns:i,defaultValue:$??y})):h?t(y,$??y,i,c,u,K):c.addKey({key:y,ns:i,defaultValue:$??y})}}}function t(e,t,n,s,a,r=!1){try{const l=r?"ordinal":"cardinal",o=new Set;for(const e of a.locales)try{const t=new Intl.PluralRules(e,{type:l});t.resolvedOptions().pluralCategories.forEach(e=>o.add(e))}catch(e){const t=new Intl.PluralRules("en",{type:l});t.resolvedOptions().pluralCategories.forEach(e=>o.add(e))}const c=Array.from(o).sort(),u=a.extract.pluralSeparator??"_";for(const a of c){const l=r?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;s.addKey({key:l,ns:n,defaultValue:t,hasCount:!0,isOrdinal:r})}}catch(a){s.addKey({key:e,ns:n,defaultValue:t})}}function n(e,t,n,s,a,r,l=!1){try{const o=l?"ordinal":"cardinal",c=new Set;for(const e of r.locales)try{const t=new Intl.PluralRules(e,{type:o});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}catch(e){const t=new Intl.PluralRules(r.extract.primaryLanguage||"en",{type:o});t.resolvedOptions().pluralCategories.forEach(e=>c.add(e))}const u=Array.from(c).sort(),d=r.extract.pluralSeparator??"_";for(const r of u){const o=l?`${e}_${s}${d}ordinal${d}${r}`:`${e}_${s}${d}${r}`;a.addKey({key:o,ns:n,defaultValue:t,hasCount:!0,isOrdinal:l})}}catch(r){a.addKey({key:`${e}_${s}`,ns:n,defaultValue:t})}}function s(e){const t=/^\s*,\s*(['"])(.*?)\1/.exec(e);if(t)return t[2];const n=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(e);return n?n[2]:void 0}function a(e){const t=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function r(e){const t=/^\s*,\s*\{[^}]*context\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function l(e){const t=/^\s*,\s*\{[^}]*count\s*:\s*(\d+)/.exec(e);if(t)return parseInt(t[1],10)}function o(e){const t=/^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(e);if(t)return"true"===t[1]}export{e 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 n(n,i){const r=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),a=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),s=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),p=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let l;s||"JSXAttribute"!==p?.type||"JSXExpressionContainer"!==p.value?.type||"ObjectExpression"!==p.value.expression.type||(l=e(p.value.expression,"count"));const o=!!s||!!l,u=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===u?.type&&"JSXExpressionContainer"===u.value?.type&&"ObjectExpression"===u.value.expression.type?u.value.expression:void 0,v=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),f=!!v,c=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let d="JSXAttribute"===c?.type&&"JSXExpressionContainer"===c.value?.type?c.value.expression:"JSXAttribute"===c?.type&&"StringLiteral"===c.value?.type?c.value:void 0;const m=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let S;if(S="JSXAttribute"===m?.type&&"StringLiteral"===m.value?.type?m.value.value:void 0,y&&(void 0===S&&(S=t(y,"ns")),void 0===d)){const t=e(y,"context");t?.value&&(d=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," ")}(n.children,i);let x,g,J;if("JSXAttribute"===a?.type&&"StringLiteral"===a.value?.type)x=a.value.value;else{const e=i.extract.defaultValue;x="string"==typeof e?e:""}if("JSXAttribute"===r?.type){if("StringLiteral"===r.value?.type){if(g=r.value,J=g.value,!J||""===J.trim())return console.warn("Ignoring Trans component with empty i18nKey"),null;if(S&&"StringLiteral"===g.type){const e=i.extract.nsSeparator??":",t=g.value;if(e&&t.startsWith(`${S}${e}`)){if(J=t.slice(`${S}${e}`.length),!J||""===J.trim())return console.warn("Ignoring Trans component with i18nKey that becomes empty after namespace removal"),null;g={...g,value:J}}}}else"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(g=r.value.expression);if(!g)return null}return a||!J||b.trim()?!a&&b.trim()&&(x=b):x=J,{keyExpression:g,serializedChildren:b,ns:S,defaultValue:x,hasCount:o,isOrdinal:f,contextExpression:d,optionsNode:y}}export{n as extractFromTransComponent};
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -547,15 +547,44 @@ export class ASTVisitors {
|
|
|
547
547
|
const parts = key.split(nsSeparator)
|
|
548
548
|
ns = parts.shift()
|
|
549
549
|
key = parts.join(nsSeparator)
|
|
550
|
+
|
|
551
|
+
if (!key || key.trim() === '') {
|
|
552
|
+
this.logger.warn(`Skipping key that became empty after namespace removal: '${ns}${nsSeparator}'`)
|
|
553
|
+
continue
|
|
554
|
+
}
|
|
550
555
|
}
|
|
551
556
|
|
|
552
557
|
if (!ns && scopeInfo?.defaultNs) ns = scopeInfo.defaultNs
|
|
553
558
|
if (!ns) ns = this.config.extract.defaultNS
|
|
554
559
|
|
|
555
560
|
let finalKey = key
|
|
561
|
+
|
|
562
|
+
// Apply keyPrefix AFTER namespace extraction
|
|
556
563
|
if (scopeInfo?.keyPrefix) {
|
|
557
564
|
const keySeparator = this.config.extract.keySeparator ?? '.'
|
|
558
|
-
|
|
565
|
+
|
|
566
|
+
// Apply keyPrefix - handle case where keyPrefix already ends with separator
|
|
567
|
+
if (keySeparator !== false) {
|
|
568
|
+
if (scopeInfo.keyPrefix.endsWith(keySeparator)) {
|
|
569
|
+
finalKey = `${scopeInfo.keyPrefix}${key}`
|
|
570
|
+
} else {
|
|
571
|
+
finalKey = `${scopeInfo.keyPrefix}${keySeparator}${key}`
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
finalKey = `${scopeInfo.keyPrefix}${key}`
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Validate keyPrefix combinations that create problematic keys
|
|
578
|
+
if (keySeparator !== false) {
|
|
579
|
+
// Check for patterns that would create empty segments in the nested key structure
|
|
580
|
+
const segments = finalKey.split(keySeparator)
|
|
581
|
+
const hasEmptySegment = segments.some(segment => segment.trim() === '')
|
|
582
|
+
|
|
583
|
+
if (hasEmptySegment) {
|
|
584
|
+
this.logger.warn(`Skipping key with empty segments: '${finalKey}' (keyPrefix: '${scopeInfo.keyPrefix}', key: '${key}')`)
|
|
585
|
+
continue
|
|
586
|
+
}
|
|
587
|
+
}
|
|
559
588
|
}
|
|
560
589
|
|
|
561
590
|
const isLastKey = i === keysToProcess.length - 1
|
|
@@ -594,14 +623,19 @@ export class ASTVisitors {
|
|
|
594
623
|
const hasCount = getObjectPropValue(options, 'count') !== undefined
|
|
595
624
|
const isOrdinalByOption = getObjectPropValue(options, 'ordinal') === true
|
|
596
625
|
if (hasCount || isOrdinalByKey) {
|
|
597
|
-
//
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
626
|
+
// Check if plurals are disabled
|
|
627
|
+
if (this.config.extract.disablePlurals) {
|
|
628
|
+
// When plurals are disabled, treat count as a regular option (for interpolation only)
|
|
629
|
+
// Still handle context normally
|
|
630
|
+
if (keysWithContext.length > 0) {
|
|
631
|
+
keysWithContext.forEach(this.pluginContext.addKey)
|
|
632
|
+
} else {
|
|
633
|
+
// No context, just add the base key (no plurals even if count is present)
|
|
634
|
+
this.pluginContext.addKey({ key: finalKey, ns, defaultValue: dv })
|
|
602
635
|
}
|
|
603
636
|
} else {
|
|
604
|
-
//
|
|
637
|
+
// Original plural handling logic when plurals are enabled
|
|
638
|
+
// Always pass the base key to handlePluralKeys - it will handle context internally
|
|
605
639
|
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue)
|
|
606
640
|
}
|
|
607
641
|
|
|
@@ -725,20 +759,29 @@ export class ASTVisitors {
|
|
|
725
759
|
}
|
|
726
760
|
}
|
|
727
761
|
|
|
728
|
-
//
|
|
729
|
-
const
|
|
730
|
-
const hasContext = typeof contextValue === 'string' && contextValue.length > 0
|
|
731
|
-
|
|
732
|
-
// Determine which key variants to generate
|
|
762
|
+
// Handle context - both static and dynamic
|
|
763
|
+
const contextProp = getObjectProperty(options, 'context')
|
|
733
764
|
const keysToGenerate: Array<{ key: string, context?: string }> = []
|
|
734
765
|
|
|
735
|
-
if (
|
|
736
|
-
//
|
|
737
|
-
|
|
766
|
+
if (contextProp?.value) {
|
|
767
|
+
// Handle dynamic context by resolving all possible values
|
|
768
|
+
const contextValues = this.resolvePossibleContextStringValues(contextProp.value)
|
|
738
769
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
770
|
+
if (contextValues.length > 0) {
|
|
771
|
+
// Generate keys for each context value
|
|
772
|
+
for (const contextValue of contextValues) {
|
|
773
|
+
if (contextValue.length > 0) { // Skip empty contexts
|
|
774
|
+
keysToGenerate.push({ key, context: contextValue })
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// For dynamic context, also generate base plural forms if generateBasePluralForms is not disabled
|
|
779
|
+
const shouldGenerateBaseForms = this.config.extract?.generateBasePluralForms !== false
|
|
780
|
+
if (shouldGenerateBaseForms) {
|
|
781
|
+
keysToGenerate.push({ key })
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
// Couldn't resolve context, fall back to base key only
|
|
742
785
|
keysToGenerate.push({ key })
|
|
743
786
|
}
|
|
744
787
|
} else {
|
|
@@ -774,9 +817,10 @@ export class ASTVisitors {
|
|
|
774
817
|
// 3. Construct the final plural key
|
|
775
818
|
let finalKey: string
|
|
776
819
|
if (context) {
|
|
820
|
+
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
777
821
|
finalKey = isOrdinal
|
|
778
|
-
? `${baseKey}${
|
|
779
|
-
: `${baseKey}${
|
|
822
|
+
? `${baseKey}${contextSeparator}${context}${pluralSeparator}ordinal${pluralSeparator}${category}`
|
|
823
|
+
: `${baseKey}${contextSeparator}${context}${pluralSeparator}${category}`
|
|
780
824
|
} else {
|
|
781
825
|
finalKey = isOrdinal
|
|
782
826
|
? `${baseKey}${pluralSeparator}ordinal${pluralSeparator}${category}`
|
|
@@ -901,39 +945,85 @@ export class ASTVisitors {
|
|
|
901
945
|
|
|
902
946
|
// Handle the combination of context and count
|
|
903
947
|
if (contextExpression && hasCount) {
|
|
904
|
-
//
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
948
|
+
// Check if plurals are disabled
|
|
949
|
+
if (this.config.extract.disablePlurals) {
|
|
950
|
+
// When plurals are disabled, treat count as a regular option
|
|
951
|
+
// Still handle context normally
|
|
952
|
+
const contextValues = this.resolvePossibleContextStringValues(contextExpression)
|
|
953
|
+
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
954
|
+
|
|
955
|
+
if (contextValues.length > 0) {
|
|
956
|
+
// For static context (string literal), only add context variants
|
|
957
|
+
if (contextExpression.type === 'StringLiteral') {
|
|
958
|
+
for (const context of contextValues) {
|
|
959
|
+
for (const extractedKey of extractedKeys) {
|
|
960
|
+
const contextKey = `${extractedKey.key}${contextSeparator}${context}`
|
|
961
|
+
this.pluginContext.addKey({ key: contextKey, ns: extractedKey.ns, defaultValue: extractedKey.defaultValue })
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
} else {
|
|
965
|
+
// For dynamic context, add both base and context variants
|
|
966
|
+
extractedKeys.forEach(extractedKey => {
|
|
967
|
+
this.pluginContext.addKey({
|
|
968
|
+
key: extractedKey.key,
|
|
969
|
+
ns: extractedKey.ns,
|
|
970
|
+
defaultValue: extractedKey.defaultValue
|
|
971
|
+
})
|
|
972
|
+
})
|
|
973
|
+
for (const context of contextValues) {
|
|
974
|
+
for (const extractedKey of extractedKeys) {
|
|
975
|
+
const contextKey = `${extractedKey.key}${contextSeparator}${context}`
|
|
976
|
+
this.pluginContext.addKey({ key: contextKey, ns: extractedKey.ns, defaultValue: extractedKey.defaultValue })
|
|
977
|
+
}
|
|
978
|
+
}
|
|
926
979
|
}
|
|
980
|
+
} else {
|
|
981
|
+
// Fallback to just base keys if context resolution fails
|
|
982
|
+
extractedKeys.forEach(extractedKey => {
|
|
983
|
+
this.pluginContext.addKey({
|
|
984
|
+
key: extractedKey.key,
|
|
985
|
+
ns: extractedKey.ns,
|
|
986
|
+
defaultValue: extractedKey.defaultValue
|
|
987
|
+
})
|
|
988
|
+
})
|
|
927
989
|
}
|
|
928
990
|
} else {
|
|
929
|
-
//
|
|
930
|
-
|
|
991
|
+
// Original plural handling logic when plurals are enabled
|
|
992
|
+
// Find isOrdinal prop on the <Trans> component
|
|
993
|
+
const ordinalAttr = node.opening.attributes?.find(
|
|
994
|
+
(attr) =>
|
|
995
|
+
attr.type === 'JSXAttribute' &&
|
|
996
|
+
attr.name.type === 'Identifier' &&
|
|
997
|
+
attr.name.value === 'ordinal'
|
|
998
|
+
)
|
|
999
|
+
const isOrdinal = !!ordinalAttr
|
|
1000
|
+
|
|
1001
|
+
const contextValues = this.resolvePossibleContextStringValues(contextExpression)
|
|
1002
|
+
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
1003
|
+
|
|
1004
|
+
// Generate all combinations of context and plural forms
|
|
1005
|
+
if (contextValues.length > 0) {
|
|
1006
|
+
// Generate base plural forms (no context)
|
|
1007
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
1008
|
+
|
|
1009
|
+
// Generate context + plural combinations
|
|
1010
|
+
for (const context of contextValues) {
|
|
1011
|
+
for (const extractedKey of extractedKeys) {
|
|
1012
|
+
const contextKey = `${extractedKey.key}${contextSeparator}${context}`
|
|
1013
|
+
this.generatePluralKeysForTrans(contextKey, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode)
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
// Fallback to just plural forms if context resolution fails
|
|
1018
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
1019
|
+
}
|
|
931
1020
|
}
|
|
932
1021
|
} else if (contextExpression) {
|
|
933
1022
|
const contextValues = this.resolvePossibleContextStringValues(contextExpression)
|
|
934
1023
|
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
935
1024
|
|
|
936
1025
|
if (contextValues.length > 0) {
|
|
1026
|
+
// Add context variants
|
|
937
1027
|
for (const context of contextValues) {
|
|
938
1028
|
for (const { key, ns, defaultValue } of extractedKeys) {
|
|
939
1029
|
this.pluginContext.addKey({ key: `${key}${contextSeparator}${context}`, ns, defaultValue })
|
|
@@ -941,22 +1031,57 @@ export class ASTVisitors {
|
|
|
941
1031
|
}
|
|
942
1032
|
// Only add the base key as a fallback if the context is dynamic (i.e., not a simple string).
|
|
943
1033
|
if (contextExpression.type !== 'StringLiteral') {
|
|
944
|
-
extractedKeys.forEach(
|
|
1034
|
+
extractedKeys.forEach(extractedKey => {
|
|
1035
|
+
this.pluginContext.addKey({
|
|
1036
|
+
key: extractedKey.key,
|
|
1037
|
+
ns: extractedKey.ns,
|
|
1038
|
+
defaultValue: extractedKey.defaultValue
|
|
1039
|
+
})
|
|
1040
|
+
})
|
|
945
1041
|
}
|
|
1042
|
+
} else {
|
|
1043
|
+
// If no context values were resolved, just add base keys
|
|
1044
|
+
extractedKeys.forEach(extractedKey => {
|
|
1045
|
+
this.pluginContext.addKey({
|
|
1046
|
+
key: extractedKey.key,
|
|
1047
|
+
ns: extractedKey.ns,
|
|
1048
|
+
defaultValue: extractedKey.defaultValue
|
|
1049
|
+
})
|
|
1050
|
+
})
|
|
946
1051
|
}
|
|
947
1052
|
} else if (hasCount) {
|
|
948
|
-
//
|
|
949
|
-
|
|
950
|
-
(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1053
|
+
// Check if plurals are disabled
|
|
1054
|
+
if (this.config.extract.disablePlurals) {
|
|
1055
|
+
// When plurals are disabled, just add the base keys (no plural forms)
|
|
1056
|
+
extractedKeys.forEach(extractedKey => {
|
|
1057
|
+
this.pluginContext.addKey({
|
|
1058
|
+
key: extractedKey.key,
|
|
1059
|
+
ns: extractedKey.ns,
|
|
1060
|
+
defaultValue: extractedKey.defaultValue
|
|
1061
|
+
})
|
|
1062
|
+
})
|
|
1063
|
+
} else {
|
|
1064
|
+
// Original plural handling logic when plurals are enabled
|
|
1065
|
+
// Find isOrdinal prop on the <Trans> component
|
|
1066
|
+
const ordinalAttr = node.opening.attributes?.find(
|
|
1067
|
+
(attr) =>
|
|
1068
|
+
attr.type === 'JSXAttribute' &&
|
|
1069
|
+
attr.name.type === 'Identifier' &&
|
|
1070
|
+
attr.name.value === 'ordinal'
|
|
1071
|
+
)
|
|
1072
|
+
const isOrdinal = !!ordinalAttr
|
|
956
1073
|
|
|
957
|
-
|
|
1074
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
1075
|
+
}
|
|
958
1076
|
} else {
|
|
959
|
-
|
|
1077
|
+
// No count or context - just add the base keys
|
|
1078
|
+
extractedKeys.forEach(extractedKey => {
|
|
1079
|
+
this.pluginContext.addKey({
|
|
1080
|
+
key: extractedKey.key,
|
|
1081
|
+
ns: extractedKey.ns,
|
|
1082
|
+
defaultValue: extractedKey.defaultValue
|
|
1083
|
+
})
|
|
1084
|
+
})
|
|
960
1085
|
}
|
|
961
1086
|
}
|
|
962
1087
|
}
|
|
@@ -39,6 +39,12 @@ export function extractKeysFromComments (
|
|
|
39
39
|
let match: RegExpExecArray | null
|
|
40
40
|
while ((match = keyRegex.exec(text)) !== null) {
|
|
41
41
|
let key = match[2]
|
|
42
|
+
|
|
43
|
+
// Validate that the key is not empty or whitespace-only
|
|
44
|
+
if (!key || key.trim() === '') {
|
|
45
|
+
continue // Skip empty keys
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
let ns: string | undefined
|
|
43
49
|
const remainder = text.slice(match.index + match[0].length)
|
|
44
50
|
|
|
@@ -54,6 +60,11 @@ export function extractKeysFromComments (
|
|
|
54
60
|
isOrdinalByKey = true
|
|
55
61
|
// Normalize the key by stripping the suffix
|
|
56
62
|
key = key.slice(0, -(pluralSeparator.length + 7)) // Remove "_ordinal"
|
|
63
|
+
|
|
64
|
+
// Validate that the key is still not empty after normalization
|
|
65
|
+
if (!key || key.trim() === '') {
|
|
66
|
+
continue // Skip keys that become empty after normalization
|
|
67
|
+
}
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
const isOrdinal = ordinal === true || isOrdinalByKey
|
|
@@ -81,26 +92,38 @@ export function extractKeysFromComments (
|
|
|
81
92
|
// 4. Final fallback to configured default namespace
|
|
82
93
|
if (!ns) ns = config.extract.defaultNS
|
|
83
94
|
|
|
84
|
-
// 5. Handle context and count combinations
|
|
85
|
-
if (
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
// 5. Handle context and count combinations based on disablePlurals setting
|
|
96
|
+
if (config.extract.disablePlurals) {
|
|
97
|
+
// When plurals are disabled, ignore count for key generation
|
|
98
|
+
if (context) {
|
|
99
|
+
// Only generate context variants (no base key when context is static)
|
|
100
|
+
pluginContext.addKey({ key: `${key}_${context}`, ns, defaultValue: defaultValue ?? key })
|
|
101
|
+
} else {
|
|
102
|
+
// Simple key (ignore count)
|
|
103
|
+
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
93
104
|
}
|
|
94
|
-
} else if (context) {
|
|
95
|
-
// Just context variants
|
|
96
|
-
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
97
|
-
pluginContext.addKey({ key: `${key}_${context}`, ns, defaultValue: defaultValue ?? key })
|
|
98
|
-
} else if (count) {
|
|
99
|
-
// Just plural variants
|
|
100
|
-
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config, isOrdinal)
|
|
101
105
|
} else {
|
|
102
|
-
//
|
|
103
|
-
|
|
106
|
+
// Original plural handling logic when plurals are enabled
|
|
107
|
+
if (context && count) {
|
|
108
|
+
// Generate context+plural combinations
|
|
109
|
+
generateContextPluralKeys(key, defaultValue ?? key, ns, context, pluginContext, config, isOrdinal)
|
|
110
|
+
|
|
111
|
+
// Only generate base plural forms if generateBasePluralForms is not disabled
|
|
112
|
+
const shouldGenerateBaseForms = config.extract?.generateBasePluralForms !== false
|
|
113
|
+
if (shouldGenerateBaseForms) {
|
|
114
|
+
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config, isOrdinal)
|
|
115
|
+
}
|
|
116
|
+
} else if (context) {
|
|
117
|
+
// Just context variants
|
|
118
|
+
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
119
|
+
pluginContext.addKey({ key: `${key}_${context}`, ns, defaultValue: defaultValue ?? key })
|
|
120
|
+
} else if (count) {
|
|
121
|
+
// Just plural variants
|
|
122
|
+
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config, isOrdinal)
|
|
123
|
+
} else {
|
|
124
|
+
// Simple key
|
|
125
|
+
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
126
|
+
}
|
|
104
127
|
}
|
|
105
128
|
}
|
|
106
129
|
}
|
|
@@ -126,7 +126,9 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
126
126
|
)
|
|
127
127
|
let contextExpression = (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'JSXExpressionContainer')
|
|
128
128
|
? contextAttr.value.expression
|
|
129
|
-
:
|
|
129
|
+
: (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'StringLiteral')
|
|
130
|
+
? contextAttr.value
|
|
131
|
+
: undefined
|
|
130
132
|
|
|
131
133
|
// 1. Prioritize direct props for 'ns' and 'context'
|
|
132
134
|
const nsAttr = node.opening.attributes?.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'ns')
|
|
@@ -178,6 +180,12 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
178
180
|
keyExpression = i18nKeyAttr.value
|
|
179
181
|
processedKeyValue = keyExpression.value
|
|
180
182
|
|
|
183
|
+
// Validate that the key is not empty
|
|
184
|
+
if (!processedKeyValue || processedKeyValue.trim() === '') {
|
|
185
|
+
console.warn('Ignoring Trans component with empty i18nKey')
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
|
|
181
189
|
// Handle namespace prefix removal when both ns and i18nKey are provided
|
|
182
190
|
if (ns && keyExpression.type === 'StringLiteral') {
|
|
183
191
|
const nsSeparator = config.extract.nsSeparator ?? ':'
|
|
@@ -186,6 +194,13 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
186
194
|
// If the key starts with the namespace followed by the separator, remove the prefix
|
|
187
195
|
if (nsSeparator && keyValue.startsWith(`${ns}${nsSeparator}`)) {
|
|
188
196
|
processedKeyValue = keyValue.slice(`${ns}${nsSeparator}`.length)
|
|
197
|
+
|
|
198
|
+
// Validate processed key is not empty
|
|
199
|
+
if (!processedKeyValue || processedKeyValue.trim() === '') {
|
|
200
|
+
console.warn('Ignoring Trans component with i18nKey that becomes empty after namespace removal')
|
|
201
|
+
return null
|
|
202
|
+
}
|
|
203
|
+
|
|
189
204
|
// Create a new StringLiteral with the namespace prefix removed
|
|
190
205
|
keyExpression = {
|
|
191
206
|
...keyExpression,
|
package/src/types.ts
CHANGED
|
@@ -119,6 +119,9 @@ export interface I18nextToolkitConfig {
|
|
|
119
119
|
|
|
120
120
|
// New option to control whether base plural forms are generated when context is present
|
|
121
121
|
generateBasePluralForms?: boolean
|
|
122
|
+
|
|
123
|
+
// New option to completely disable plural generation
|
|
124
|
+
disablePlurals?: boolean
|
|
122
125
|
};
|
|
123
126
|
|
|
124
127
|
/** Configuration options for TypeScript type generation */
|
|
@@ -256,7 +259,7 @@ export interface Plugin {
|
|
|
256
259
|
*
|
|
257
260
|
* @param keys - Final map of all extracted keys
|
|
258
261
|
*/
|
|
259
|
-
onEnd?: (keys: Map<string,
|
|
262
|
+
onEnd?: (keys: Map<string, ExtractedKey>) => void | Promise<void>;
|
|
260
263
|
|
|
261
264
|
/**
|
|
262
265
|
* Hook called after all files have been processed and translation files have been generated.
|
|
@@ -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,EAAE,IAAI,EAA6F,UAAU,EAAkD,MAAM,WAAW,CAAA;AACpM,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AAUvG,MAAM,WAAW,eAAe;IAC9B,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACxC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACvC,kCAAkC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,kBAAkB,CAAC,EAAE,OAAO,KAAK,MAAM,EAAE,CAAA;IACvG,8BAA8B,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,kBAAkB,CAAC,EAAE,OAAO,KAAK,MAAM,EAAE,CAAA;CACpG;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,KAAK,CAAiB;IAEvB,UAAU,cAAoB;IAErC,OAAO,CAAC,KAAK,CAAqE;IAElF;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe;IAazB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IA2DZ;;;;;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;IAkB5D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAsChC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,+BAA+B;IAmEvC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,8BAA8B;IAwDtC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAA6F,UAAU,EAAkD,MAAM,WAAW,CAAA;AACpM,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AAUvG,MAAM,WAAW,eAAe;IAC9B,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACxC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACvC,kCAAkC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,kBAAkB,CAAC,EAAE,OAAO,KAAK,MAAM,EAAE,CAAA;IACvG,8BAA8B,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,kBAAkB,CAAC,EAAE,OAAO,KAAK,MAAM,EAAE,CAAA;CACpG;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,KAAK,CAAiB;IAEvB,UAAU,cAAoB;IAErC,OAAO,CAAC,KAAK,CAAqE;IAElF;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe;IAazB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IA2DZ;;;;;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;IAkB5D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAsChC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,+BAA+B;IAmEvC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,8BAA8B;IAwDtC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IAsM5B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IA+HxB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAyOxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;OASG;IACH,OAAO,CAAC,kCAAkC;IAM1C;;;;;;;;;OASG;IACH,OAAO,CAAC,8BAA8B;IAMtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,yCAAyC;IAoCjD,OAAO,CAAC,mCAAmC;IAwB3C;;;;;;;OAOG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;;OAOG;IACH,OAAO,CAAC,kDAAkD;IAyB1D;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|
|
@@ -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,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,
|
|
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,CAqGN"}
|
|
@@ -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,CAgLxH"}
|
package/types/types.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export interface I18nextToolkitConfig {
|
|
|
93
93
|
/** If true, keys that are not found in the source code will be removed from translation files. (default: true) */
|
|
94
94
|
removeUnusedKeys?: boolean;
|
|
95
95
|
generateBasePluralForms?: boolean;
|
|
96
|
+
disablePlurals?: boolean;
|
|
96
97
|
};
|
|
97
98
|
/** Configuration options for TypeScript type generation */
|
|
98
99
|
types?: {
|
|
@@ -211,10 +212,7 @@ export interface Plugin {
|
|
|
211
212
|
*
|
|
212
213
|
* @param keys - Final map of all extracted keys
|
|
213
214
|
*/
|
|
214
|
-
onEnd?: (keys: Map<string,
|
|
215
|
-
key: string;
|
|
216
|
-
defaultValue?: string;
|
|
217
|
-
}>) => void | Promise<void>;
|
|
215
|
+
onEnd?: (keys: Map<string, ExtractedKey>) => void | Promise<void>;
|
|
218
216
|
/**
|
|
219
217
|
* Hook called after all files have been processed and translation files have been generated.
|
|
220
218
|
* Useful for post-processing, validation, or reporting based on the final results.
|
package/types/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,2DAA2D;IAC3D,OAAO,EAAE;QACP,oEAAoE;QACpE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAEzB,4DAA4D;QAC5D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAE3B,mGAAmG;QACnG,MAAM,EAAE,MAAM,CAAC;QAEf,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,uEAAuE;QACvE,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAErC,8EAA8E;QAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAEpC,oDAAoD;QACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAE1B,mDAAmD;QACnD,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,+EAA+E;QAC/E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAErB,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAE3B;;;;;WAKG;QACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;YACnC,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC,CAAC;QAEH,kFAAkF;QAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE7B,kGAAkG;QAClG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAEvB,8FAA8F;QAC9F,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtC,wFAAwF;QACxF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE5B,2HAA2H;QAC3H,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC,CAAC;QAEhE,yDAAyD;QACzD,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAE9B,2EAA2E;QAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;QAEvF,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,0DAA0D;QAC1D,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE9B;;;;;;;WAOG;QACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;QAErE;;;;;WAKG;QACH,eAAe,CAAC,EAAE,OAAO,CAAC;QAE1B,kHAAkH;QAClH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAG3B,uBAAuB,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,2DAA2D;IAC3D,OAAO,EAAE;QACP,oEAAoE;QACpE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAEzB,4DAA4D;QAC5D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAE3B,mGAAmG;QACnG,MAAM,EAAE,MAAM,CAAC;QAEf,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,uEAAuE;QACvE,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAErC,8EAA8E;QAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAEpC,oDAAoD;QACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAE1B,mDAAmD;QACnD,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,+EAA+E;QAC/E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAErB,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAE3B;;;;;WAKG;QACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;YACnC,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC,CAAC;QAEH,kFAAkF;QAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE7B,kGAAkG;QAClG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAEvB,8FAA8F;QAC9F,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtC,wFAAwF;QACxF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE5B,2HAA2H;QAC3H,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC,CAAC;QAEhE,yDAAyD;QACzD,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAE9B,2EAA2E;QAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;QAEvF,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,0DAA0D;QAC1D,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE9B;;;;;;;WAOG;QACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;QAErE;;;;;WAKG;QACH,eAAe,CAAC,EAAE,OAAO,CAAC;QAE1B,kHAAkH;QAClH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAG3B,uBAAuB,CAAC,EAAE,OAAO,CAAA;QAGjC,cAAc,CAAC,EAAE,OAAO,CAAA;KACzB,CAAC;IAEF,2DAA2D;IAC3D,KAAK,CAAC,EAAE;QACN,mEAAmE;QACnE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAEzB,0DAA0D;QAC1D,MAAM,EAAE,MAAM,CAAC;QAEf,8EAA8E;QAC9E,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;QAEtC,qDAAqD;QACrD,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE;QACP,wBAAwB;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,gEAAgE;QAChE,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB,+CAA+C;QAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB,8DAA8D;QAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;QAEvB,8CAA8C;QAC9C,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAE7B,8CAA8C;QAC9C,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAElC,0CAA0C;QAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,MAAM;IACrB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;OAUG;IACH,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAEhI;;;;;;;;;;OAUG;IACH,4BAA4B,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAEnI;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElE;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClG;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IAEZ,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,oCAAoC;IACpC,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;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IAEb,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IAEjB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC,kEAAkE;IAClE,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,MAAM;IACrB;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAExC;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAExC,oDAAoD;IACpD,MAAM,EAAE,oBAAoB,CAAC;IAE7B,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAAC;CAC1D;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|