i18next-cli 1.7.0 → 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 +8 -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/comment-parser.js +1 -1
- package/dist/cjs/extractor/parsers/jsx-parser.js +1 -1
- package/dist/cjs/extractor/plugin-manager.js +1 -1
- package/dist/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/comment-parser.js +1 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +1 -1
- package/dist/esm/extractor/plugin-manager.js +1 -1
- package/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 -36
- package/src/extractor/parsers/comment-parser.ts +78 -37
- 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 +0 -10
- package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/comment-parser.d.ts.map +1 -1
- package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
- package/types/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,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.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
|
+
|
|
12
|
+
## [1.7.1](https://github.com/i18next/i18next-cli/compare/v1.7.0...v1.7.1) - 2025-10-06
|
|
13
|
+
|
|
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)
|
|
15
|
+
|
|
8
16
|
## [1.7.0](https://github.com/i18next/i18next-cli/compare/v1.6.1...v1.7.9) - 2025-10-06
|
|
9
17
|
|
|
10
18
|
- **Extractor (Comments):** Improved comment extraction for complex translation patterns with both context and plural options. Commented `t()` calls like `// t('options.option', { context: 'month', count: 1 })` now correctly generate all combinations of context and plural forms (e.g., `options.option_month_one`, `options.option_month_other`, `options.option_day_one`, etc.), providing comprehensive key extraction for dynamic scenarios where developers use commented hints to declare all possible runtime values. [#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,r,s){this.pluginContext=t,this.config=e,this.logger=r,this.hooks={onBeforeVisitNode:s?.onBeforeVisitNode,onAfterVisitNode:s?.onAfterVisitNode,resolvePossibleKeyStringValues:s?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:s?.resolvePossibleContextStringValues}}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const r=e[t];if(Array.isArray(r))for(const e of r)e&&"object"==typeof e&&this.walk(e);else r&&"object"==typeof r&&this.walk(r)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e)){return this.scopeStack[t].get(e)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const r="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!r)return;const s=r.callee;if("Identifier"===s.type){const t=this.getUseTranslationConfig(s.value);if(t)return this.handleUseTranslationDeclarator(e,r,t),void this.handleUseTranslationForComments(e,r,t)}"MemberExpression"===s.type&&"Identifier"===s.property.type&&"getFixedT"===s.property.value&&this.handleGetFixedTDeclarator(e,r)}handleUseTranslationForComments(e,t,r){let s;if("Identifier"===e.id.type&&(s=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(s=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){s="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){s=t.value.value;break}}if(!s)return;const n=t.arguments?.[r.nsArg]?.expression,i=t.arguments?.[r.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===n?.type?o=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(o=n.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(s,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,r,s){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const i=r.arguments?.[s.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=r.arguments?.[s.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t.getObjectPropValue(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(n,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const r=e.id.value,s=t.arguments,n=s[1]?.expression,i=s[2]?.expression,o="StringLiteral"===n?.type?n.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(r,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const r=this.getFunctionName(e.callee);if(!r)return;const s=this.getVarFromScope(r),n=this.config.extract.functions||["t","*.t"];let i=void 0!==s;if(!i)for(const e of n)if(e.startsWith("*.")){if(r.endsWith(e.substring(1))){i=!0;break}}else if(e===r){i=!0;break}if(!i||0===e.arguments.length)return;const{keysToProcess:o,isSelectorAPI:a}=this.handleCallExpressionArgument(e,0);if(0===o.length)return;let l=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${u}ordinal`)&&(l=!0,o[e]=o[e].slice(0,-8));let p,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(p=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const f=c?t.getObjectPropValue(c,"defaultValue"):void 0,y="string"==typeof f?f:p;for(let e=0;e<o.length;e++){let r,n=o[e];if(c){const e=t.getObjectPropValue(c,"ns");"string"==typeof e&&(r=e)}const i=this.config.extract.nsSeparator??":";if(!r&&i&&n.includes(i)){const e=n.split(i);r=e.shift(),n=e.join(i)}!r&&s?.defaultNs&&(r=s.defaultNs),r||(r=this.config.extract.defaultNS);let u=n;if(s?.keyPrefix){const e=this.config.extract.keySeparator??".";u=`${s.keyPrefix}${e}${n}`}const p=e===o.length-1&&y||n;if(c){const e=t.getObjectProperty(c,"context"),s=[];if("StringLiteral"===e?.value?.type||"NumericLiteral"===e?.value.type||"BooleanLiteral"===e?.value.type){const t=`${e.value.value}`,n=this.config.extract.contextSeparator??"_";""!==t&&s.push({key:`${u}${n}${t}`,ns:r,defaultValue:p})}else if(e?.value){const t=this.resolvePossibleContextStringValues(e.value),n=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{s.push({key:`${u}${n}${e}`,ns:r,defaultValue:p})}),s.push({key:u,ns:r,defaultValue:p}))}const n=void 0!==t.getObjectPropValue(c,"count"),i=!0===t.getObjectPropValue(c,"ordinal");if(n||l){if(s.length>0)for(const{key:e,ns:t}of s)this.handlePluralKeys(e,t,c,i||l);else this.handlePluralKeys(u,r,c,i||l);continue}if(s.length>0){s.forEach(this.pluginContext.addKey);continue}!0===t.getObjectPropValue(c,"returnObjects")&&this.objectKeys.add(u)}a&&this.objectKeys.add(u),this.pluginContext.addKey({key:u,ns:r,defaultValue:p})}}handleCallExpressionArgument(e,t){const r=e.arguments[t].expression,s=[];let n=!1;if("ArrowFunctionExpression"===r.type){const e=this.extractKeyFromSelector(r);e&&(s.push(e),n=!0)}else if("ArrayExpression"===r.type)for(const e of r.elements)e?.expression&&s.push(...this.resolvePossibleKeyStringValues(e.expression));else s.push(...this.resolvePossibleKeyStringValues(r));return{keysToProcess:s.filter(e=>!!e),isSelectorAPI:n}}handlePluralKeys(e,r,s,n){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(s,"defaultValue"),u=t.getObjectPropValue(s,`defaultValue${a}other`),p=t.getObjectPropValue(s,`defaultValue${a}ordinal${a}other`);for(const i of o){const o=n?`defaultValue${a}ordinal${a}${i}`:`defaultValue${a}${i}`,c=t.getObjectPropValue(s,o);let f;f="string"==typeof c?c:"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:r,defaultValue:f,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(s,"defaultValue");this.pluginContext.addKey({key:e,ns:r,defaultValue:"string"==typeof i?i:e})}}handleSimplePluralKeys(e,t,r){try{const s=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,n=this.config.extract.pluralSeparator??"_";for(const i of s)this.pluginContext.addKey({key:`${e}${n}${i}`,ns:r,defaultValue:t,hasCount:!0})}catch(s){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:r,defaultValue:t})}}handleJSXElement(t){const r=this.getElementName(t);if(r&&(this.config.extract.transComponents||["Trans"]).includes(r)){const r=e.extractFromTransComponent(t,this.config),s=[];if(r){if(r.keyExpression){const e=this.resolvePossibleKeyStringValues(r.keyExpression);s.push(...e)}else s.push(r.serializedChildren);let e;const{contextExpression:n,optionsNode:i,defaultValue:o,hasCount:a,isOrdinal:l,serializedChildren:u}=r;if(r.ns){const{ns:t}=r;e=s.map(e=>({key:e,ns:t,defaultValue:o||u,hasCount:a,isOrdinal:l}))}else{e=s.map(e=>{const t=this.config.extract.nsSeparator??":";let r;if(t&&e.includes(t)){let s;[r,...s]=e.split(t),e=s.join(t)}return{key:e,ns:r,defaultValue:o||u,hasCount:a,isOrdinal:l}});const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===r?.type&&"JSXExpressionContainer"===r.value?.type&&"Identifier"===r.value.expression.type){const t=r.value.expression.value,s=this.getVarFromScope(t);s?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=s.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),n&&a){const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!r,o=this.resolvePossibleContextStringValues(n),a=this.config.extract.contextSeparator??"_";if(o.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i));for(const t of o)for(const r of e){const e=`${r.key}${a}${t}`;this.generatePluralKeysForTrans(e,r.defaultValue,r.ns,s,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i))}else if(n){const t=this.resolvePossibleContextStringValues(n),r=this.config.extract.contextSeparator??"_";if(t.length>0){for(const s of t)for(const{key:t,ns:n,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${r}${s}`,ns:n,defaultValue:i});"StringLiteral"!==n.type&&e.forEach(this.pluginContext.addKey)}}else if(a){const r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),s=!!r;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,s,i))}else e.forEach(this.pluginContext.addKey)}}}generatePluralKeysForTrans(e,r,s,n,i){try{const o=n?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:o}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t.getObjectPropValue(i,`defaultValue${l}other`),p=t.getObjectPropValue(i,`defaultValue${l}ordinal${l}other`));for(const o of a){const a=n?`defaultValue${l}ordinal${l}${o}`:`defaultValue${l}${o}`,c=i?t.getObjectPropValue(i,a):void 0;let f;f="string"==typeof c?c:"one"===o&&"string"==typeof r?r:n&&"string"==typeof p?p:n||"string"!=typeof u?"string"==typeof r?r:e:u;const y=n?`${e}${l}ordinal${l}${o}`:`${e}${l}${o}`;this.pluginContext.addKey({key:y,ns:s,defaultValue:f,hasCount:!0,isOrdinal:n})}}catch(t){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,ns:s,defaultValue:r})}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const r=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&r.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&r.unshift(t.value),r.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let r=t;const s=[];for(;"MemberExpression"===r.type;){const e=r.property;if("Identifier"===e.type)s.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;s.unshift(e.expression.value)}r=r.object}if(s.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return s.join(t)}return null}resolvePossibleContextStringValues(e){return[...this.hooks.resolvePossibleContextStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleKeyStringValues(e){return[...this.hooks.resolvePossibleKeyStringValues?.(e)??[],...this.resolvePossibleStringValuesFromExpression(e)]}resolvePossibleStringValuesFromExpression(e,t=!1){if("StringLiteral"===e.type)return e.value||t?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValuesFromExpression(e.consequent,t),...this.resolvePossibleStringValuesFromExpression(e.alternate,t)]}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,...r]=e.quasis;return e.expressions.reduce((e,t,s)=>e.flatMap(e=>{const n=r[s]?.cooked??"";return this.resolvePossibleStringValuesFromExpression(t,!0).map(t=>`${e}${t}${n}`)}),[t.cooked??""])}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const r of t){if("string"==typeof r&&r===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof r&&r.name===e)return{name:r.name,nsArg:r.nsArg??0,keyPrefixArg:r.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let r=e;for(;"MemberExpression"===r.type;){if("Identifier"!==r.property.type)return null;t.unshift(r.property.value),r=r.object}if("ThisExpression"===r.type)t.unshift("this");else{if("Identifier"!==r.type)return null;t.unshift(r.value)}return t.join(".")}return null}};
|
|
1
|
+
"use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];hooks;objectKeys=new Set;scope=new Map;constructor(e,t,s,r){this.pluginContext=t,this.config=e,this.logger=s,this.hooks={onBeforeVisitNode:r?.onBeforeVisitNode,onAfterVisitNode:r?.onAfterVisitNode,resolvePossibleKeyStringValues:r?.resolvePossibleKeyStringValues,resolvePossibleContextStringValues:r?.resolvePossibleContextStringValues}}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),this.hooks.onBeforeVisitNode?.(e),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}this.hooks.onAfterVisitNode?.(e);for(const t in e){if("span"===t)continue;const s=e[t];if(Array.isArray(s))for(const e of s)e&&"object"==typeof e&&this.walk(e);else s&&"object"==typeof s&&this.walk(s)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e)){return this.scopeStack[t].get(e)}const t=this.scope.get(e);if(t)return t}handleVariableDeclarator(e){const t=e.init;if(!t)return;const s="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!s)return;const r=s.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return this.handleUseTranslationDeclarator(e,s,t),void this.handleUseTranslationForComments(e,s,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,s)}handleUseTranslationForComments(e,t,s){let r;if("Identifier"===e.id.type&&(r=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(r=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){r="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){r=t.value.value;break}}if(!r)return;const n=t.arguments?.[s.nsArg]?.expression,i=t.arguments?.[s.keyPrefixArg]?.expression;let o,a;if("StringLiteral"===n?.type?o=n.value:"ArrayExpression"===n?.type&&"StringLiteral"===n.elements[0]?.expression.type&&(o=n.elements[0].expression.value),"ObjectExpression"===i?.type){const e=i.properties.find(e=>"KeyValueProperty"===e.type&&"Identifier"===e.key.type&&"keyPrefix"===e.key.value);"KeyValueProperty"===e?.type&&"StringLiteral"===e.value.type&&(a=e.value.value)}(o||a)&&this.scope.set(r,{defaultNs:o,keyPrefix:a})}handleUseTranslationDeclarator(e,s,r){let n;if("Identifier"===e.id.type&&(n=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(n=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){n="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){n=t.value.value;break}}if(!n)return;const i=s.arguments?.[r.nsArg]?.expression;let o;"StringLiteral"===i?.type?o=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(o=i.elements[0].expression.value);const a=s.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===a?.type){const e=t.getObjectPropValue(a,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(n,{defaultNs:o,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const s=e.id.value,r=t.arguments,n=r[1]?.expression,i=r[2]?.expression,o="StringLiteral"===n?.type?n.value:void 0,a="StringLiteral"===i?.type?i.value:void 0;(o||a)&&this.setVarInScope(s,{defaultNs:o,keyPrefix:a})}handleCallExpression(e){const s=this.getFunctionName(e.callee);if(!s)return;const r=this.getVarFromScope(s),n=this.config.extract.functions||["t","*.t"];let i=void 0!==r;if(!i)for(const e of n)if(e.startsWith("*.")){if(s.endsWith(e.substring(1))){i=!0;break}}else if(e===s){i=!0;break}if(!i||0===e.arguments.length)return;const{keysToProcess:o,isSelectorAPI:a}=this.handleCallExpressionArgument(e,0);if(0===o.length)return;let l=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${u}ordinal`)&&(l=!0,o[e]=o[e].slice(0,-8));let p,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";function e(e,t,n,s,
|
|
1
|
+
"use strict";function e(e,t,n,s,a,r=!1){try{const l=r?"ordinal":"cardinal",o=a.extract.primaryLanguage||a.locales[0]||"en",c=new Intl.PluralRules(o,{type:l}).resolvedOptions().pluralCategories,u=a.extract.pluralSeparator??"_";for(const a of c){const l=r?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;s.addKey({key:l,ns:n,defaultValue:t,hasCount:!0,isOrdinal:r})}}catch(a){s.addKey({key:e,ns:n,defaultValue:t})}}function t(e,t,n,s,a,r,l=!1){try{const o=l?"ordinal":"cardinal",c=r.extract.primaryLanguage||r.locales[0]||"en",u=new Intl.PluralRules(c,{type:o}).resolvedOptions().pluralCategories,i=r.extract.pluralSeparator??"_";for(const r of u){const o=l?`${e}_${s}${i}ordinal${i}${r}`:`${e}_${s}${i}${r}`;a.addKey({key:o,ns:n,defaultValue:t,hasCount:!0,isOrdinal:l})}}catch(r){a.addKey({key:`${e}_${s}`,ns:n,defaultValue:t})}}function n(e){const t=/^\s*,\s*(['"])(.*?)\1/.exec(e);if(t)return t[2];const n=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(e);return n?n[2]:void 0}function s(e){const t=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function a(e){const t=/^\s*,\s*\{[^}]*context\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function r(e){const t=/^\s*,\s*\{[^}]*count\s*:\s*(\d+)/.exec(e);if(t)return parseInt(t[1],10)}function l(e){const t=/^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(e);if(t)return"true"===t[1]}exports.extractKeysFromComments=function(o,c,u,i){const d=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(e){const t=[],n=new Set,s=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let a;for(;null!==(a=s.exec(e));){const e=(a[1]??a[2]).trim();e&&!n.has(e)&&(n.add(e),t.push(e))}return t}(o);for(const o of f){let f;for(;null!==(f=d.exec(o));){let d,y=f[2];const $=o.slice(f.index+f[0].length),p=n($),x=a($),g=r($),h=l($);let K=!1;const V=u.extract.pluralSeparator??"_";y.endsWith(`${V}ordinal`)&&(K=!0,y=y.slice(0,-(V.length+7)));const k=!0===h||K;d=s($);const S=u.extract.nsSeparator??":";if(!d&&S&&y.includes(S)){const e=y.split(S);d=e.shift(),y=e.join(S)}if(!d&&i){const e=i("t");e?.defaultNs&&(d=e.defaultNs)}d||(d=u.extract.defaultNS),x&&g?(e(y,p??y,d,c,u,k),t(y,p??y,d,x,c,u,k)):x?(c.addKey({key:y,ns:d,defaultValue:p??y}),c.addKey({key:`${y}_${x}`,ns:d,defaultValue:p??y})):g?e(y,p??y,d,c,u,k):c.addKey({key:y,ns:d,defaultValue:p??y})}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const 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})}}handleSimplePluralKeys(e,t,s){try{const n=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const i of n)this.pluginContext.addKey({key:`${e}${r}${i}`,ns:s,defaultValue:t,hasCount:!0})}catch(n){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:s,defaultValue:t})}}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
|
-
function e(e,
|
|
1
|
+
function e(e,c,u,d){const i=new RegExp("\\bt\\s*\\(\\s*(['\"])([^'\"]+)\\1","g"),f=function(e){const t=[],n=new Set,s=/\/\/(.*)|\/\*([\s\S]*?)\*\//g;let a;for(;null!==(a=s.exec(e));){const e=(a[1]??a[2]).trim();e&&!n.has(e)&&(n.add(e),t.push(e))}return t}(e);for(const e of f){let f;for(;null!==(f=i.exec(e));){let i,$=f[2];const p=e.slice(f.index+f[0].length),y=s(p),x=l(p),g=r(p),h=o(p);let V=!1;const k=u.extract.pluralSeparator??"_";$.endsWith(`${k}ordinal`)&&(V=!0,$=$.slice(0,-(k.length+7)));const K=!0===h||V;i=a(p);const S=u.extract.nsSeparator??":";if(!i&&S&&$.includes(S)){const e=$.split(S);i=e.shift(),$=e.join(S)}if(!i&&d){const e=d("t");e?.defaultNs&&(i=e.defaultNs)}i||(i=u.extract.defaultNS),x&&g?(t($,y??$,i,c,u,K),n($,y??$,i,x,c,u,K)):x?(c.addKey({key:$,ns:i,defaultValue:y??$}),c.addKey({key:`${$}_${x}`,ns:i,defaultValue:y??$})):g?t($,y??$,i,c,u,K):c.addKey({key:$,ns:i,defaultValue:y??$})}}}function t(e,t,n,s,a,l=!1){try{const r=l?"ordinal":"cardinal",o=a.extract.primaryLanguage||a.locales[0]||"en",c=new Intl.PluralRules(o,{type:r}).resolvedOptions().pluralCategories,u=a.extract.pluralSeparator??"_";for(const a of c){const r=l?`${e}${u}ordinal${u}${a}`:`${e}${u}${a}`;s.addKey({key:r,ns:n,defaultValue:t,hasCount:!0,isOrdinal:l})}}catch(a){s.addKey({key:e,ns:n,defaultValue:t})}}function n(e,t,n,s,a,l,r=!1){try{const o=r?"ordinal":"cardinal",c=l.extract.primaryLanguage||l.locales[0]||"en",u=new Intl.PluralRules(c,{type:o}).resolvedOptions().pluralCategories,d=l.extract.pluralSeparator??"_";for(const l of u){const o=r?`${e}_${s}${d}ordinal${d}${l}`:`${e}_${s}${d}${l}`;a.addKey({key:o,ns:n,defaultValue:t,hasCount:!0,isOrdinal:r})}}catch(l){a.addKey({key:`${e}_${s}`,ns:n,defaultValue:t})}}function s(e){const t=/^\s*,\s*(['"])(.*?)\1/.exec(e);if(t)return t[2];const n=/^\s*,\s*\{[^}]*defaultValue\s*:\s*(['"])(.*?)\1/.exec(e);return n?n[2]:void 0}function a(e){const t=/^\s*,\s*\{[^}]*ns\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function l(e){const t=/^\s*,\s*\{[^}]*context\s*:\s*(['"])(.*?)\1/.exec(e);if(t)return t[2]}function r(e){const t=/^\s*,\s*\{[^}]*count\s*:\s*(\d+)/.exec(e);if(t)return parseInt(t[1],10)}function o(e){const t=/^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(e);if(t)return"true"===t[1]}export{e as extractKeysFromComments};
|
|
@@ -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,38 +756,11 @@ 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
|
}
|
|
745
763
|
|
|
746
|
-
/**
|
|
747
|
-
* Generates simple plural keys, typically for a <Trans> component.
|
|
748
|
-
*
|
|
749
|
-
* @param key - Base key name for which plural keys should be generated
|
|
750
|
-
* @param defaultValue - Optional default value to associate with each plural variant
|
|
751
|
-
* @param ns - Optional namespace to use for the generated keys
|
|
752
|
-
*
|
|
753
|
-
* @private
|
|
754
|
-
*/
|
|
755
|
-
private handleSimplePluralKeys (key: string, defaultValue: string | undefined, ns: string | undefined): void {
|
|
756
|
-
try {
|
|
757
|
-
const pluralCategories = new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories
|
|
758
|
-
const pluralSeparator = this.config.extract.pluralSeparator ?? '_'
|
|
759
|
-
for (const category of pluralCategories) {
|
|
760
|
-
this.pluginContext.addKey({
|
|
761
|
-
key: `${key}${pluralSeparator}${category}`,
|
|
762
|
-
ns,
|
|
763
|
-
defaultValue,
|
|
764
|
-
hasCount: true,
|
|
765
|
-
})
|
|
766
|
-
}
|
|
767
|
-
} catch (e) {
|
|
768
|
-
this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`)
|
|
769
|
-
this.pluginContext.addKey({ key, ns, defaultValue })
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
764
|
/**
|
|
774
765
|
* Processes JSX elements to extract translation keys from Trans components.
|
|
775
766
|
*
|
|
@@ -816,7 +807,13 @@ export class ASTVisitors {
|
|
|
816
807
|
key = parts.join(nsSeparator)
|
|
817
808
|
}
|
|
818
809
|
|
|
819
|
-
return {
|
|
810
|
+
return {
|
|
811
|
+
key,
|
|
812
|
+
ns,
|
|
813
|
+
defaultValue: defaultValue || serializedChildren,
|
|
814
|
+
hasCount,
|
|
815
|
+
isOrdinal,
|
|
816
|
+
}
|
|
820
817
|
})
|
|
821
818
|
|
|
822
819
|
const tProp = node.opening.attributes?.find(
|
|
@@ -845,7 +842,13 @@ export class ASTVisitors {
|
|
|
845
842
|
} else {
|
|
846
843
|
const { ns } = extractedAttributes
|
|
847
844
|
extractedKeys = keysToProcess.map(key => {
|
|
848
|
-
return {
|
|
845
|
+
return {
|
|
846
|
+
key,
|
|
847
|
+
ns,
|
|
848
|
+
defaultValue: defaultValue || serializedChildren,
|
|
849
|
+
hasCount,
|
|
850
|
+
isOrdinal,
|
|
851
|
+
}
|
|
849
852
|
})
|
|
850
853
|
}
|
|
851
854
|
|
|
@@ -45,6 +45,18 @@ export function extractKeysFromComments (
|
|
|
45
45
|
const defaultValue = parseDefaultValueFromComment(remainder)
|
|
46
46
|
const context = parseContextFromComment(remainder)
|
|
47
47
|
const count = parseCountFromComment(remainder)
|
|
48
|
+
const ordinal = parseOrdinalFromComment(remainder)
|
|
49
|
+
|
|
50
|
+
// Check if key ends with _ordinal suffix (like in ast-visitors)
|
|
51
|
+
let isOrdinalByKey = false
|
|
52
|
+
const pluralSeparator = config.extract.pluralSeparator ?? '_'
|
|
53
|
+
if (key.endsWith(`${pluralSeparator}ordinal`)) {
|
|
54
|
+
isOrdinalByKey = true
|
|
55
|
+
// Normalize the key by stripping the suffix
|
|
56
|
+
key = key.slice(0, -(pluralSeparator.length + 7)) // Remove "_ordinal"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const isOrdinal = ordinal === true || isOrdinalByKey
|
|
48
60
|
|
|
49
61
|
// 1. Check for namespace in options object first (e.g., { ns: 'common' })
|
|
50
62
|
ns = parseNsFromComment(remainder)
|
|
@@ -72,15 +84,15 @@ export function extractKeysFromComments (
|
|
|
72
84
|
// 5. Handle context and count combinations
|
|
73
85
|
if (context && count) {
|
|
74
86
|
// Generate all combinations: base plural + context+plural
|
|
75
|
-
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config)
|
|
76
|
-
generateContextPluralKeys(key, defaultValue ?? key, ns, context, pluginContext, config)
|
|
87
|
+
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config, isOrdinal)
|
|
88
|
+
generateContextPluralKeys(key, defaultValue ?? key, ns, context, pluginContext, config, isOrdinal)
|
|
77
89
|
} else if (context) {
|
|
78
90
|
// Just context variants
|
|
79
91
|
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
80
92
|
pluginContext.addKey({ key: `${key}_${context}`, ns, defaultValue: defaultValue ?? key })
|
|
81
93
|
} else if (count) {
|
|
82
94
|
// Just plural variants
|
|
83
|
-
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config)
|
|
95
|
+
generatePluralKeys(key, defaultValue ?? key, ns, pluginContext, config, isOrdinal)
|
|
84
96
|
} else {
|
|
85
97
|
// Simple key
|
|
86
98
|
pluginContext.addKey({ key, ns, defaultValue: defaultValue ?? key })
|
|
@@ -97,26 +109,32 @@ function generatePluralKeys (
|
|
|
97
109
|
defaultValue: string,
|
|
98
110
|
ns: string | undefined,
|
|
99
111
|
pluginContext: PluginContext,
|
|
100
|
-
config: I18nextToolkitConfig
|
|
112
|
+
config: I18nextToolkitConfig,
|
|
113
|
+
isOrdinal = false
|
|
101
114
|
): void {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const categories = new Set<string>()
|
|
115
|
+
try {
|
|
116
|
+
const type = isOrdinal ? 'ordinal' : 'cardinal'
|
|
117
|
+
const primaryLanguage = config.extract.primaryLanguage || config.locales[0] || 'en'
|
|
118
|
+
const pluralCategories = new Intl.PluralRules(primaryLanguage, { type }).resolvedOptions().pluralCategories
|
|
119
|
+
const pluralSeparator = config.extract.pluralSeparator ?? '_'
|
|
108
120
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
// Generate keys for each plural category
|
|
122
|
+
for (const category of pluralCategories) {
|
|
123
|
+
const finalKey = isOrdinal
|
|
124
|
+
? `${key}${pluralSeparator}ordinal${pluralSeparator}${category}`
|
|
125
|
+
: `${key}${pluralSeparator}${category}`
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
pluginContext.addKey({
|
|
128
|
+
key: finalKey,
|
|
129
|
+
ns,
|
|
130
|
+
defaultValue,
|
|
131
|
+
hasCount: true,
|
|
132
|
+
isOrdinal
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
} catch (e) {
|
|
136
|
+
// Fallback if Intl API fails
|
|
137
|
+
pluginContext.addKey({ key, ns, defaultValue })
|
|
120
138
|
}
|
|
121
139
|
}
|
|
122
140
|
|
|
@@ -129,26 +147,32 @@ function generateContextPluralKeys (
|
|
|
129
147
|
ns: string | undefined,
|
|
130
148
|
context: string,
|
|
131
149
|
pluginContext: PluginContext,
|
|
132
|
-
config: I18nextToolkitConfig
|
|
150
|
+
config: I18nextToolkitConfig,
|
|
151
|
+
isOrdinal = false
|
|
133
152
|
): void {
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
try {
|
|
154
|
+
const type = isOrdinal ? 'ordinal' : 'cardinal'
|
|
155
|
+
const primaryLanguage = config.extract.primaryLanguage || config.locales[0] || 'en'
|
|
156
|
+
const pluralCategories = new Intl.PluralRules(primaryLanguage, { type }).resolvedOptions().pluralCategories
|
|
157
|
+
const pluralSeparator = config.extract.pluralSeparator ?? '_'
|
|
136
158
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
// Generate keys for each context + plural combination
|
|
160
|
+
for (const category of pluralCategories) {
|
|
161
|
+
const finalKey = isOrdinal
|
|
162
|
+
? `${key}_${context}${pluralSeparator}ordinal${pluralSeparator}${category}`
|
|
163
|
+
: `${key}_${context}${pluralSeparator}${category}`
|
|
140
164
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
165
|
+
pluginContext.addKey({
|
|
166
|
+
key: finalKey,
|
|
167
|
+
ns,
|
|
168
|
+
defaultValue,
|
|
169
|
+
hasCount: true,
|
|
170
|
+
isOrdinal
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// Fallback if Intl API fails
|
|
175
|
+
pluginContext.addKey({ key: `${key}_${context}`, ns, defaultValue })
|
|
152
176
|
}
|
|
153
177
|
}
|
|
154
178
|
|
|
@@ -250,3 +274,20 @@ function parseCountFromComment (remainder: string): number | undefined {
|
|
|
250
274
|
|
|
251
275
|
return undefined
|
|
252
276
|
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Parses ordinal flag from the remainder of a comment after a translation function call.
|
|
280
|
+
* Looks for ordinal specified in options object syntax.
|
|
281
|
+
*
|
|
282
|
+
* @param remainder - The remaining text after the translation key
|
|
283
|
+
* @returns The parsed ordinal value or undefined if none found
|
|
284
|
+
*
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
function parseOrdinalFromComment (remainder: string): boolean | undefined {
|
|
288
|
+
// Look for ordinal in an options object, e.g., { ordinal: true }
|
|
289
|
+
const ordinalObj = /^\s*,\s*\{[^}]*ordinal\s*:\s*(true|false)/.exec(remainder)
|
|
290
|
+
if (ordinalObj) return ordinalObj[1] === 'true'
|
|
291
|
+
|
|
292
|
+
return undefined
|
|
293
|
+
}
|
|
@@ -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"}
|
|
@@ -195,16 +195,6 @@ export declare class ASTVisitors {
|
|
|
195
195
|
* @private
|
|
196
196
|
*/
|
|
197
197
|
private handlePluralKeys;
|
|
198
|
-
/**
|
|
199
|
-
* Generates simple plural keys, typically for a <Trans> component.
|
|
200
|
-
*
|
|
201
|
-
* @param key - Base key name for which plural keys should be generated
|
|
202
|
-
* @param defaultValue - Optional default value to associate with each plural variant
|
|
203
|
-
* @param ns - Optional namespace to use for the generated keys
|
|
204
|
-
*
|
|
205
|
-
* @private
|
|
206
|
-
*/
|
|
207
|
-
private handleSimplePluralKeys;
|
|
208
198
|
/**
|
|
209
199
|
* Processes JSX elements to extract translation keys from Trans components.
|
|
210
200
|
*
|
|
@@ -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":"comment-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/comment-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,oBAAoB,EAC5B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAC1F,IAAI,
|
|
1
|
+
{"version":3,"file":"comment-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/comment-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,oBAAoB,EAC5B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAC1F,IAAI,CAyEN"}
|
|
@@ -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"}
|