webpack-gc-i18n-plugin 1.1.4 → 1.1.6

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.
@@ -7,6 +7,8 @@ require('crypto-js');
7
7
  var OpenCC = require('opencc-js');
8
8
  var types = require('@babel/types');
9
9
  require('lodash');
10
+ var path = require('node:path');
11
+ var fs = require('node:fs');
10
12
  var babel = require('@babel/core');
11
13
  var generate = require('@babel/generator');
12
14
 
@@ -382,6 +384,299 @@ const REGEX_MAP = {
382
384
  [OriginLangKeyEnum.RU]: /[йцукенгшщзхъфывапролджэячсмитьбюё .-]{1,}/ // 俄语字母
383
385
  };
384
386
 
387
+ /*
388
+ * @Date: 2025-03-26 20:28:21
389
+ * @LastEditors: xiaoshan
390
+ * @LastEditTime: 2025-03-31 10:29:49
391
+ * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/split.ts
392
+ */
393
+ // 插件核心文件
394
+ // 字符串切割与转换函数
395
+ // import generate from '@babel/generator'
396
+
397
+ // todo 这个切割函数可以优化,性能可能很差
398
+ /**
399
+ * 根据正则表达式分割字符串,并将符合正则的连续字符拼接起来。
400
+ * @param str - 要分割的字符串。
401
+ * @param separatorRegex - 用于分割字符串的正则表达式。
402
+ * @returns 分割并拼接后的字符串数组。
403
+ */
404
+ /**
405
+ * 这个函数的主要功能是根据给定的正则表达式分割字符串,并对分割结果进行特殊处理。
406
+ * 处理过程分为三个主要步骤:
407
+ *
408
+ * 1. 首先根据分隔符正则和标点符号正则进行初步分割
409
+ * 2. 然后将连续的标点符号和符合分隔符正则的部分重新连接
410
+ * 3. 最后将不符合分隔符正则的相邻部分合并
411
+ *
412
+ * @param str - 需要分割的源字符串
413
+ * @param separatorRegex - 用于分割的正则表达式
414
+ * @returns 处理后的字符串数组
415
+ */
416
+ function splitByRegex(str, separatorRegex) {
417
+ // 定义标点符号的正则表达式
418
+ const punctuationRegex = /[,。?!《》,..:!?""'';'"、0-9\n\r\t\v\f]/;
419
+ // 创建一个新的正则表达式,用于分割字符串
420
+ const splitRegex = new RegExp(`(${separatorRegex.source}|${punctuationRegex.source})`, separatorRegex.flags);
421
+
422
+ // 使用正则表达式分割字符串,并过滤掉空字符串
423
+ const splitArr = str.split(splitRegex).filter(Boolean);
424
+ const result = [];
425
+ let currentMatch = '';
426
+
427
+ // 定义连接标点符号的正则表达式
428
+ const connectPunctuationRegex = /[,。?!《》,..:!?;'"、0-9]/;
429
+ // 创建一个新的正则表达式,用于检测是否需要连接
430
+ const connectRegex = new RegExp(`(${separatorRegex.source}|${connectPunctuationRegex.source})`, separatorRegex.flags);
431
+
432
+ // 遍历分割后的数组
433
+ for (const item of splitArr) {
434
+ if (connectRegex.test(item)) {
435
+ // 如果当前项符合连接条件,则将其添加到当前匹配字符串中
436
+ currentMatch += item;
437
+ } else {
438
+ // 如果当前匹配字符串不为空,则将其添加到结果数组中
439
+ if (currentMatch) {
440
+ result.push(currentMatch);
441
+ currentMatch = '';
442
+ }
443
+ // 将当前项添加到结果数组中
444
+ result.push(item);
445
+ }
446
+ }
447
+
448
+ // 如果最后一个匹配字符串不为空,则将其添加到结果数组中
449
+ if (currentMatch) {
450
+ result.push(currentMatch);
451
+ }
452
+
453
+ // 再遍历一次,把不符合separatorRegex 这个正则的拼起来
454
+ const finalResult = [];
455
+ let tempStr = '';
456
+ for (let i = 0; i < result.length; i++) {
457
+ const item = result[i];
458
+ if (separatorRegex.test(item)) {
459
+ if (tempStr) {
460
+ finalResult.push(tempStr);
461
+ tempStr = '';
462
+ }
463
+ finalResult.push(item);
464
+ } else {
465
+ tempStr += item;
466
+ if (i === result.length - 1 || separatorRegex.test(result[i + 1])) {
467
+ finalResult.push(tempStr);
468
+ tempStr = '';
469
+ }
470
+ }
471
+ }
472
+ if (tempStr) {
473
+ finalResult.push(tempStr);
474
+ }
475
+ return finalResult;
476
+ }
477
+
478
+ /**
479
+ * 检查字符串是否需要切割。
480
+ * @param str - 要检查的字符串。
481
+ * @returns 如果字符串需要切割,则返回 true,否则返回 false。
482
+ */
483
+ function checkNeedSplit(str) {
484
+ // 检查字符串中是否包含需要切割的特殊字符
485
+ return str.includes('\n') || str.includes('\\') || str.includes('\r') || str.includes('\t') || str.includes('\v') || str.includes('\f') || str.includes('>') || str.includes('<');
486
+ }
487
+
488
+ /**
489
+ * @description: 将字符串数组转换为babel的模板字符串节点
490
+ * @param {string[]} strArray - 字符串数组
491
+ * @return {types.CallExpression} - babel的深度扫描的表达式
492
+ */
493
+ function convertToTemplateLiteral(strArray, option) {
494
+ const quasis = [];
495
+ const expressions = [];
496
+ strArray.forEach((str, index) => {
497
+ if (index === 0) {
498
+ if (getOriginRegex().test(str)) {
499
+ quasis.push(types__namespace.templateElement({
500
+ raw: '',
501
+ cooked: ''
502
+ }, false));
503
+ expressions.push(createI18nTranslator({
504
+ value: str,
505
+ isExpression: true,
506
+ insertOption: option
507
+ }));
508
+ } else {
509
+ quasis.push(types__namespace.templateElement({
510
+ raw: str,
511
+ cooked: str
512
+ }, false));
513
+ }
514
+ } else {
515
+ if (getOriginRegex().test(str)) {
516
+ expressions.push(createI18nTranslator({
517
+ value: str,
518
+ isExpression: true,
519
+ insertOption: option
520
+ }));
521
+ } else {
522
+ quasis.push(types__namespace.templateElement({
523
+ raw: str,
524
+ cooked: str
525
+ }, false));
526
+ }
527
+ }
528
+ });
529
+ if (quasis.length === expressions.length) {
530
+ quasis.push(types__namespace.templateElement({
531
+ raw: '',
532
+ cooked: ''
533
+ }, true));
534
+ } else if (quasis.length > expressions.length) {
535
+ quasis[quasis.length - 1].tail = true;
536
+ }
537
+ const templateLiteral = types__namespace.templateLiteral(quasis, expressions);
538
+ const deepScanCall = types__namespace.callExpression(types__namespace.identifier('$deepScan'), [templateLiteral]);
539
+ // 打印转换结果
540
+ // console.log('deepScanCall', (generate as any).default(deepScanCall).code)
541
+ return deepScanCall;
542
+ }
543
+
544
+ /**
545
+ * @description: 读取国际化JSON文件
546
+ * @return {*}
547
+ */
548
+ function getLangTranslateJSONFile() {
549
+ const filePath = path.join(option.globalPath, 'index.json');
550
+ try {
551
+ const content = fs.readFileSync(filePath, 'utf-8');
552
+ return content;
553
+ } catch (error) {
554
+ if (error.code === 'ENOENT') {
555
+ console.log('❌读取JSON配置文件异常,文件不存在');
556
+ } else {
557
+ console.log('❌读取JSON配置文件异常,无法读取文件');
558
+ }
559
+ return JSON.stringify({});
560
+ }
561
+ }
562
+
563
+ /**
564
+ * @description: 基于langKey获取JSON配置文件中对应语言对象
565
+ * @param {string} key
566
+ * @return {*}
567
+ */
568
+ function getLangObjByJSONFileWithLangKey(key) {
569
+ let insertJSONObj = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
570
+ // 获取翻译配置对象,优先使用传入的配置,否则从本地文件读取
571
+ const JSONObj = insertJSONObj || JSON.parse(getLangTranslateJSONFile());
572
+
573
+ // 初始化语言映射对象,用于存储不同语言的hash: value映射
574
+ const langObj = {};
575
+
576
+ // 遍历hash,提取hash对应语言key的值,并写入到langObj
577
+ Object.keys(JSONObj).forEach(langCode => {
578
+ langObj[langCode] = JSONObj[langCode][key];
579
+ });
580
+
581
+ // 返回当前语言的hash: value映射对象
582
+ // 例如: 'zh-cn' > {'hash1': '你好', 'hash2': '世界'}
583
+ // 'en' > {'hash1': 'hello', 'hash2': 'world'}
584
+ return langObj;
585
+ }
586
+
587
+ /*
588
+ * @Author: xiaoshanwen
589
+ * @Date: 2023-10-30 18:23:03
590
+ * @LastEditTime: 2025-03-16 19:12:54
591
+ * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/translate.ts
592
+ */
593
+
594
+ let langObj = {};
595
+ function normalizeValueForDedupe(value) {
596
+ return String(value || '').trim();
597
+ }
598
+ function findKeyByValue(valueMap, value) {
599
+ const exactKey = Object.entries(valueMap).find(_ref => {
600
+ let [, oldValue] = _ref;
601
+ return oldValue === value;
602
+ })?.[0];
603
+ if (exactKey) return exactKey;
604
+ const normalizedValue = normalizeValueForDedupe(value);
605
+ return Object.entries(valueMap).find(_ref2 => {
606
+ let [, oldValue] = _ref2;
607
+ return normalizeValueForDedupe(oldValue) === normalizedValue;
608
+ })?.[0];
609
+ }
610
+ function getExistingOriginLangObj() {
611
+ try {
612
+ const filePath = path.join(option.globalPath, 'index.json');
613
+ if (!fs.existsSync(filePath)) return {};
614
+ return getLangObjByJSONFileWithLangKey(option.originLang);
615
+ } catch (error) {
616
+ return {};
617
+ }
618
+ }
619
+ function getLangKeyByValue(value) {
620
+ return findKeyByValue(langObj, value) || findKeyByValue(getExistingOriginLangObj(), value);
621
+ }
622
+ function resolveLangKey(key, value) {
623
+ return getLangKeyByValue(value) || key;
624
+ }
625
+ function isTemplateValue(value) {
626
+ return /\$\{[^}]+}/.test(value);
627
+ }
628
+ function hasOriginSymbols$1(value) {
629
+ const originRegex = REGEX_MAP[option.originLang];
630
+ return originRegex ? originRegex.test(value) : false;
631
+ }
632
+ function getTemplateSemanticSplitSegments(value) {
633
+ const segments = value.split(/\$\{[^}]+}/g);
634
+ const result = new Set();
635
+ for (let index = 0; index < segments.length - 1; index++) {
636
+ const beforeValue = segments[index];
637
+ const afterValue = segments[index + 1];
638
+ if (!hasOriginSymbols$1(beforeValue) || !hasOriginSymbols$1(afterValue)) continue;
639
+ [beforeValue, afterValue].forEach(segment => {
640
+ if (segment.trim().length > 1) {
641
+ result.add(segment);
642
+ }
643
+ });
644
+ }
645
+ return [...result];
646
+ }
647
+ function isTemplateStaticSegment(value) {
648
+ return Object.values(langObj).some(oldValue => isTemplateValue(oldValue) && getTemplateSemanticSplitSegments(oldValue).includes(value));
649
+ }
650
+ function removeTemplateStaticSegments(value) {
651
+ const segments = new Set(getTemplateSemanticSplitSegments(value));
652
+ Object.keys(langObj).forEach(key => {
653
+ if (segments.has(langObj[key])) {
654
+ delete langObj[key];
655
+ }
656
+ });
657
+ }
658
+
659
+ /**
660
+ * @description: 设置翻译对象属性
661
+ * @param {string} key
662
+ * @param {string} value
663
+ * @return {*}
664
+ */
665
+ function setLangObj(key, value) {
666
+ if (isTemplateValue(value)) {
667
+ removeTemplateStaticSegments(value);
668
+ } else if (isTemplateStaticSegment(value)) {
669
+ return;
670
+ }
671
+ const existingKey = getLangKeyByValue(value);
672
+ if (existingKey && existingKey !== key) {
673
+ return;
674
+ }
675
+ if (!langObj[key]) {
676
+ langObj[key] = value;
677
+ }
678
+ }
679
+
385
680
  /*
386
681
  * @Author: xiaoshanwen
387
682
  * @Date: 2023-10-11 10:01:43
@@ -503,7 +798,7 @@ function createI18nTranslator(createOption) {
503
798
  valStr
504
799
  } = normalizeTranslateValue(value);
505
800
  // 若 key 存在则使用 key,否则调用 generateId 函数根据 valStr 生成唯一的键
506
- const generatedKey = key || generateId(valStr);
801
+ const generatedKey = key || resolveLangKey(generateId(valStr), trimmedValue);
507
802
  // 提取公共配置对象,避免重复代码
508
803
  const config = {
509
804
  option: option,
@@ -760,184 +1055,6 @@ let option = {
760
1055
  ...DEFAULT_OPTION
761
1056
  };
762
1057
 
763
- /*
764
- * @Author: xiaoshanwen
765
- * @Date: 2023-10-30 18:23:03
766
- * @LastEditTime: 2025-03-16 19:12:54
767
- * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/translate.ts
768
- */
769
-
770
- let langObj = {};
771
-
772
- /**
773
- * @description: 设置翻译对象属性
774
- * @param {string} key
775
- * @param {string} value
776
- * @return {*}
777
- */
778
- function setLangObj(key, value) {
779
- if (!langObj[key]) {
780
- langObj[key] = value;
781
- }
782
- }
783
-
784
- /*
785
- * @Date: 2025-03-26 20:28:21
786
- * @LastEditors: xiaoshan
787
- * @LastEditTime: 2025-03-31 10:29:49
788
- * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/split.ts
789
- */
790
- // 插件核心文件
791
- // 字符串切割与转换函数
792
- // import generate from '@babel/generator'
793
-
794
- // todo 这个切割函数可以优化,性能可能很差
795
- /**
796
- * 根据正则表达式分割字符串,并将符合正则的连续字符拼接起来。
797
- * @param str - 要分割的字符串。
798
- * @param separatorRegex - 用于分割字符串的正则表达式。
799
- * @returns 分割并拼接后的字符串数组。
800
- */
801
- /**
802
- * 这个函数的主要功能是根据给定的正则表达式分割字符串,并对分割结果进行特殊处理。
803
- * 处理过程分为三个主要步骤:
804
- *
805
- * 1. 首先根据分隔符正则和标点符号正则进行初步分割
806
- * 2. 然后将连续的标点符号和符合分隔符正则的部分重新连接
807
- * 3. 最后将不符合分隔符正则的相邻部分合并
808
- *
809
- * @param str - 需要分割的源字符串
810
- * @param separatorRegex - 用于分割的正则表达式
811
- * @returns 处理后的字符串数组
812
- */
813
- function splitByRegex(str, separatorRegex) {
814
- // 定义标点符号的正则表达式
815
- const punctuationRegex = /[,。?!《》,..:!?""'';'"、0-9\n\r\t\v\f]/;
816
- // 创建一个新的正则表达式,用于分割字符串
817
- const splitRegex = new RegExp(`(${separatorRegex.source}|${punctuationRegex.source})`, separatorRegex.flags);
818
-
819
- // 使用正则表达式分割字符串,并过滤掉空字符串
820
- const splitArr = str.split(splitRegex).filter(Boolean);
821
- const result = [];
822
- let currentMatch = '';
823
-
824
- // 定义连接标点符号的正则表达式
825
- const connectPunctuationRegex = /[,。?!《》,..:!?;'"、0-9]/;
826
- // 创建一个新的正则表达式,用于检测是否需要连接
827
- const connectRegex = new RegExp(`(${separatorRegex.source}|${connectPunctuationRegex.source})`, separatorRegex.flags);
828
-
829
- // 遍历分割后的数组
830
- for (const item of splitArr) {
831
- if (connectRegex.test(item)) {
832
- // 如果当前项符合连接条件,则将其添加到当前匹配字符串中
833
- currentMatch += item;
834
- } else {
835
- // 如果当前匹配字符串不为空,则将其添加到结果数组中
836
- if (currentMatch) {
837
- result.push(currentMatch);
838
- currentMatch = '';
839
- }
840
- // 将当前项添加到结果数组中
841
- result.push(item);
842
- }
843
- }
844
-
845
- // 如果最后一个匹配字符串不为空,则将其添加到结果数组中
846
- if (currentMatch) {
847
- result.push(currentMatch);
848
- }
849
-
850
- // 再遍历一次,把不符合separatorRegex 这个正则的拼起来
851
- const finalResult = [];
852
- let tempStr = '';
853
- for (let i = 0; i < result.length; i++) {
854
- const item = result[i];
855
- if (separatorRegex.test(item)) {
856
- if (tempStr) {
857
- finalResult.push(tempStr);
858
- tempStr = '';
859
- }
860
- finalResult.push(item);
861
- } else {
862
- tempStr += item;
863
- if (i === result.length - 1 || separatorRegex.test(result[i + 1])) {
864
- finalResult.push(tempStr);
865
- tempStr = '';
866
- }
867
- }
868
- }
869
- if (tempStr) {
870
- finalResult.push(tempStr);
871
- }
872
- return finalResult;
873
- }
874
-
875
- /**
876
- * 检查字符串是否需要切割。
877
- * @param str - 要检查的字符串。
878
- * @returns 如果字符串需要切割,则返回 true,否则返回 false。
879
- */
880
- function checkNeedSplit(str) {
881
- // 检查字符串中是否包含需要切割的特殊字符
882
- return str.includes('\n') || str.includes('\\') || str.includes('\r') || str.includes('\t') || str.includes('\v') || str.includes('\f') || str.includes('>') || str.includes('<');
883
- }
884
-
885
- /**
886
- * @description: 将字符串数组转换为babel的模板字符串节点
887
- * @param {string[]} strArray - 字符串数组
888
- * @return {types.CallExpression} - babel的深度扫描的表达式
889
- */
890
- function convertToTemplateLiteral(strArray, option) {
891
- const quasis = [];
892
- const expressions = [];
893
- strArray.forEach((str, index) => {
894
- if (index === 0) {
895
- if (getOriginRegex().test(str)) {
896
- quasis.push(types__namespace.templateElement({
897
- raw: '',
898
- cooked: ''
899
- }, false));
900
- expressions.push(createI18nTranslator({
901
- value: str,
902
- isExpression: true,
903
- insertOption: option
904
- }));
905
- } else {
906
- quasis.push(types__namespace.templateElement({
907
- raw: str,
908
- cooked: str
909
- }, false));
910
- }
911
- } else {
912
- if (getOriginRegex().test(str)) {
913
- expressions.push(createI18nTranslator({
914
- value: str,
915
- isExpression: true,
916
- insertOption: option
917
- }));
918
- } else {
919
- quasis.push(types__namespace.templateElement({
920
- raw: str,
921
- cooked: str
922
- }, false));
923
- }
924
- }
925
- });
926
- if (quasis.length === expressions.length) {
927
- quasis.push(types__namespace.templateElement({
928
- raw: '',
929
- cooked: ''
930
- }, true));
931
- } else if (quasis.length > expressions.length) {
932
- quasis[quasis.length - 1].tail = true;
933
- }
934
- const templateLiteral = types__namespace.templateLiteral(quasis, expressions);
935
- const deepScanCall = types__namespace.callExpression(types__namespace.identifier('$deepScan'), [templateLiteral]);
936
- // 打印转换结果
937
- // console.log('deepScanCall', (generate as any).default(deepScanCall).code)
938
- return deepScanCall;
939
- }
940
-
941
1058
  /*
942
1059
  * @Author: xiaoshanwen
943
1060
  * @Date: 2023-11-01 16:35:38
@@ -1005,16 +1122,37 @@ function handleTemplateLiteralWithExpressions(node, path) {
1005
1122
  }
1006
1123
  return false;
1007
1124
  }
1125
+ if (!hasExpressionInsideOriginContent(node)) {
1126
+ return false;
1127
+ }
1008
1128
  const {
1009
1129
  trimmedValue,
1010
1130
  valStr
1011
1131
  } = normalizeTranslateValue(fullValue);
1012
- const id = generateId(valStr);
1132
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1013
1133
  const translateNode = createTranslateNode(path, id, valStr, expressionLabels, node.expressions);
1014
1134
  path.replaceWith(translateNode);
1015
1135
  setLangObj(id, trimmedValue);
1016
1136
  return true;
1017
1137
  }
1138
+ function hasExpressionInsideOriginContent(node) {
1139
+ return node.expressions.some((_, index) => {
1140
+ const beforeValue = node.quasis.slice(0, index + 1).map(getTemplateElementValue).join('');
1141
+ const afterValue = node.quasis.slice(index + 1).map(getTemplateElementValue).join('');
1142
+ return hasOriginSymbols(beforeValue) && hasOriginSymbols(afterValue);
1143
+ });
1144
+ }
1145
+ function getTemplateElementValue(quasi) {
1146
+ let value = quasi.value.raw || quasi.value.cooked || '';
1147
+ if (asianLangs.some(lang => option.originLang.includes(lang) || option.originLang === lang)) {
1148
+ try {
1149
+ value = unicodeToString(value);
1150
+ } catch (error) {
1151
+ console.log('转换异常');
1152
+ }
1153
+ }
1154
+ return value;
1155
+ }
1018
1156
  function getExpressionPlaceholder(expression) {
1019
1157
  if (types.isStringLiteral(expression) || types.isNumericLiteral(expression)) {
1020
1158
  return String(expression.value);
@@ -1079,7 +1217,7 @@ function createTrackedTranslateCall(value) {
1079
1217
  trimmedValue,
1080
1218
  valStr
1081
1219
  } = normalizeTranslateValue(value);
1082
- const id = generateId(valStr);
1220
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1083
1221
  if (id && trimmedValue) {
1084
1222
  setLangObj(id, trimmedValue);
1085
1223
  }
@@ -1108,16 +1246,53 @@ function createTemplateLiteralTranslateCall(node) {
1108
1246
  if (!fullValue || !hasOriginSymbols(fullValue) || !option.excludedPattern.length || checkAgainstRegexArray(fullValue, [...option.excludedPattern])) {
1109
1247
  return null;
1110
1248
  }
1249
+ if (!hasExpressionInsideOriginContent(node)) {
1250
+ return createTemplateLiteralWithTranslatedQuasis(node, expressions);
1251
+ }
1111
1252
  const {
1112
1253
  trimmedValue,
1113
1254
  valStr
1114
1255
  } = normalizeTranslateValue(fullValue);
1115
- const id = generateId(valStr);
1256
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1116
1257
  if (id && trimmedValue) {
1117
1258
  setLangObj(id, trimmedValue);
1118
1259
  }
1119
1260
  return createTranslateCall(id, valStr, expressionLabels, expressions);
1120
1261
  }
1262
+ function createTemplateLiteralWithTranslatedQuasis(node, expressions) {
1263
+ const quasis = [];
1264
+ const nextExpressions = [];
1265
+ let pendingText = '';
1266
+ let hasTranslatedQuasi = false;
1267
+ node.quasis.forEach((quasi, index) => {
1268
+ const value = getTemplateElementValue(quasi);
1269
+ if (value && hasOriginSymbols(value) && option.excludedPattern.length && !checkAgainstRegexArray(value, [...option.excludedPattern])) {
1270
+ quasis.push(types.templateElement({
1271
+ raw: pendingText,
1272
+ cooked: pendingText
1273
+ }, false));
1274
+ nextExpressions.push(createTrackedTranslateCall(value));
1275
+ pendingText = '';
1276
+ hasTranslatedQuasi = true;
1277
+ } else {
1278
+ pendingText += value;
1279
+ }
1280
+ if (index < expressions.length) {
1281
+ quasis.push(types.templateElement({
1282
+ raw: pendingText,
1283
+ cooked: pendingText
1284
+ }, false));
1285
+ nextExpressions.push(types.cloneNode(expressions[index]));
1286
+ pendingText = '';
1287
+ }
1288
+ });
1289
+ if (!hasTranslatedQuasi) return null;
1290
+ quasis.push(types.templateElement({
1291
+ raw: pendingText,
1292
+ cooked: pendingText
1293
+ }, true));
1294
+ return types.templateLiteral(quasis, nextExpressions);
1295
+ }
1121
1296
  function getConditionalExpressionLabel(expression) {
1122
1297
  const test = expression.test;
1123
1298
  if (types.isBinaryExpression(test) || types.isLogicalExpression(test)) {
@@ -1197,9 +1372,10 @@ function handleTemplateElement(node, insertOption) {
1197
1372
  // 替换为字符类型翻译节点
1198
1373
  node.value.raw = node.value.cooked = `\${${newNode}}`;
1199
1374
  const {
1375
+ trimmedValue,
1200
1376
  valStr
1201
1377
  } = normalizeTranslateValue(value);
1202
- let id = generateId(valStr);
1378
+ let id = resolveLangKey(generateId(valStr), trimmedValue);
1203
1379
  if (id && value) {
1204
1380
  setLangObj(id, value);
1205
1381
  }