vue-i18n-extract-plugin 1.0.78 → 1.0.80

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.
@@ -0,0 +1,20 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
4
+ "[javascript]": {
5
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
6
+ },
7
+ "[typescriptreact]": {
8
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
9
+ },
10
+ "[vue]": {
11
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
12
+ },
13
+ "editor.codeActionsOnSave": {
14
+ "source.fixAll": "explicit",
15
+ "source.fixAll.eslint": "explicit",
16
+ "source.fixAll.stylelint": "explicit",
17
+ "source.organizeImports": "never"
18
+ },
19
+ "cSpell.words": []
20
+ }
package/README.md CHANGED
@@ -48,7 +48,9 @@ extractI18n(options)
48
48
  ```javascript
49
49
  const defaultOptions = {
50
50
  translateKey: "$t", // 提取的函数的名称
51
+ useTranslationIdentifier: "useTranslation", // 注入到组件的hook名称, 会注入const { $t } = useTranslation()
51
52
  JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" msg="xxx" />
53
+ injectUseTranslation: true, // 是否自动注入 useTranslation
52
54
  jsx: false, // 是否启用 JSX 语法转换,开启后JSX里纯文件将转换为 <Trans id="aaa" msg="xxx" />而不是 $t("aaa")
53
55
  rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
54
56
  extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
@@ -70,6 +72,7 @@ const defaultOptions = {
70
72
  i18nPkgImportPath: "@/i18n", // i18n语言包导入路径
71
73
  outputPath: "src/i18n", // 提取的语言包输出文件路径
72
74
  generateId: null, // 自定义生成 key 的函数
75
+ shouldExtract: null, // 自定义是否提取文件的函数
73
76
  customGenLangFileName: langKey => langKey, // 自定义生成语言文件名
74
77
  // 翻译后的文本处理函数,方便对翻译后的文本进行二次加工,如每个单词首字母大写, params: text: 翻译后的文本, toLang: 翻译后的目标语言,translateLangKeys的枚举成员
75
78
  customTranslatedText: (text, toLang) => text,
@@ -12,7 +12,9 @@ module.exports = function () {
12
12
  JSXElement,
13
13
  jsx,
14
14
  enabled,
15
- autoImportI18n
15
+ autoImportI18n,
16
+ injectUseTranslation,
17
+ useTranslationIdentifier
16
18
  } = {
17
19
  ...defaultOptions,
18
20
  ...state.opts
@@ -21,7 +23,11 @@ module.exports = function () {
21
23
  if (!enabled || !autoImportI18n) return;
22
24
 
23
25
  const ast = path.parentPath?.node || path.node;
24
- const importNames = jsx ? [importName, JSXElement] : [importName];
26
+ const importNames = jsx
27
+ ? [importName, JSXElement]
28
+ : injectUseTranslation
29
+ ? [importName, useTranslationIdentifier]
30
+ : [importName];
25
31
 
26
32
  for (const importName of importNames) {
27
33
  i18nImportAstTransform(ast, importName, importPath);
package/lib/extract.js CHANGED
@@ -173,7 +173,9 @@ function createDirectiveFromProp(prop, content) {
173
173
  function addI18nImportIfNeeded(ast, options, generateCode) {
174
174
  const importNames = options.jsx
175
175
  ? [options.translateKey, options.JSXElement]
176
- : [options.translateKey];
176
+ : options.injectUseTranslation
177
+ ? [options.translateKey, options.useTranslationIdentifier]
178
+ : [options.translateKey];
177
179
 
178
180
  for (const importName of importNames) {
179
181
  i18nImportAstTransform(ast, importName, options.i18nPkgImportPath);
@@ -274,6 +276,10 @@ function transformScript(code, options, useAst, i18nMap) {
274
276
  };
275
277
  }
276
278
 
279
+ function _shouldExtract(text, options) {
280
+ return (options.shouldExtract || shouldExtract)(text, options.fromLang);
281
+ }
282
+
277
283
  function transformTemplate(templateContent, options) {
278
284
  const innerI18nMap = {};
279
285
  let magicString;
@@ -363,7 +369,7 @@ function transformTemplate(templateContent, options) {
363
369
  if (
364
370
  text &&
365
371
  typeof text === "string" &&
366
- shouldExtract(text, options.fromLang)
372
+ _shouldExtract(text, options)
367
373
  ) {
368
374
  try {
369
375
  const newValue = transformScriptExpression(
@@ -400,7 +406,7 @@ function transformTemplate(templateContent, options) {
400
406
  if (
401
407
  text &&
402
408
  typeof text === "string" &&
403
- shouldExtract(text, options.fromLang)
409
+ _shouldExtract(text, options)
404
410
  ) {
405
411
  try {
406
412
  const newValue = extractTCallsFromInterpolation(
package/lib/options.js CHANGED
@@ -2,7 +2,9 @@ const { GoogleTranslator } = require("./translators");
2
2
 
3
3
  const defaultOptions = {
4
4
  translateKey: "$t", // 提取的函数的名称
5
+ useTranslationIdentifier: "useTranslation", // 注入到组件的hook名称, 会注入const { $t } = useTranslation()
5
6
  JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" msg="xxx" />
7
+ injectUseTranslation: false, // 是否自动注入 useTranslation
6
8
  jsx: false, // 是否启用 JSX 语法转换,开启后JSX里纯文件将转换为 <Trans id="aaa" msg="xxx" />而不是 $t("aaa")
7
9
  rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
8
10
  extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
@@ -18,12 +20,22 @@ const defaultOptions = {
18
20
  excludedCall: [], // 排除的调用函数名称数组
19
21
  includePath: ["src/"], // 包含路径的数组
20
22
  excludedPath: [], // 排除路径的数组
21
- allowedExtensions: [".vue", ".nvue", ".uvue", ".tsx", ".ts", ".jsx", ".js", ".uts"], // 允许提取的文件扩展名
23
+ allowedExtensions: [
24
+ ".vue",
25
+ ".nvue",
26
+ ".uvue",
27
+ ".tsx",
28
+ ".ts",
29
+ ".jsx",
30
+ ".js",
31
+ ".uts"
32
+ ], // 允许提取的文件扩展名
22
33
  fromLang: "zh-cn", // 源语言, 目前支持提取的语言有:zh-cn(zh-tw), en, ja, ko, ru
23
34
  translateLangKeys: ["zh-tw", "en"], // 需要翻译为的语言键
24
35
  i18nPkgImportPath: "@/i18n", // i18n语言包导入路径
25
36
  outputPath: "src/i18n", // 提取的语言包输出文件路径
26
37
  generateId: null, // 自定义生成 key 的函数
38
+ shouldExtract: null, // 自定义是否提取文件的函数
27
39
  customGenLangFileName: langKey => langKey, // 自定义生成语言文件名
28
40
  // 翻译后的文本处理函数,方便对翻译后的文本进行二次加工,如每个单词首字母大写, params: text: 翻译后的文本, toLang: 翻译后的目标语言,translateLangKeys的枚举成员
29
41
  customTranslatedText: (text, toLang) => text,
package/lib/translate.js CHANGED
@@ -256,7 +256,8 @@ async function translateChunks(transLangObj, toTranslateLang, option) {
256
256
  translationChunks[i],
257
257
  option.fromLang,
258
258
  toTranslateLang,
259
- SEPARATOR
259
+ SEPARATOR,
260
+ option.generateId
260
261
  )
261
262
  );
262
263
  }
@@ -46,10 +46,16 @@ class Translator {
46
46
  }
47
47
  }
48
48
 
49
- async translate(text, fromKey, toKey, separator) {
49
+ async translate(text, fromKey, toKey, separator, customGenerateId) {
50
50
  let result = "";
51
51
  try {
52
- result = await this.option.fetchMethod(text, fromKey, toKey, separator);
52
+ result = await this.option.fetchMethod(
53
+ text,
54
+ fromKey,
55
+ toKey,
56
+ separator,
57
+ customGenerateId
58
+ );
53
59
  } catch (error) {
54
60
  this.option.onError(error, this.defaultErrorHandler);
55
61
  }
@@ -1,6 +1,6 @@
1
1
  // 代码灵感来自https://github.com/dadidi9900/auto-plugins-json-translate/blob/main/src/services/translationService.ts
2
2
  const axios = require("axios");
3
- const { generateId } = require("../utils");
3
+ const { generateId: _generateId } = require("../utils");
4
4
  const { Translator } = require("./translator");
5
5
 
6
6
  /**
@@ -25,9 +25,23 @@ class VolcEngineTranslator extends Translator {
25
25
  constructor(option = {}) {
26
26
  super({
27
27
  name: "火山引擎ai翻译",
28
- fetchMethod: async (text, fromKey, toKey, separator) => {
28
+ fetchMethod: async (
29
+ text,
30
+ fromKey,
31
+ toKey,
32
+ separator,
33
+ customGenerateId
34
+ ) => {
29
35
  let salt = new Date().getTime();
30
36
  const textArr = text.split(separator);
37
+
38
+ const generateId = text => {
39
+ if (typeof customGenerateId === "function") {
40
+ return customGenerateId(text, _generateId);
41
+ }
42
+ return _generateId(text);
43
+ };
44
+
31
45
  const sourceMap = Object.fromEntries(
32
46
  textArr.map(text => [generateId(text), text])
33
47
  );
package/lib/visitors.js CHANGED
@@ -3,7 +3,7 @@ const {
3
3
  generateId: _generateId,
4
4
  extractFunctionName,
5
5
  EXCLUDED_CALL,
6
- shouldExtract
6
+ shouldExtract: _shouldExtract
7
7
  } = require("./utils");
8
8
 
9
9
  function isTFunction(node, option) {
@@ -18,10 +18,8 @@ function isTFunction(node, option) {
18
18
  }
19
19
 
20
20
  function isVNodeCall(path, nodeName) {
21
- const node = path.node;
22
21
  if (!path.isCallExpression()) return false;
23
-
24
- const callee = node.callee;
22
+ const callee = path.node.callee;
25
23
  return (
26
24
  (t.isIdentifier(callee) && callee.name === nodeName) ||
27
25
  (t.isMemberExpression(callee) &&
@@ -56,7 +54,23 @@ function getPropKey(propNode) {
56
54
  return null;
57
55
  }
58
56
 
59
- // 将静态属性转换为动态属性,如 <div title="xxx" /> => <div :title="$t('hashed')" />
57
+ /**
58
+ * 向 hoisted dynamicProps 数组中追加 propKey(若不存在)
59
+ * @returns 是否真的发生了修改
60
+ */
61
+ function pushDynamicPropsIfMissing(arr, propKey) {
62
+ const hasKey = arr.elements.some(
63
+ el => t.isStringLiteral(el) && el.value === propKey
64
+ );
65
+
66
+ if (!hasKey) {
67
+ arr.elements.push(t.stringLiteral(propKey));
68
+ }
69
+
70
+ return !hasKey;
71
+ }
72
+
73
+ // 自动补齐动态属性,如 createVNode(c, {message: dynamicValue}) => createVNode(c, {message: dynamicValue}, null, 8, ['message'])
60
74
  function transformDirectiveIfNeeded(path, parentPath) {
61
75
  let hasCreateVNode = false;
62
76
  // 属性值情况,如 title: "xxx"
@@ -67,10 +81,7 @@ function transformDirectiveIfNeeded(path, parentPath) {
67
81
 
68
82
  const vNodeCall = path.findParent(
69
83
  p =>
70
- p.isCallExpression() &&
71
- (t.isIdentifier(p.node.callee, { name: "_createVNode" }) ||
72
- (t.isMemberExpression(p.node.callee) &&
73
- t.isIdentifier(p.node.callee.property, { name: "_createVNode" })))
84
+ isVNodeCall(p, "_createVNode") || isVNodeCall(p, "_createElementVNode")
74
85
  );
75
86
 
76
87
  if (vNodeCall) {
@@ -91,13 +102,23 @@ function transformDirectiveIfNeeded(path, parentPath) {
91
102
  }
92
103
 
93
104
  // 设置 patchFlag = 8
94
- args[patchFlagIndex] = t.numericLiteral(8);
105
+ if (!args[patchFlagIndex]) {
106
+ args[patchFlagIndex] = t.numericLiteral(8);
107
+ }
95
108
 
96
109
  // 设置 dynamicProps = ["propKey"]
97
110
  const existingDynamicProps = args[dynamicPropsIndex];
98
- if (!existingDynamicProps || !t.isArrayExpression(existingDynamicProps)) {
111
+
112
+ if (t.isIdentifier(existingDynamicProps)) {
113
+ const arr = hoistedMap.get(existingDynamicProps.name);
114
+ if (arr) {
115
+ pushDynamicPropsIfMissing(arr, propKey);
116
+ }
117
+ }
118
+
119
+ if (!existingDynamicProps) {
99
120
  args[dynamicPropsIndex] = t.arrayExpression([t.stringLiteral(propKey)]);
100
- } else {
121
+ } else if (t.isArrayExpression(existingDynamicProps)) {
101
122
  const existingKeys = new Set(
102
123
  existingDynamicProps.elements
103
124
  .filter(el => t.isStringLiteral(el))
@@ -134,7 +155,7 @@ function generateText(rawText, hashedText, options) {
134
155
 
135
156
  function generateId(rawText, options) {
136
157
  if (typeof options.generateId === "function") {
137
- return options.generateId(rawText);
158
+ return options.generateId(rawText, _generateId);
138
159
  }
139
160
  return _generateId(rawText);
140
161
  }
@@ -284,10 +305,95 @@ function isAnyVNodeCall(path) {
284
305
  return false;
285
306
  }
286
307
 
308
+ function markComponentUsesI18n(path) {
309
+ const fnPath = path.findParent(
310
+ p =>
311
+ p.isFunctionDeclaration() ||
312
+ p.isFunctionExpression() ||
313
+ p.isArrowFunctionExpression()
314
+ );
315
+
316
+ if (!fnPath) return;
317
+
318
+ fnPath.node.__usesI18n = true;
319
+ }
320
+
321
+ function createUseTranslationDeclaration(options) {
322
+ return t.variableDeclaration("const", [
323
+ t.variableDeclarator(
324
+ t.objectPattern([
325
+ t.objectProperty(
326
+ t.identifier(options.translateKey),
327
+ t.identifier(options.translateKey),
328
+ false,
329
+ true
330
+ )
331
+ ]),
332
+ t.callExpression(t.identifier(options.useTranslationIdentifier), [])
333
+ )
334
+ ]);
335
+ }
336
+
337
+ function injectUseTranslation(fnPath, options) {
338
+ const body = fnPath.get("body");
339
+
340
+ if (!body.isBlockStatement()) {
341
+ // ArrowFunctionExpression 简写:() => <div />
342
+ body.replaceWith(
343
+ t.blockStatement([
344
+ createUseTranslationDeclaration(options),
345
+ t.returnStatement(body.node)
346
+ ])
347
+ );
348
+ return;
349
+ }
350
+
351
+ const statements = body.get("body");
352
+
353
+ // 已经存在 useTranslationss
354
+ if (
355
+ statements.some(
356
+ s =>
357
+ s.isVariableDeclaration() &&
358
+ s.node.declarations.some(
359
+ d =>
360
+ t.isCallExpression(d.init) &&
361
+ t.isIdentifier(d.init.callee, {
362
+ name: options.useTranslationIdentifier
363
+ })
364
+ )
365
+ )
366
+ ) {
367
+ return;
368
+ }
369
+
370
+ body.unshiftContainer("body", createUseTranslationDeclaration(options));
371
+ }
372
+
373
+ function shouldExtract(text, options) {
374
+ return (options.shouldExtract || _shouldExtract)(text, options.fromLang);
375
+ }
376
+
377
+ const hoistedMap = new Map();
378
+
287
379
  function createI18nVisitor(option, i18nMap) {
288
380
  const excludedCall = [...option.excludedCall, ...EXCLUDED_CALL];
289
381
 
290
382
  return {
383
+ Program(path) {
384
+ path.traverse({
385
+ VariableDeclarator(p) {
386
+ const { id, init } = p.node;
387
+ if (
388
+ t.isIdentifier(id) &&
389
+ t.isArrayExpression(init) &&
390
+ id.name.startsWith("_hoisted_")
391
+ ) {
392
+ hoistedMap.set(id.name, init);
393
+ }
394
+ }
395
+ });
396
+ },
291
397
  CallExpression(path) {
292
398
  if (!isTFunction(path.node, option)) {
293
399
  if (isVNodeCall(path, "_createTextVNode")) {
@@ -315,7 +421,7 @@ function createI18nVisitor(option, i18nMap) {
315
421
 
316
422
  if (!keyText) return;
317
423
 
318
- if (!shouldExtract(keyText, option.fromLang)) {
424
+ if (!shouldExtract(keyText, option)) {
319
425
  return;
320
426
  }
321
427
 
@@ -373,7 +479,7 @@ function createI18nVisitor(option, i18nMap) {
373
479
  // return;
374
480
  // }
375
481
 
376
- if (!value || !shouldExtract(value, option.fromLang)) {
482
+ if (!value || !shouldExtract(value, option)) {
377
483
  return;
378
484
  }
379
485
 
@@ -463,7 +569,7 @@ function createI18nVisitor(option, i18nMap) {
463
569
  return;
464
570
  }
465
571
 
466
- if (!shouldExtract(value, option.fromLang)) {
572
+ if (!shouldExtract(value, option)) {
467
573
  return;
468
574
  }
469
575
 
@@ -489,7 +595,7 @@ function createI18nVisitor(option, i18nMap) {
489
595
  const text = path.node.value.trim();
490
596
  if (!text) return; // 空白或换行等,跳过
491
597
 
492
- if (!shouldExtract(text, option.fromLang)) {
598
+ if (!shouldExtract(text, option)) {
493
599
  return;
494
600
  }
495
601
 
@@ -499,6 +605,10 @@ function createI18nVisitor(option, i18nMap) {
499
605
  i18nMap[hashed] = text;
500
606
  }
501
607
 
608
+ if (option.injectUseTranslation) {
609
+ markComponentUsesI18n(path);
610
+ }
611
+
502
612
  if (option.jsx) {
503
613
  const jsxElement = generateJSXElement(
504
614
  option.JSXElement,
@@ -531,7 +641,7 @@ function createI18nVisitor(option, i18nMap) {
531
641
  const value = expr.value.trim();
532
642
 
533
643
  if (!value) return;
534
- if (!shouldExtract(value, option.fromLang)) {
644
+ if (!shouldExtract(value, option)) {
535
645
  return;
536
646
  }
537
647
 
@@ -541,6 +651,10 @@ function createI18nVisitor(option, i18nMap) {
541
651
  i18nMap[hashed] = value;
542
652
  }
543
653
 
654
+ if (option.injectUseTranslation) {
655
+ markComponentUsesI18n(path);
656
+ }
657
+
544
658
  if (
545
659
  option.jsx &&
546
660
  (path.parentPath.isJSXElement() || path.parentPath.isJSXFragment())
@@ -637,6 +751,19 @@ function createI18nVisitor(option, i18nMap) {
637
751
  )
638
752
  );
639
753
  }
754
+ },
755
+ // visitor:Function / ArrowFunction
756
+ Function: {
757
+ exit(path) {
758
+ if (!option.injectUseTranslation) return;
759
+ if (!path.node.__usesI18n) return;
760
+
761
+ // 防止重复注入
762
+ if (path.node.__i18nInjected) return;
763
+ path.node.__i18nInjected = true;
764
+
765
+ injectUseTranslation(path, option);
766
+ }
640
767
  }
641
768
  };
642
769
  }
@@ -11,8 +11,8 @@ function vitePluginImportI18n(option) {
11
11
  name: "vite-plugin-import-i18n",
12
12
  enforce: "pre",
13
13
  async transform(code, path) {
14
- path = path.split('?')[0];
15
-
14
+ path = path.split("?")[0];
15
+
16
16
  if (!option.enabled || !option.autoImportI18n || !filter(path)) return;
17
17
 
18
18
  return i18nImportTransform(
@@ -20,7 +20,9 @@ function vitePluginImportI18n(option) {
20
20
  path,
21
21
  option.jsx
22
22
  ? [option.translateKey, option.JSXElement]
23
- : [option.translateKey],
23
+ : option.injectUseTranslation
24
+ ? [option.translateKey, option.useTranslationIdentifier]
25
+ : [option.translateKey],
24
26
  option.i18nPkgImportPath
25
27
  );
26
28
  }
@@ -11,7 +11,9 @@ module.exports = function (source) {
11
11
  translateKey: importName,
12
12
  JSXElement,
13
13
  jsx,
14
- i18nPkgImportPath: importPath
14
+ i18nPkgImportPath: importPath,
15
+ injectUseTranslation,
16
+ useTranslationIdentifier
15
17
  } = { ...defaultOptions, ...global.getOptions() };
16
18
 
17
19
  // // webpack 4.0
@@ -44,7 +46,11 @@ module.exports = function (source) {
44
46
  return i18nImportTransform(
45
47
  source,
46
48
  global.resourcePath,
47
- jsx ? [importName, JSXElement] : [importName],
49
+ jsx
50
+ ? [importName, JSXElement]
51
+ : injectUseTranslation
52
+ ? [importName, useTranslationIdentifier]
53
+ : [importName],
48
54
  importPath
49
55
  );
50
56
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-i18n-extract-plugin",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "main": "lib/index.js",
5
5
  "types": "types/index.d.ts",
6
6
  "bin": {
@@ -1,32 +1,38 @@
1
- import { Translator } from './translators';
1
+ import { Translator } from "./translators";
2
2
 
3
- export type LangKey = "zh-cn" | 'zh-tw' | 'en' | 'ja' | 'ko' | 'ru' | string;
3
+ export type LangKey = "zh-cn" | "zh-tw" | "en" | "ja" | "ko" | "ru" | string;
4
4
 
5
5
  export interface I18nOptions {
6
- translateKey: string
7
- JSXElement: string
8
- jsx: boolean
9
- rewrite: boolean
10
- extractFromText: boolean
11
- autoImportI18n: boolean
12
- autoTranslate: boolean
13
- cleanTranslate: boolean
14
- keepRaw: boolean
15
- keepDefaultMsg: boolean
16
- enabled: boolean
17
- outputJsonFileInPlugin: boolean
18
- outputJsonFileDebounceTimeInPlugin: number
19
- translateInterval: number
20
- excludedCall: string[]
21
- includePath: string[] | string
22
- excludedPath: string[] | string
23
- allowedExtensions: string[]
24
- fromLang: LangKey
25
- translateLangKeys: LangKey[]
26
- i18nPkgImportPath: string
27
- outputPath: string
28
- generateId: ((text: string) => string) | null | undefined
29
- customGenLangFileName: (langKey: LangKey) => LangKey
30
- customTranslatedText: (text: string, toLang: LangKey) => string,
31
- translator: Translator
32
- };
6
+ translateKey: string;
7
+ useTranslationIdentifier: string;
8
+ JSXElement: string;
9
+ injectUseTranslation: boolean;
10
+ jsx: boolean;
11
+ rewrite: boolean;
12
+ extractFromText: boolean;
13
+ autoImportI18n: boolean;
14
+ autoTranslate: boolean;
15
+ cleanTranslate: boolean;
16
+ keepRaw: boolean;
17
+ keepDefaultMsg: boolean;
18
+ enabled: boolean;
19
+ outputJsonFileInPlugin: boolean;
20
+ outputJsonFileDebounceTimeInPlugin: number;
21
+ translateInterval: number;
22
+ excludedCall: string[];
23
+ includePath: string[] | string;
24
+ excludedPath: string[] | string;
25
+ allowedExtensions: string[];
26
+ fromLang: LangKey;
27
+ translateLangKeys: LangKey[];
28
+ i18nPkgImportPath: string;
29
+ outputPath: string;
30
+ generateId:
31
+ | ((text: string, rawGenerateIdFn?: (text: string) => string) => string)
32
+ | null
33
+ | undefined;
34
+ shouldExtract: (text: string, langKey: LangKey) => boolean;
35
+ customGenLangFileName: (langKey: LangKey) => LangKey;
36
+ customTranslatedText: (text: string, toLang: LangKey) => string;
37
+ translator: Translator;
38
+ }