webpack-gc-i18n-plugin 1.1.4 → 1.1.5

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.
@@ -382,6 +382,219 @@ const REGEX_MAP = {
382
382
  [OriginLangKeyEnum.RU]: /[йцукенгшщзхъфывапролджэячсмитьбюё .-]{1,}/ // 俄语字母
383
383
  };
384
384
 
385
+ /*
386
+ * @Date: 2025-03-26 20:28:21
387
+ * @LastEditors: xiaoshan
388
+ * @LastEditTime: 2025-03-31 10:29:49
389
+ * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/split.ts
390
+ */
391
+ // 插件核心文件
392
+ // 字符串切割与转换函数
393
+ // import generate from '@babel/generator'
394
+
395
+ // todo 这个切割函数可以优化,性能可能很差
396
+ /**
397
+ * 根据正则表达式分割字符串,并将符合正则的连续字符拼接起来。
398
+ * @param str - 要分割的字符串。
399
+ * @param separatorRegex - 用于分割字符串的正则表达式。
400
+ * @returns 分割并拼接后的字符串数组。
401
+ */
402
+ /**
403
+ * 这个函数的主要功能是根据给定的正则表达式分割字符串,并对分割结果进行特殊处理。
404
+ * 处理过程分为三个主要步骤:
405
+ *
406
+ * 1. 首先根据分隔符正则和标点符号正则进行初步分割
407
+ * 2. 然后将连续的标点符号和符合分隔符正则的部分重新连接
408
+ * 3. 最后将不符合分隔符正则的相邻部分合并
409
+ *
410
+ * @param str - 需要分割的源字符串
411
+ * @param separatorRegex - 用于分割的正则表达式
412
+ * @returns 处理后的字符串数组
413
+ */
414
+ function splitByRegex(str, separatorRegex) {
415
+ // 定义标点符号的正则表达式
416
+ const punctuationRegex = /[,。?!《》,..:!?""'';'"、0-9\n\r\t\v\f]/;
417
+ // 创建一个新的正则表达式,用于分割字符串
418
+ const splitRegex = new RegExp(`(${separatorRegex.source}|${punctuationRegex.source})`, separatorRegex.flags);
419
+
420
+ // 使用正则表达式分割字符串,并过滤掉空字符串
421
+ const splitArr = str.split(splitRegex).filter(Boolean);
422
+ const result = [];
423
+ let currentMatch = '';
424
+
425
+ // 定义连接标点符号的正则表达式
426
+ const connectPunctuationRegex = /[,。?!《》,..:!?;'"、0-9]/;
427
+ // 创建一个新的正则表达式,用于检测是否需要连接
428
+ const connectRegex = new RegExp(`(${separatorRegex.source}|${connectPunctuationRegex.source})`, separatorRegex.flags);
429
+
430
+ // 遍历分割后的数组
431
+ for (const item of splitArr) {
432
+ if (connectRegex.test(item)) {
433
+ // 如果当前项符合连接条件,则将其添加到当前匹配字符串中
434
+ currentMatch += item;
435
+ } else {
436
+ // 如果当前匹配字符串不为空,则将其添加到结果数组中
437
+ if (currentMatch) {
438
+ result.push(currentMatch);
439
+ currentMatch = '';
440
+ }
441
+ // 将当前项添加到结果数组中
442
+ result.push(item);
443
+ }
444
+ }
445
+
446
+ // 如果最后一个匹配字符串不为空,则将其添加到结果数组中
447
+ if (currentMatch) {
448
+ result.push(currentMatch);
449
+ }
450
+
451
+ // 再遍历一次,把不符合separatorRegex 这个正则的拼起来
452
+ const finalResult = [];
453
+ let tempStr = '';
454
+ for (let i = 0; i < result.length; i++) {
455
+ const item = result[i];
456
+ if (separatorRegex.test(item)) {
457
+ if (tempStr) {
458
+ finalResult.push(tempStr);
459
+ tempStr = '';
460
+ }
461
+ finalResult.push(item);
462
+ } else {
463
+ tempStr += item;
464
+ if (i === result.length - 1 || separatorRegex.test(result[i + 1])) {
465
+ finalResult.push(tempStr);
466
+ tempStr = '';
467
+ }
468
+ }
469
+ }
470
+ if (tempStr) {
471
+ finalResult.push(tempStr);
472
+ }
473
+ return finalResult;
474
+ }
475
+
476
+ /**
477
+ * 检查字符串是否需要切割。
478
+ * @param str - 要检查的字符串。
479
+ * @returns 如果字符串需要切割,则返回 true,否则返回 false。
480
+ */
481
+ function checkNeedSplit(str) {
482
+ // 检查字符串中是否包含需要切割的特殊字符
483
+ return str.includes('\n') || str.includes('\\') || str.includes('\r') || str.includes('\t') || str.includes('\v') || str.includes('\f') || str.includes('>') || str.includes('<');
484
+ }
485
+
486
+ /**
487
+ * @description: 将字符串数组转换为babel的模板字符串节点
488
+ * @param {string[]} strArray - 字符串数组
489
+ * @return {types.CallExpression} - babel的深度扫描的表达式
490
+ */
491
+ function convertToTemplateLiteral(strArray, option) {
492
+ const quasis = [];
493
+ const expressions = [];
494
+ strArray.forEach((str, index) => {
495
+ if (index === 0) {
496
+ if (getOriginRegex().test(str)) {
497
+ quasis.push(types__namespace.templateElement({
498
+ raw: '',
499
+ cooked: ''
500
+ }, false));
501
+ expressions.push(createI18nTranslator({
502
+ value: str,
503
+ isExpression: true,
504
+ insertOption: option
505
+ }));
506
+ } else {
507
+ quasis.push(types__namespace.templateElement({
508
+ raw: str,
509
+ cooked: str
510
+ }, false));
511
+ }
512
+ } else {
513
+ if (getOriginRegex().test(str)) {
514
+ expressions.push(createI18nTranslator({
515
+ value: str,
516
+ isExpression: true,
517
+ insertOption: option
518
+ }));
519
+ } else {
520
+ quasis.push(types__namespace.templateElement({
521
+ raw: str,
522
+ cooked: str
523
+ }, false));
524
+ }
525
+ }
526
+ });
527
+ if (quasis.length === expressions.length) {
528
+ quasis.push(types__namespace.templateElement({
529
+ raw: '',
530
+ cooked: ''
531
+ }, true));
532
+ } else if (quasis.length > expressions.length) {
533
+ quasis[quasis.length - 1].tail = true;
534
+ }
535
+ const templateLiteral = types__namespace.templateLiteral(quasis, expressions);
536
+ const deepScanCall = types__namespace.callExpression(types__namespace.identifier('$deepScan'), [templateLiteral]);
537
+ // 打印转换结果
538
+ // console.log('deepScanCall', (generate as any).default(deepScanCall).code)
539
+ return deepScanCall;
540
+ }
541
+
542
+ /*
543
+ * @Author: xiaoshanwen
544
+ * @Date: 2023-10-30 18:23:03
545
+ * @LastEditTime: 2025-03-16 19:12:54
546
+ * @FilePath: /i18n_translation_vite/packages/autoI18nPluginCore/src/utils/translate.ts
547
+ */
548
+
549
+ let langObj = {};
550
+ function getLangKeyByValue(value) {
551
+ return Object.entries(langObj).find(_ref => {
552
+ let [, oldValue] = _ref;
553
+ return oldValue === value;
554
+ })?.[0];
555
+ }
556
+ function resolveLangKey(key, value) {
557
+ return getLangKeyByValue(value) || key;
558
+ }
559
+ function isTemplateValue(value) {
560
+ return /\$\{[^}]+}/.test(value);
561
+ }
562
+ function getTemplateStaticSegments(value) {
563
+ return value.split(/\$\{[^}]+}/g).filter(item => item);
564
+ }
565
+ function isTemplateStaticSegment(value) {
566
+ return Object.values(langObj).some(oldValue => isTemplateValue(oldValue) && getTemplateStaticSegments(oldValue).includes(value));
567
+ }
568
+ function removeTemplateStaticSegments(value) {
569
+ const segments = new Set(getTemplateStaticSegments(value));
570
+ Object.keys(langObj).forEach(key => {
571
+ if (segments.has(langObj[key])) {
572
+ delete langObj[key];
573
+ }
574
+ });
575
+ }
576
+
577
+ /**
578
+ * @description: 设置翻译对象属性
579
+ * @param {string} key
580
+ * @param {string} value
581
+ * @return {*}
582
+ */
583
+ function setLangObj(key, value) {
584
+ if (isTemplateValue(value)) {
585
+ removeTemplateStaticSegments(value);
586
+ } else if (isTemplateStaticSegment(value)) {
587
+ return;
588
+ }
589
+ const existingKey = getLangKeyByValue(value);
590
+ if (existingKey && existingKey !== key) {
591
+ return;
592
+ }
593
+ if (!langObj[key]) {
594
+ langObj[key] = value;
595
+ }
596
+ }
597
+
385
598
  /*
386
599
  * @Author: xiaoshanwen
387
600
  * @Date: 2023-10-11 10:01:43
@@ -503,7 +716,7 @@ function createI18nTranslator(createOption) {
503
716
  valStr
504
717
  } = normalizeTranslateValue(value);
505
718
  // 若 key 存在则使用 key,否则调用 generateId 函数根据 valStr 生成唯一的键
506
- const generatedKey = key || generateId(valStr);
719
+ const generatedKey = key || resolveLangKey(generateId(valStr), trimmedValue);
507
720
  // 提取公共配置对象,避免重复代码
508
721
  const config = {
509
722
  option: option,
@@ -760,184 +973,6 @@ let option = {
760
973
  ...DEFAULT_OPTION
761
974
  };
762
975
 
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
976
  /*
942
977
  * @Author: xiaoshanwen
943
978
  * @Date: 2023-11-01 16:35:38
@@ -1009,7 +1044,7 @@ function handleTemplateLiteralWithExpressions(node, path) {
1009
1044
  trimmedValue,
1010
1045
  valStr
1011
1046
  } = normalizeTranslateValue(fullValue);
1012
- const id = generateId(valStr);
1047
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1013
1048
  const translateNode = createTranslateNode(path, id, valStr, expressionLabels, node.expressions);
1014
1049
  path.replaceWith(translateNode);
1015
1050
  setLangObj(id, trimmedValue);
@@ -1079,7 +1114,7 @@ function createTrackedTranslateCall(value) {
1079
1114
  trimmedValue,
1080
1115
  valStr
1081
1116
  } = normalizeTranslateValue(value);
1082
- const id = generateId(valStr);
1117
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1083
1118
  if (id && trimmedValue) {
1084
1119
  setLangObj(id, trimmedValue);
1085
1120
  }
@@ -1112,7 +1147,7 @@ function createTemplateLiteralTranslateCall(node) {
1112
1147
  trimmedValue,
1113
1148
  valStr
1114
1149
  } = normalizeTranslateValue(fullValue);
1115
- const id = generateId(valStr);
1150
+ const id = resolveLangKey(generateId(valStr), trimmedValue);
1116
1151
  if (id && trimmedValue) {
1117
1152
  setLangObj(id, trimmedValue);
1118
1153
  }
@@ -1197,9 +1232,10 @@ function handleTemplateElement(node, insertOption) {
1197
1232
  // 替换为字符类型翻译节点
1198
1233
  node.value.raw = node.value.cooked = `\${${newNode}}`;
1199
1234
  const {
1235
+ trimmedValue,
1200
1236
  valStr
1201
1237
  } = normalizeTranslateValue(value);
1202
- let id = generateId(valStr);
1238
+ let id = resolveLangKey(generateId(valStr), trimmedValue);
1203
1239
  if (id && value) {
1204
1240
  setLangObj(id, value);
1205
1241
  }