i18next-cli 1.7.1 → 1.8.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 +4 -0
- package/README.md +16 -1
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +1 -1
- package/dist/cjs/extractor/parsers/ast-visitors.js +1 -1
- package/dist/cjs/extractor/parsers/jsx-parser.js +1 -1
- package/dist/cjs/extractor/plugin-manager.js +1 -1
- package/dist/cjs/syncer.js +1 -1
- package/dist/cjs/utils/default-value.js +1 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +1 -1
- package/dist/esm/extractor/parsers/ast-visitors.js +1 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +1 -1
- package/dist/esm/extractor/plugin-manager.js +1 -1
- package/dist/esm/syncer.js +1 -1
- package/dist/esm/utils/default-value.js +1 -0
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/extractor/core/translation-manager.ts +15 -5
- package/src/extractor/parsers/ast-visitors.ts +39 -9
- package/src/extractor/parsers/jsx-parser.ts +14 -1
- package/src/extractor/plugin-manager.ts +23 -2
- package/src/syncer.ts +4 -1
- package/src/types.ts +1 -1
- package/src/utils/default-value.ts +44 -0
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
- package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
- package/types/extractor/plugin-manager.d.ts.map +1 -1
- package/types/syncer.d.ts.map +1 -1
- package/types/types.d.ts +1 -1
- package/types/types.d.ts.map +1 -1
- package/types/utils/default-value.d.ts +29 -0
- package/types/utils/default-value.d.ts.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,10 @@ 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.8.0](https://github.com/i18next/i18next-cli/compare/v1.7.1...v1.8.0) - 2025-10-06
|
|
9
|
+
|
|
10
|
+
- **Extractor:** The `defaultValue` option now accepts a function `(key, namespace, language) => string` for dynamic default value generation. This enables powerful patterns like i18next-parser compatibility (`defaultValue: (key) => key`), development-friendly TODO markers (`defaultValue: (key) => \`TODO: translate ${key}\``), and language-specific fallbacks. The function receives the translation key, namespace, and target language as parameters, allowing for sophisticated fallback strategies. [#52](https://github.com/i18next/i18next-cli/issues/52)
|
|
11
|
+
|
|
8
12
|
## [1.7.1](https://github.com/i18next/i18next-cli/compare/v1.7.0...v1.7.1) - 2025-10-06
|
|
9
13
|
|
|
10
14
|
- **Extractor (Comments):** Enhanced comment parser to support ordinal plurals alongside cardinal plurals and context combinations. Commented `t()` calls now correctly handle ordinal flags (e.g., `// t('position', { count: 1, ordinal: true })`) and generate all appropriate ordinal plural forms (`position_ordinal_one`, `position_ordinal_two`, `position_ordinal_few`, `position_ordinal_other`) using the proper `Intl.PluralRules` API. The parser also supports ordinal detection via `_ordinal` suffix in keys and properly combines ordinal plurals with context options for comprehensive key generation. This ensures complete parity with the AST-based extractor for all plural types. [#50](https://github.com/i18next/i18next-cli/issues/50)
|
package/README.md
CHANGED
|
@@ -351,7 +351,22 @@ export default defineConfig({
|
|
|
351
351
|
primaryLanguage: 'en', // Defaults to the first locale in the `locales` array
|
|
352
352
|
secondaryLanguages: ['de', 'fr'], // Defaults to all locales except primaryLanguage
|
|
353
353
|
|
|
354
|
-
|
|
354
|
+
// Default value for missing keys in secondary languages
|
|
355
|
+
// Can be a string, function, or object for flexible fallback strategies
|
|
356
|
+
defaultValue: '', // Simple string: all missing keys get this value
|
|
357
|
+
|
|
358
|
+
// Or use a function for dynamic defaults:
|
|
359
|
+
// defaultValue: (key, namespace, language) => key, // i18next-parser style: use key as value
|
|
360
|
+
// defaultValue: (key, namespace, language) => `TODO: translate ${key}`, // Mark untranslated keys
|
|
361
|
+
// defaultValue: (key, namespace, language) => language === 'de' ? 'German TODO' : 'TODO', // Language-specific
|
|
362
|
+
|
|
363
|
+
// Or use an object for namespace-specific defaults:
|
|
364
|
+
// defaultValue: {
|
|
365
|
+
// common: 'Common translation needed',
|
|
366
|
+
// errors: 'Error message needs translation',
|
|
367
|
+
// // fallback for other namespaces:
|
|
368
|
+
// '*': (key) => key
|
|
369
|
+
// },
|
|
355
370
|
|
|
356
371
|
/** If true, keys that are not found in the source code will be removed from translation files. (default: true) */
|
|
357
372
|
removeUnusedKeys: true,
|
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.8.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
|
|
1
|
+
"use strict";var e=require("node:path"),t=require("glob"),s=require("../../utils/nested-object.js"),o=require("../../utils/file-utils.js"),n=require("../../utils/default-value.js");function a(e){const t=`^${e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(t)}function r(e){if("object"!=typeof e||null===e||Array.isArray(e))return e;const t={},s=Object.keys(e).sort((e,t)=>{const s=e.localeCompare(t,void 0,{sensitivity:"base"});return 0===s?e.localeCompare(t,void 0,{sensitivity:"case"}):s});for(const o of s)t[o]=r(e[o]);return t}function i(e,t,o,a,i,c,u){const{keySeparator:l=".",sort:f=!0,removeUnusedKeys:p=!0,primaryLanguage:g,defaultValue:y=""}=o.extract;let d=p?{}:JSON.parse(JSON.stringify(t));const x=s.getNestedKeys(t,l??".");for(const e of x)if(c.some(t=>t.test(e))){const o=s.getNestedValue(t,e,l??".");s.setNestedValue(d,e,o,l??".")}for(const{key:o,defaultValue:r}of e){const c=s.getNestedValue(t,o,l??"."),f=!e.some(e=>e.key.startsWith(`${o}${l}`)&&e.key!==o),p="object"==typeof c&&null!==c&&(u.has(o)||!r||r===o),x="object"==typeof c&&null!==c&&f&&!u.has(o)&&!p;if(p){s.setNestedValue(d,o,c,l??".");continue}let h;h=void 0===c||x?a===g?r||o:n.resolveDefaultValue(y,o,i,a):c,s.setNestedValue(d,o,h,l??".")}if(!0===f)return r(d);if("function"==typeof f){const t={},s=Object.keys(d),o=new Map;for(const t of e){const e=!1===l?t.key:t.key.split(l)[0];o.has(e)||o.set(e,t)}s.sort((e,t)=>{if("function"==typeof f){const s=o.get(e),n=o.get(t);if(s&&n)return f(s,n)}return e.localeCompare(t,void 0,{sensitivity:"base"})});for(const e of s)t[e]=d[e];d=t}return d}exports.getTranslations=async function(s,n,r){r.extract.primaryLanguage||=r.locales[0]||"en",r.extract.secondaryLanguages||=r.locales.filter(e=>e!==r?.extract?.primaryLanguage);const c=r.extract.defaultNS??"translation",u=[...r.extract.preservePatterns||[]],l=r.extract.indentation??2;for(const e of n)u.push(`${e}.*`);const f=u.map(a),p=new Map;for(const e of s.values()){const t=e.ns||c;p.has(t)||p.set(t,[]),p.get(t).push(e)}const g=[],y=Array.isArray(r.extract.ignore)?r.extract.ignore:r.extract.ignore?[r.extract.ignore]:[];for(const s of r.locales){if(r.extract.mergeNamespaces||!r.extract.output.includes("{{namespace}}")){const t={},a=o.getOutputPath(r.extract.output,s),c=e.resolve(process.cwd(),a),u=await o.loadTranslationFile(c)||{},y=new Set([...p.keys(),...Object.keys(u)]);for(const e of y){const o=p.get(e)||[],a=u[e]||{};t[e]=i(o,a,r,s,e,f,n)}const d=JSON.stringify(u,null,l),x=JSON.stringify(t,null,l);g.push({path:c,updated:x!==d,newTranslations:t,existingTranslations:u})}else{const a=new Set(p.keys()),c=o.getOutputPath(r.extract.output,s,"*"),u=await t.glob(c,{ignore:y});for(const t of u)a.add(e.basename(t,e.extname(t)));for(const t of a){const a=p.get(t)||[],c=o.getOutputPath(r.extract.output,s,t),u=e.resolve(process.cwd(),c),y=await o.loadTranslationFile(u)||{},d=i(a,y,r,s,t,f,n),x=JSON.stringify(y,null,l),h=JSON.stringify(d,null,l);g.push({path:u,updated:h!==x,newTranslations:d,existingTranslations:y})}}}return g};
|
|
@@ -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,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);s=e.shift(),n=e.join(i)}!s&&r?.defaultNs&&(s=r.defaultNs),s||(s=this.config.extract.defaultNS);let u=n;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";u=`${r.keyPrefix}${e}${n}`}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){if(r.length>0)for(const{key:e,ns:t}of r)this.handlePluralKeys(e,t,f,i||l);else this.handlePluralKeys(u,s,f,i||l);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){try{const i=n?"ordinal":"cardinal",o=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:i}).resolvedOptions().pluralCategories,a=this.config.extract.pluralSeparator??"_",l=t.getObjectPropValue(r,"defaultValue"),u=t.getObjectPropValue(r,`defaultValue${a}other`),p=t.getObjectPropValue(r,`defaultValue${a}ordinal${a}other`);for(const i of o){const o=n?`defaultValue${a}ordinal${a}${i}`:`defaultValue${a}${i}`,f=t.getObjectPropValue(r,o);let c;c="string"==typeof f?f:"one"===i&&"string"==typeof l?l:n&&"string"==typeof p?p:n||"string"!=typeof u?"string"==typeof l?l:e:u;const y=n?`${e}${a}ordinal${a}${i}`:`${e}${a}${i}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:c,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 i=t.getObjectPropValue(r,"defaultValue");this.pluginContext.addKey({key:e,ns:s,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.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){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.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)]}return"Identifier"===e.type&&"undefined"===e.value?[]:"TemplateLiteral"===e.type?this.resolvePossibleStringValuesFromTemplateString(e):"NumericLiteral"===e.type||"BooleanLiteral"===e.type?[`${e.value}`]:[]}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??""])}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
|
+
"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,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 s,n=o[e];if(c){const e=t.getObjectPropValue(c,"ns");"string"==typeof e&&(s=e)}const i=this.config.extract.nsSeparator??":";if(!s&&i&&n.includes(i)){const e=n.split(i);s=e.shift(),n=e.join(i)}!s&&r?.defaultNs&&(s=r.defaultNs),s||(s=this.config.extract.defaultNS);let u=n;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";u=`${r.keyPrefix}${e}${n}`}const p=e===o.length-1&&y||n;if(c){const e=t.getObjectProperty(c,"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(c,"count"),i=!0===t.getObjectPropValue(c,"ordinal");if(n||l){if(r.length>0)for(const{key:e,ns:t}of r)this.handlePluralKeys(e,t,c,i||l,y);else this.handlePluralKeys(u,s,c,i||l,y);continue}if(r.length>0){r.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: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 Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_",u=t.getObjectPropValue(r,"defaultValue"),p=t.getObjectPropValue(r,`defaultValue${l}other`),c=t.getObjectPropValue(r,`defaultValue${l}ordinal${l}other`),f=t.getObjectPropValue(r,"count");let y;if("number"==typeof f)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";y=new Intl.PluralRules(e,{type:o}).select(f)}catch(e){}for(const o of a){const a=n?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`,f=t.getObjectPropValue(r,a);let g;g="string"==typeof f?f:"one"===o&&"string"==typeof u?u:n&&"string"==typeof c?c:n||"string"!=typeof p?"string"==typeof u?u:i&&y===o?i:e:p;const d=n?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:d,ns:s,defaultValue:g,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){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.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 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: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)]}return"Identifier"===e.type&&"undefined"===e.value?[]:"TemplateLiteral"===e.type?this.resolvePossibleStringValuesFromTemplateString(e):"NumericLiteral"===e.type||"BooleanLiteral"===e.type?[`${e.value}`]:[]}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??""])}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";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const u=!!a||!!p,
|
|
1
|
+
"use strict";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const u=!!a||!!p,o=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),l="JSXAttribute"===o?.type&&"JSXExpressionContainer"===o.value?.type&&"ObjectExpression"===o.value.expression.type?o.value.expression:void 0,y=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),v=!!y,f=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===f?.type&&"JSXExpressionContainer"===f.value?.type?f.value.expression:void 0;const d=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let S;if(S="JSXAttribute"===d?.type&&"StringLiteral"===d.value?.type?d.value.value:void 0,l&&(void 0===S&&(S=e.getObjectPropValue(l,"ns")),void 0===c)){const t=e.getObjectProperty(l,"context");t?.value&&(c=t.value)}const b=function(e,t){const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function i(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const n=e.expression;if("StringLiteral"===n.type)t+=n.value;else if("Identifier"===n.type)t+=`{{${n.value}}}`;else if("ObjectExpression"===n.type){const e=n.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=i(e.children);a&&n.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=i(e.children))}),t}return i(e).trim().replace(/\s{2,}/g," ")}(t.children,n);let x,m,g;if("JSXAttribute"===r?.type&&"StringLiteral"===r.value?.type)x=r.value.value;else{const e=n.extract.defaultValue;x="string"==typeof e?e:""}if("JSXAttribute"===i?.type){if("StringLiteral"===i.value?.type){if(m=i.value,g=m.value,S&&"StringLiteral"===m.type){const e=n.extract.nsSeparator??":",t=m.value;e&&t.startsWith(`${S}${e}`)&&(g=t.slice(`${S}${e}`.length),m={...m,value:g})}}else"JSXExpressionContainer"===i.value?.type&&"JSXEmptyExpression"!==i.value.expression.type&&(m=i.value.expression);if(!m)return null}return r||!g||b.trim()?!r&&b.trim()&&(x=b):x=g,{keyExpression:m,serializedChildren:b,ns:S,defaultValue:x,hasCount:u,isOrdinal:v,contextExpression:c,optionsNode:l}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";exports.createPluginContext=function(e,t,
|
|
1
|
+
"use strict";exports.createPluginContext=function(e,t,a,u){return{addKey:t=>{const a=`${t.ns??"translation"}:${t.key}`,u=t.defaultValue??t.key,l=e.get(a);if(l){const n=l.defaultValue===l.key||l.hasCount&&l.defaultValue&&l.key.includes("_")&&l.key.startsWith(l.defaultValue),s=u===t.key;n&&!s&&e.set(a,{...t,defaultValue:u})}else e.set(a,{...t,defaultValue:u})},config:Object.freeze({...a,plugins:[...t]}),logger:u,getVarFromScope:()=>{}}},exports.initializePlugins=async function(e){for(const t of e)await(t.setup?.())};
|
package/dist/cjs/syncer.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:fs/promises"),t=require("path"),o=require("chalk"),n=require("ora"),r=require("glob"),a=require("./utils/nested-object.js"),
|
|
1
|
+
"use strict";var e=require("node:fs/promises"),t=require("path"),o=require("chalk"),n=require("ora"),r=require("glob"),a=require("./utils/nested-object.js"),l=require("./utils/file-utils.js"),i=require("./utils/funnel-msg-tracker.js"),s=require("./utils/default-value.js");exports.runSyncer=async function(c){const u=n("Running i18next locale synchronizer...\n").start();try{const n=c.extract.primaryLanguage||c.locales[0]||"en",f=c.locales.filter(e=>e!==n),{output:d,keySeparator:y=".",outputFormat:g="json",indentation:h=2,defaultValue:p=""}=c.extract,w=[];let m=!1;const S=l.getOutputPath(d,n,"*"),q=await r.glob(S);if(0===q.length)return void u.warn(`No translation files found for primary language "${n}". Nothing to sync.`);for(const n of q){const r=t.basename(n).split(".")[0],i=await l.loadTranslationFile(n);if(!i){w.push(` ${o.yellow("-")} Could not read primary file: ${n}`);continue}const c=a.getNestedKeys(i,y??".");for(const n of f){const i=l.getOutputPath(d,n,r),u=t.resolve(process.cwd(),i),f=await l.loadTranslationFile(u)||{},S={};for(const e of c){const t=a.getNestedValue(f,e,y??".")??s.resolveDefaultValue(p,e,r,n);a.setNestedValue(S,e,t,y??".")}const q=JSON.stringify(f);if(JSON.stringify(S)!==q){m=!0;const n=l.serializeTranslationFile(S,g,h);await e.mkdir(t.dirname(u),{recursive:!0}),await e.writeFile(u,n),w.push(` ${o.green("✓")} Synchronized: ${i}`)}else w.push(` ${o.gray("-")} Already in sync: ${i}`)}}u.succeed(o.bold("Synchronization complete!")),w.forEach(e=>console.log(e)),m?await async function(){if(!await i.shouldShowFunnel("syncer"))return;return console.log(o.green.bold("\n✅ Sync complete.")),console.log(o.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${o.cyan("npx i18next-cli locize-migrate")}`),i.recordFunnelShown("syncer")}():console.log(o.green.bold("\n✅ All locales are already in sync."))}catch(e){u.fail(o.red("Synchronization failed.")),console.error(e)}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";exports.resolveDefaultValue=function(t,e,r,u){if("function"==typeof t)try{return t(e,r,u)}catch(t){return""}return t||""};
|
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.8.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{resolve as t,basename as e,extname as o}from"node:path";import{glob as
|
|
1
|
+
import{resolve as t,basename as e,extname as o}from"node:path";import{glob as s}from"glob";import{getNestedKeys as n,getNestedValue as r,setNestedValue as a}from"../../utils/nested-object.js";import{getOutputPath as c,loadTranslationFile as i}from"../../utils/file-utils.js";import{resolveDefaultValue as f}from"../../utils/default-value.js";function u(t){const e=`^${t.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`;return new RegExp(e)}function l(t){if("object"!=typeof t||null===t||Array.isArray(t))return t;const e={},o=Object.keys(t).sort((t,e)=>{const o=t.localeCompare(e,void 0,{sensitivity:"base"});return 0===o?t.localeCompare(e,void 0,{sensitivity:"case"}):o});for(const s of o)e[s]=l(t[s]);return e}function p(t,e,o,s,c,i,u){const{keySeparator:p=".",sort:y=!0,removeUnusedKeys:g=!0,primaryLanguage:m,defaultValue:d=""}=o.extract;let x=g?{}:JSON.parse(JSON.stringify(e));const h=n(e,p??".");for(const t of h)if(i.some(e=>e.test(t))){const o=r(e,t,p??".");a(x,t,o,p??".")}for(const{key:o,defaultValue:n}of t){const i=r(e,o,p??"."),l=!t.some(t=>t.key.startsWith(`${o}${p}`)&&t.key!==o),y="object"==typeof i&&null!==i&&(u.has(o)||!n||n===o),g="object"==typeof i&&null!==i&&l&&!u.has(o)&&!y;if(y){a(x,o,i,p??".");continue}let h;h=void 0===i||g?s===m?n||o:f(d,o,c,s):i,a(x,o,h,p??".")}if(!0===y)return l(x);if("function"==typeof y){const e={},o=Object.keys(x),s=new Map;for(const e of t){const t=!1===p?e.key:e.key.split(p)[0];s.has(t)||s.set(t,e)}o.sort((t,e)=>{if("function"==typeof y){const o=s.get(t),n=s.get(e);if(o&&n)return y(o,n)}return t.localeCompare(e,void 0,{sensitivity:"base"})});for(const t of o)e[t]=x[t];x=e}return x}async function y(n,r,a){a.extract.primaryLanguage||=a.locales[0]||"en",a.extract.secondaryLanguages||=a.locales.filter(t=>t!==a?.extract?.primaryLanguage);const f=a.extract.defaultNS??"translation",l=[...a.extract.preservePatterns||[]],y=a.extract.indentation??2;for(const t of r)l.push(`${t}.*`);const g=l.map(u),m=new Map;for(const t of n.values()){const e=t.ns||f;m.has(e)||m.set(e,[]),m.get(e).push(t)}const d=[],x=Array.isArray(a.extract.ignore)?a.extract.ignore:a.extract.ignore?[a.extract.ignore]:[];for(const n of a.locales){if(a.extract.mergeNamespaces||!a.extract.output.includes("{{namespace}}")){const e={},o=c(a.extract.output,n),s=t(process.cwd(),o),f=await i(s)||{},u=new Set([...m.keys(),...Object.keys(f)]);for(const t of u){const o=m.get(t)||[],s=f[t]||{};e[t]=p(o,s,a,n,t,g,r)}const l=JSON.stringify(f,null,y),x=JSON.stringify(e,null,y);d.push({path:s,updated:x!==l,newTranslations:e,existingTranslations:f})}else{const f=new Set(m.keys()),u=c(a.extract.output,n,"*"),l=await s(u,{ignore:x});for(const t of l)f.add(e(t,o(t)));for(const e of f){const o=m.get(e)||[],s=c(a.extract.output,n,e),f=t(process.cwd(),s),u=await i(f)||{},l=p(o,u,a,n,e,g,r),x=JSON.stringify(u,null,y),h=JSON.stringify(l,null,y);d.push({path:f,updated:h!==x,newTranslations:l,existingTranslations:u})}}}return d}export{y as getTranslations};
|
|
@@ -1 +1 @@
|
|
|
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,g="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);n=e.shift(),i=e.join(o)}!n&&r?.defaultNs&&(n=r.defaultNs),n||(n=this.config.extract.defaultNS);let p=i;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";p=`${r.keyPrefix}${e}${i}`}const f=e===a.length-1&&g||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){if(r.length>0)for(const{key:e,ns:t}of r)this.handlePluralKeys(e,t,c,o||u);else this.handlePluralKeys(p,n,c,o||u);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,s,n,r){try{const i=r?"ordinal":"cardinal",o=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:i}).resolvedOptions().pluralCategories,a=this.config.extract.pluralSeparator??"_",l=t(n,"defaultValue"),u=t(n,`defaultValue${a}other`),p=t(n,`defaultValue${a}ordinal${a}other`);for(const i of o){const o=t(n,r?`defaultValue${a}ordinal${a}${i}`:`defaultValue${a}${i}`);let f;f="string"==typeof o?o:"one"===i&&"string"==typeof l?l:r&&"string"==typeof p?p:r||"string"!=typeof u?"string"==typeof l?l:e:u;const c=r?`${e}${a}ordinal${a}${i}`:`${e}${a}${i}`;this.pluginContext.addKey({key:c,ns:s,defaultValue:f,hasCount:!0,isOrdinal:r})}}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=t(n,"defaultValue");this.pluginContext.addKey({key:e,ns:s,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){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(this.pluginContext.addKey)}}else if(a){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(this.pluginContext.addKey)}}}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)]}return"Identifier"===e.type&&"undefined"===e.value?[]:"TemplateLiteral"===e.type?this.resolvePossibleStringValuesFromTemplateString(e):"NumericLiteral"===e.type||"BooleanLiteral"===e.type?[`${e.value}`]:[]}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??""])}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
|
+
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,g="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);n=e.shift(),i=e.join(o)}!n&&r?.defaultNs&&(n=r.defaultNs),n||(n=this.config.extract.defaultNS);let p=i;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";p=`${r.keyPrefix}${e}${i}`}const f=e===a.length-1&&g||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){if(r.length>0)for(const{key:e,ns:t}of r)this.handlePluralKeys(e,t,c,o||u,g);else this.handlePluralKeys(p,n,c,o||u,g);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,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??"_",u=t(n,"defaultValue"),p=t(n,`defaultValue${l}other`),f=t(n,`defaultValue${l}ordinal${l}other`),c=t(n,"count");let y;if("number"==typeof c)try{const e=this.config.extract?.primaryLanguage||this.config.locales[0]||"en";y=new Intl.PluralRules(e,{type:o}).select(c)}catch(e){}for(const o of a){const a=t(n,r?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`);let c;c="string"==typeof a?a:"one"===o&&"string"==typeof u?u:r&&"string"==typeof f?f:r||"string"!=typeof p?"string"==typeof u?u:i&&y===o?i:e:p;const g=r?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:g,ns:s,defaultValue:c,hasCount:!0,isOrdinal:r})}}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const o=i||t(n,"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),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){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(this.pluginContext.addKey)}}else if(a){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(this.pluginContext.addKey)}}}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)]}return"Identifier"===e.type&&"undefined"===e.value?[]:"TemplateLiteral"===e.type?this.resolvePossibleStringValuesFromTemplateString(e):"NumericLiteral"===e.type||"BooleanLiteral"===e.type?[`${e.value}`]:[]}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??""])}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
|
-
import{getObjectProperty as e,getObjectPropValue as t}from"./ast-utils.js";function i(i,n){const r=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),a=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),s=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),p=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;s||"JSXAttribute"!==p?.type||"JSXExpressionContainer"!==p.value?.type||"ObjectExpression"!==p.value.expression.type||(u=e(p.value.expression,"count"));const l=!!s||!!u,o=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===o?.type&&"JSXExpressionContainer"===o.value?.type&&"ObjectExpression"===o.value.expression.type?o.value.expression:void 0,v=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),f=!!v,d=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:void 0;const S=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let m;if(m="JSXAttribute"===S?.type&&"StringLiteral"===S.value?.type?S.value.value:void 0,y&&(void 0===m&&(m=t(y,"ns")),void 0===c)){const t=e(y,"context");t?.value&&(c=t.value)}const x=function(e,t){const i=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function n(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const i=e.expression;if("StringLiteral"===i.type)t+=i.value;else if("Identifier"===i.type)t+=`{{${i.value}}}`;else if("ObjectExpression"===i.type){const e=i.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=n(e.children);a&&i.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=n(e.children))}),t}return n(e).trim().replace(/\s{2,}/g," ")}(i.children,n);let b,J,X
|
|
1
|
+
import{getObjectProperty as e,getObjectPropValue as t}from"./ast-utils.js";function i(i,n){const r=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),a=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),s=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),p=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;s||"JSXAttribute"!==p?.type||"JSXExpressionContainer"!==p.value?.type||"ObjectExpression"!==p.value.expression.type||(u=e(p.value.expression,"count"));const l=!!s||!!u,o=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===o?.type&&"JSXExpressionContainer"===o.value?.type&&"ObjectExpression"===o.value.expression.type?o.value.expression:void 0,v=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),f=!!v,d=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:void 0;const S=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let m;if(m="JSXAttribute"===S?.type&&"StringLiteral"===S.value?.type?S.value.value:void 0,y&&(void 0===m&&(m=t(y,"ns")),void 0===c)){const t=e(y,"context");t?.value&&(c=t.value)}const x=function(e,t){const i=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function n(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const i=e.expression;if("StringLiteral"===i.type)t+=i.value;else if("Identifier"===i.type)t+=`{{${i.value}}}`;else if("ObjectExpression"===i.type){const e=i.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=n(e.children);a&&i.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=n(e.children))}),t}return n(e).trim().replace(/\s{2,}/g," ")}(i.children,n);let b,J,X;if("JSXAttribute"===a?.type&&"StringLiteral"===a.value?.type)b=a.value.value;else{const e=n.extract.defaultValue;b="string"==typeof e?e:""}if("JSXAttribute"===r?.type){if("StringLiteral"===r.value?.type){if(J=r.value,X=J.value,m&&"StringLiteral"===J.type){const e=n.extract.nsSeparator??":",t=J.value;e&&t.startsWith(`${m}${e}`)&&(X=t.slice(`${m}${e}`.length),J={...J,value:X})}}else"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(J=r.value.expression);if(!J)return null}return a||!X||x.trim()?!a&&x.trim()&&(b=x):b=X,{keyExpression:J,serializedChildren:x,ns:m,defaultValue:b,hasCount:l,isOrdinal:f,contextExpression:c,optionsNode:y}}export{i as extractFromTransComponent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
async function e(e){for(const t of e)await(t.setup?.())}function t(e,t,
|
|
1
|
+
async function e(e){for(const t of e)await(t.setup?.())}function t(e,t,a,u){return{addKey:t=>{const a=`${t.ns??"translation"}:${t.key}`,u=t.defaultValue??t.key,l=e.get(a);if(l){const n=l.defaultValue===l.key||l.hasCount&&l.defaultValue&&l.key.includes("_")&&l.key.startsWith(l.defaultValue),s=u===t.key;n&&!s&&e.set(a,{...t,defaultValue:u})}else e.set(a,{...t,defaultValue:u})},config:Object.freeze({...a,plugins:[...t]}),logger:u,getVarFromScope:()=>{}}}export{t as createPluginContext,e as initializePlugins};
|
package/dist/esm/syncer.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mkdir as o,writeFile as t}from"node:fs/promises";import{basename as
|
|
1
|
+
import{mkdir as o,writeFile as t}from"node:fs/promises";import{basename as e,resolve as n,dirname as r}from"path";import i from"chalk";import a from"ora";import{glob as l}from"glob";import{getNestedKeys as s,getNestedValue as c,setNestedValue as f}from"./utils/nested-object.js";import{getOutputPath as u,loadTranslationFile as m,serializeTranslationFile as y}from"./utils/file-utils.js";import{shouldShowFunnel as p,recordFunnelShown as d}from"./utils/funnel-msg-tracker.js";import{resolveDefaultValue as g}from"./utils/default-value.js";async function h(h){const w=a("Running i18next locale synchronizer...\n").start();try{const a=h.extract.primaryLanguage||h.locales[0]||"en",S=h.locales.filter(o=>o!==a),{output:$,keySeparator:x=".",outputFormat:b="json",indentation:j=2,defaultValue:z=""}=h.extract,v=[];let N=!1;const k=u($,a,"*"),A=await l(k);if(0===A.length)return void w.warn(`No translation files found for primary language "${a}". Nothing to sync.`);for(const a of A){const l=e(a).split(".")[0],p=await m(a);if(!p){v.push(` ${i.yellow("-")} Could not read primary file: ${a}`);continue}const d=s(p,x??".");for(const e of S){const a=u($,e,l),s=n(process.cwd(),a),p=await m(s)||{},h={};for(const o of d){const t=c(p,o,x??".")??g(z,o,l,e);f(h,o,t,x??".")}const w=JSON.stringify(p);if(JSON.stringify(h)!==w){N=!0;const e=y(h,b,j);await o(r(s),{recursive:!0}),await t(s,e),v.push(` ${i.green("✓")} Synchronized: ${a}`)}else v.push(` ${i.gray("-")} Already in sync: ${a}`)}}w.succeed(i.bold("Synchronization complete!")),v.forEach(o=>console.log(o)),N?await async function(){if(!await p("syncer"))return;return console.log(i.green.bold("\n✅ Sync complete.")),console.log(i.yellow("🚀 Ready to collaborate with translators? Move your files to the cloud.")),console.log(` Get started with the official TMS for i18next: ${i.cyan("npx i18next-cli locize-migrate")}`),d("syncer")}():console.log(i.green.bold("\n✅ All locales are already in sync."))}catch(o){w.fail(i.red("Synchronization failed.")),console.error(o)}}export{h as runSyncer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t,r,n,e){if("function"==typeof t)try{return t(r,n,e)}catch(t){return""}return t||""}export{t as resolveDefaultValue};
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve, basename, extname } from 'node:path'
|
|
|
3
3
|
import { glob } from 'glob'
|
|
4
4
|
import { getNestedValue, setNestedValue, getNestedKeys } from '../../utils/nested-object'
|
|
5
5
|
import { getOutputPath, loadTranslationFile } from '../../utils/file-utils'
|
|
6
|
+
import { resolveDefaultValue } from '../../utils/default-value'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Converts a glob pattern to a regular expression for matching keys
|
|
@@ -52,6 +53,7 @@ function buildNewTranslationsForNs (
|
|
|
52
53
|
existingTranslations: Record<string, any>,
|
|
53
54
|
config: I18nextToolkitConfig,
|
|
54
55
|
locale: string,
|
|
56
|
+
namespace: string,
|
|
55
57
|
preservePatterns: RegExp[],
|
|
56
58
|
objectKeys: Set<string>
|
|
57
59
|
): Record<string, any> {
|
|
@@ -96,9 +98,17 @@ function buildNewTranslationsForNs (
|
|
|
96
98
|
continue
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
let valueToSet: string
|
|
102
|
+
if (existingValue === undefined || isStaleObject) {
|
|
103
|
+
if (locale === primaryLanguage) {
|
|
104
|
+
valueToSet = defaultValue || key
|
|
105
|
+
} else {
|
|
106
|
+
// For secondary languages, use the resolved default value
|
|
107
|
+
valueToSet = resolveDefaultValue(emptyDefaultValue, key, namespace, locale)
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
valueToSet = existingValue
|
|
111
|
+
}
|
|
102
112
|
|
|
103
113
|
setNestedValue(newTranslations, key, valueToSet, keySeparator ?? '.')
|
|
104
114
|
}
|
|
@@ -223,7 +233,7 @@ export async function getTranslations (
|
|
|
223
233
|
for (const ns of namespacesToProcess) {
|
|
224
234
|
const nsKeys = keysByNS.get(ns) || []
|
|
225
235
|
const existingTranslations = existingMergedFile[ns] || {}
|
|
226
|
-
newMergedTranslations[ns] = buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale, preservePatterns, objectKeys)
|
|
236
|
+
newMergedTranslations[ns] = buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale, ns, preservePatterns, objectKeys)
|
|
227
237
|
}
|
|
228
238
|
|
|
229
239
|
const oldContent = JSON.stringify(existingMergedFile, null, indentation)
|
|
@@ -247,7 +257,7 @@ export async function getTranslations (
|
|
|
247
257
|
const outputPath = getOutputPath(config.extract.output, locale, ns)
|
|
248
258
|
const fullPath = resolve(process.cwd(), outputPath)
|
|
249
259
|
const existingTranslations = await loadTranslationFile(fullPath) || {}
|
|
250
|
-
const newTranslations = buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale, preservePatterns, objectKeys)
|
|
260
|
+
const newTranslations = buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale, ns, preservePatterns, objectKeys)
|
|
251
261
|
|
|
252
262
|
const oldContent = JSON.stringify(existingTranslations, null, indentation)
|
|
253
263
|
const newContent = JSON.stringify(newTranslations, null, indentation)
|
|
@@ -597,12 +597,12 @@ export class ASTVisitors {
|
|
|
597
597
|
// If we have keys with context pluralize them
|
|
598
598
|
if (keysWithContext.length > 0) {
|
|
599
599
|
for (const { key, ns } of keysWithContext) {
|
|
600
|
-
// Pass the combined ordinal flag to the handler
|
|
601
|
-
this.handlePluralKeys(key, ns, options, isOrdinalByOption || isOrdinalByKey)
|
|
600
|
+
// Pass the combined ordinal flag and the default value to the handler
|
|
601
|
+
this.handlePluralKeys(key, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue)
|
|
602
602
|
}
|
|
603
603
|
} else {
|
|
604
604
|
// Otherwise pluralize the base key
|
|
605
|
-
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey)
|
|
605
|
+
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue)
|
|
606
606
|
}
|
|
607
607
|
|
|
608
608
|
continue // This key is fully handled
|
|
@@ -683,7 +683,7 @@ export class ASTVisitors {
|
|
|
683
683
|
*
|
|
684
684
|
* @private
|
|
685
685
|
*/
|
|
686
|
-
private handlePluralKeys (key: string, ns: string | undefined, options: ObjectExpression, isOrdinal: boolean): void {
|
|
686
|
+
private handlePluralKeys (key: string, ns: string | undefined, options: ObjectExpression, isOrdinal: boolean, defaultValueFromCall?: string): void {
|
|
687
687
|
try {
|
|
688
688
|
const type = isOrdinal ? 'ordinal' : 'cardinal'
|
|
689
689
|
|
|
@@ -695,6 +695,20 @@ export class ASTVisitors {
|
|
|
695
695
|
const otherDefault = getObjectPropValue(options, `defaultValue${pluralSeparator}other`)
|
|
696
696
|
const ordinalOtherDefault = getObjectPropValue(options, `defaultValue${pluralSeparator}ordinal${pluralSeparator}other`)
|
|
697
697
|
|
|
698
|
+
// Get the count value and determine target category if available
|
|
699
|
+
const countValue = getObjectPropValue(options, 'count')
|
|
700
|
+
let targetCategory: string | undefined
|
|
701
|
+
|
|
702
|
+
if (typeof countValue === 'number') {
|
|
703
|
+
try {
|
|
704
|
+
const primaryLanguage = this.config.extract?.primaryLanguage || this.config.locales[0] || 'en'
|
|
705
|
+
const pluralRules = new Intl.PluralRules(primaryLanguage, { type })
|
|
706
|
+
targetCategory = pluralRules.select(countValue)
|
|
707
|
+
} catch (e) {
|
|
708
|
+
// If we can't determine the category, continue with normal logic
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
698
712
|
for (const category of pluralCategories) {
|
|
699
713
|
// 1. Look for the most specific default value (e.g., defaultValue_ordinal_one)
|
|
700
714
|
const specificDefaultKey = isOrdinal ? `defaultValue${pluralSeparator}ordinal${pluralSeparator}${category}` : `defaultValue${pluralSeparator}${category}`
|
|
@@ -703,7 +717,7 @@ export class ASTVisitors {
|
|
|
703
717
|
// 2. Determine the final default value using a clear fallback chain
|
|
704
718
|
let finalDefaultValue: string | undefined
|
|
705
719
|
if (typeof specificDefault === 'string') {
|
|
706
|
-
// 1. Use the most specific default if it exists (e.g.,
|
|
720
|
+
// 1. HIGHEST PRIORITY: Use the most specific default if it exists (e.g., defaultValue_few)
|
|
707
721
|
finalDefaultValue = specificDefault
|
|
708
722
|
} else if (category === 'one' && typeof defaultValue === 'string') {
|
|
709
723
|
// 2. SPECIAL CASE: The 'one' category falls back to the main 'defaultValue' prop
|
|
@@ -717,8 +731,12 @@ export class ASTVisitors {
|
|
|
717
731
|
} else if (typeof defaultValue === 'string') {
|
|
718
732
|
// 4. If no '_other' is found, all categories can fall back to the main 'defaultValue'
|
|
719
733
|
finalDefaultValue = defaultValue
|
|
734
|
+
} else if (defaultValueFromCall && targetCategory === category) {
|
|
735
|
+
// 5. LOWER PRIORITY: If we have a default value from the t() call and this category matches the count,
|
|
736
|
+
// use the default value from the call (only if no other defaults exist)
|
|
737
|
+
finalDefaultValue = defaultValueFromCall
|
|
720
738
|
} else {
|
|
721
|
-
//
|
|
739
|
+
// 6. Final fallback to the base key itself
|
|
722
740
|
finalDefaultValue = key
|
|
723
741
|
}
|
|
724
742
|
|
|
@@ -738,7 +756,7 @@ export class ASTVisitors {
|
|
|
738
756
|
} catch (e) {
|
|
739
757
|
this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`)
|
|
740
758
|
// Fallback to a simple key if Intl API fails
|
|
741
|
-
const defaultValue = getObjectPropValue(options, 'defaultValue')
|
|
759
|
+
const defaultValue = defaultValueFromCall || getObjectPropValue(options, 'defaultValue')
|
|
742
760
|
this.pluginContext.addKey({ key, ns, defaultValue: typeof defaultValue === 'string' ? defaultValue : key })
|
|
743
761
|
}
|
|
744
762
|
}
|
|
@@ -789,7 +807,13 @@ export class ASTVisitors {
|
|
|
789
807
|
key = parts.join(nsSeparator)
|
|
790
808
|
}
|
|
791
809
|
|
|
792
|
-
return {
|
|
810
|
+
return {
|
|
811
|
+
key,
|
|
812
|
+
ns,
|
|
813
|
+
defaultValue: defaultValue || serializedChildren,
|
|
814
|
+
hasCount,
|
|
815
|
+
isOrdinal,
|
|
816
|
+
}
|
|
793
817
|
})
|
|
794
818
|
|
|
795
819
|
const tProp = node.opening.attributes?.find(
|
|
@@ -818,7 +842,13 @@ export class ASTVisitors {
|
|
|
818
842
|
} else {
|
|
819
843
|
const { ns } = extractedAttributes
|
|
820
844
|
extractedKeys = keysToProcess.map(key => {
|
|
821
|
-
return {
|
|
845
|
+
return {
|
|
846
|
+
key,
|
|
847
|
+
ns,
|
|
848
|
+
defaultValue: defaultValue || serializedChildren,
|
|
849
|
+
hasCount,
|
|
850
|
+
isOrdinal,
|
|
851
|
+
}
|
|
822
852
|
})
|
|
823
853
|
}
|
|
824
854
|
|
|
@@ -152,9 +152,22 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
152
152
|
|
|
153
153
|
const serialized = serializeJSXChildren(node.children, config)
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
// Handle default value properly
|
|
156
|
+
let defaultValue: string
|
|
157
|
+
|
|
156
158
|
if (defaultsAttr?.type === 'JSXAttribute' && defaultsAttr.value?.type === 'StringLiteral') {
|
|
159
|
+
// Explicit defaults attribute takes precedence
|
|
157
160
|
defaultValue = defaultsAttr.value.value
|
|
161
|
+
} else {
|
|
162
|
+
// Use the configured default value or fall back to empty string
|
|
163
|
+
const configuredDefault = config.extract.defaultValue
|
|
164
|
+
if (typeof configuredDefault === 'string') {
|
|
165
|
+
defaultValue = configuredDefault
|
|
166
|
+
} else {
|
|
167
|
+
// For function-based defaults or undefined, use empty string as placeholder
|
|
168
|
+
// The translation manager will handle function resolution with proper context
|
|
169
|
+
defaultValue = ''
|
|
170
|
+
}
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
let keyExpression: Expression | undefined
|
|
@@ -49,9 +49,30 @@ export function createPluginContext (allKeys: Map<string, ExtractedKey>, plugins
|
|
|
49
49
|
addKey: (keyInfo: ExtractedKey) => {
|
|
50
50
|
// Use namespace in the unique map key to avoid collisions across namespaces
|
|
51
51
|
const uniqueKey = `${keyInfo.ns ?? 'translation'}:${keyInfo.key}`
|
|
52
|
+
const defaultValue = keyInfo.defaultValue ?? keyInfo.key
|
|
52
53
|
|
|
53
|
-
if
|
|
54
|
-
|
|
54
|
+
// Check if key already exists
|
|
55
|
+
const existingKey = allKeys.get(uniqueKey)
|
|
56
|
+
|
|
57
|
+
if (existingKey) {
|
|
58
|
+
// Check if existing value is a generic fallback
|
|
59
|
+
// For plural keys, the fallback is often the base key (e.g., "item.count" for "item.count_other")
|
|
60
|
+
// For regular keys, the fallback is the key itself
|
|
61
|
+
const isExistingGenericFallback =
|
|
62
|
+
existingKey.defaultValue === existingKey.key || // Regular key fallback
|
|
63
|
+
(existingKey.hasCount && existingKey.defaultValue &&
|
|
64
|
+
existingKey.key.includes('_') &&
|
|
65
|
+
existingKey.key.startsWith(existingKey.defaultValue)) // Plural key with base key fallback
|
|
66
|
+
|
|
67
|
+
const isNewGenericFallback = defaultValue === keyInfo.key
|
|
68
|
+
|
|
69
|
+
// If existing value is a generic fallback and new value is specific, replace it
|
|
70
|
+
if (isExistingGenericFallback && !isNewGenericFallback) {
|
|
71
|
+
allKeys.set(uniqueKey, { ...keyInfo, defaultValue })
|
|
72
|
+
}
|
|
73
|
+
// Otherwise keep the existing one
|
|
74
|
+
} else {
|
|
75
|
+
// New key, just add it
|
|
55
76
|
allKeys.set(uniqueKey, { ...keyInfo, defaultValue })
|
|
56
77
|
}
|
|
57
78
|
},
|
package/src/syncer.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { I18nextToolkitConfig } from './types'
|
|
|
7
7
|
import { getNestedKeys, getNestedValue, setNestedValue } from './utils/nested-object'
|
|
8
8
|
import { getOutputPath, loadTranslationFile, serializeTranslationFile } from './utils/file-utils'
|
|
9
9
|
import { shouldShowFunnel, recordFunnelShown } from './utils/funnel-msg-tracker'
|
|
10
|
+
import { resolveDefaultValue } from './utils/default-value'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Synchronizes translation files across different locales by ensuring all secondary
|
|
@@ -84,7 +85,9 @@ export async function runSyncer (config: I18nextToolkitConfig) {
|
|
|
84
85
|
|
|
85
86
|
for (const key of primaryKeys) {
|
|
86
87
|
const existingValue = getNestedValue(existingSecondaryTranslations, key, keySeparator ?? '.')
|
|
87
|
-
|
|
88
|
+
|
|
89
|
+
// Use the resolved default value if no existing value
|
|
90
|
+
const valueToSet = existingValue ?? resolveDefaultValue(defaultValue, key, ns, lang)
|
|
88
91
|
setNestedValue(newSecondaryTranslations, key, valueToSet, keySeparator ?? '.')
|
|
89
92
|
}
|
|
90
93
|
|
package/src/types.ts
CHANGED
|
@@ -88,7 +88,7 @@ export interface I18nextToolkitConfig {
|
|
|
88
88
|
indentation?: number | string;
|
|
89
89
|
|
|
90
90
|
/** Default value to use for missing translations in secondary languages */
|
|
91
|
-
defaultValue?: string;
|
|
91
|
+
defaultValue?: string | ((key: string, namespace: string, language: string) => string);
|
|
92
92
|
|
|
93
93
|
/** Primary language that provides default values (default: first locale) */
|
|
94
94
|
primaryLanguage?: string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the default value for a missing key in secondary languages.
|
|
3
|
+
* Supports both string and function-based default values.
|
|
4
|
+
*
|
|
5
|
+
* @param defaultValue - The configured default value (string or function)
|
|
6
|
+
* @param key - The translation key
|
|
7
|
+
* @param namespace - The namespace for the key
|
|
8
|
+
* @param language - The target language
|
|
9
|
+
* @returns The resolved default value
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // String-based default value
|
|
14
|
+
* const result1 = resolveDefaultValue('[MISSING]', 'user.name', 'common', 'de')
|
|
15
|
+
* // Returns: '[MISSING]'
|
|
16
|
+
*
|
|
17
|
+
* // Function-based default value
|
|
18
|
+
* const defaultValueFn = (key, ns, lang) => `${lang.toUpperCase()}_${ns}_${key}`
|
|
19
|
+
* const result2 = resolveDefaultValue(defaultValueFn, 'user.name', 'common', 'de')
|
|
20
|
+
* // Returns: 'DE_common_user.name'
|
|
21
|
+
*
|
|
22
|
+
* // Error handling - function throws
|
|
23
|
+
* const errorFn = () => { throw new Error('Oops') }
|
|
24
|
+
* const result3 = resolveDefaultValue(errorFn, 'user.name', 'common', 'de')
|
|
25
|
+
* // Returns: '' (fallback to empty string)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function resolveDefaultValue (
|
|
29
|
+
defaultValue: string | ((key: string, namespace: string, language: string) => string) | undefined,
|
|
30
|
+
key: string,
|
|
31
|
+
namespace: string,
|
|
32
|
+
language: string
|
|
33
|
+
): string {
|
|
34
|
+
if (typeof defaultValue === 'function') {
|
|
35
|
+
try {
|
|
36
|
+
return defaultValue(key, namespace, language)
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// If the function throws an error, fall back to empty string
|
|
39
|
+
return ''
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return defaultValue || ''
|
|
44
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AA+JnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA8E9B"}
|
|
@@ -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,EAAmB,MAAM,WAAW,CAAA;AACrK,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;IAoK5B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;
|
|
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,EAAmB,MAAM,WAAW,CAAA;AACrK,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;IAoK5B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IA8ExB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAwJxB;;;;;;;;;;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;IA4BjD;;;;;;;OAOG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|
|
@@ -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,CAiKxH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjG;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/extractor/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjG;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAItE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CA0CxK"}
|
package/types/syncer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../src/syncer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../src/syncer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAMnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,SAAS,CAAE,MAAM,EAAE,oBAAoB,iBAgF5D"}
|
package/types/types.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ export interface I18nextToolkitConfig {
|
|
|
69
69
|
/** Number of spaces for JSON indentation (default: 2) */
|
|
70
70
|
indentation?: number | string;
|
|
71
71
|
/** Default value to use for missing translations in secondary languages */
|
|
72
|
-
defaultValue?: string;
|
|
72
|
+
defaultValue?: string | ((key: string, namespace: string, language: string) => string);
|
|
73
73
|
/** Primary language that provides default values (default: first locale) */
|
|
74
74
|
primaryLanguage?: string;
|
|
75
75
|
/** Secondary languages that get empty values initially */
|
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,CAAC;
|
|
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;KAC5B,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;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5F;;;;;;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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the default value for a missing key in secondary languages.
|
|
3
|
+
* Supports both string and function-based default values.
|
|
4
|
+
*
|
|
5
|
+
* @param defaultValue - The configured default value (string or function)
|
|
6
|
+
* @param key - The translation key
|
|
7
|
+
* @param namespace - The namespace for the key
|
|
8
|
+
* @param language - The target language
|
|
9
|
+
* @returns The resolved default value
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // String-based default value
|
|
14
|
+
* const result1 = resolveDefaultValue('[MISSING]', 'user.name', 'common', 'de')
|
|
15
|
+
* // Returns: '[MISSING]'
|
|
16
|
+
*
|
|
17
|
+
* // Function-based default value
|
|
18
|
+
* const defaultValueFn = (key, ns, lang) => `${lang.toUpperCase()}_${ns}_${key}`
|
|
19
|
+
* const result2 = resolveDefaultValue(defaultValueFn, 'user.name', 'common', 'de')
|
|
20
|
+
* // Returns: 'DE_common_user.name'
|
|
21
|
+
*
|
|
22
|
+
* // Error handling - function throws
|
|
23
|
+
* const errorFn = () => { throw new Error('Oops') }
|
|
24
|
+
* const result3 = resolveDefaultValue(errorFn, 'user.name', 'common', 'de')
|
|
25
|
+
* // Returns: '' (fallback to empty string)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveDefaultValue(defaultValue: string | ((key: string, namespace: string, language: string) => string) | undefined, key: string, namespace: string, language: string): string;
|
|
29
|
+
//# sourceMappingURL=default-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-value.d.ts","sourceRoot":"","sources":["../../src/utils/default-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,EACjG,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAWR"}
|