@agentscope-ai/i18n 0.1.9-rc.1 → 0.1.9-rc.2
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/lib/core/translator.js +183 -11
- package/lib/parse-jsx.js +7 -81
- package/lib/utils/ast-utils.js +66 -0
- package/package.json +1 -1
package/lib/core/translator.js
CHANGED
|
@@ -10,8 +10,21 @@ const {
|
|
|
10
10
|
translateAndUpdateFiles,
|
|
11
11
|
singleTranslateAndUpdateFiles,
|
|
12
12
|
} = require('./file-processor');
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
extractI18nKey,
|
|
15
|
+
chnRegExp,
|
|
16
|
+
onlyContainsChinesePunctuation,
|
|
17
|
+
onlyContainsEmoji,
|
|
18
|
+
isI18nCall,
|
|
19
|
+
isInConsoleCall,
|
|
20
|
+
} = require('../utils/ast-utils');
|
|
14
21
|
const { askQuestion } = require('../utils/cli-utils');
|
|
22
|
+
const {
|
|
23
|
+
shouldIgnoreLineByComment,
|
|
24
|
+
shouldIgnoreBlockByComment,
|
|
25
|
+
shouldIgnoreByComment,
|
|
26
|
+
shouldIgnoreFile,
|
|
27
|
+
} = require('../utils/file-utils');
|
|
15
28
|
const { generateMedusaExcel } = require('../utils/excel-utils');
|
|
16
29
|
// inquirer需要动态导入,因为它是ES模块
|
|
17
30
|
const t = require('@babel/types');
|
|
@@ -310,6 +323,113 @@ const scanFilesAndCollectKeys = (files) => {
|
|
|
310
323
|
return { usedKeys, usedKeysData };
|
|
311
324
|
};
|
|
312
325
|
|
|
326
|
+
// 扫描未翻译的中文文案(未被 $i18n.get 包裹的中文字符串)
|
|
327
|
+
const scanUntranslatedTexts = (files, doNotTranslateFiles) => {
|
|
328
|
+
const untranslatedTexts = [];
|
|
329
|
+
|
|
330
|
+
const filteredFiles = files.filter((file) => {
|
|
331
|
+
if (shouldIgnoreFile(file, doNotTranslateFiles || [])) return false;
|
|
332
|
+
if (shouldIgnoreByComment(file)) return false;
|
|
333
|
+
return true;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
filteredFiles.forEach((file) => {
|
|
337
|
+
try {
|
|
338
|
+
const code = fs.readFileSync(file, 'utf8');
|
|
339
|
+
const ast = parseAST(code);
|
|
340
|
+
|
|
341
|
+
const shouldIgnoreNode = (nodePath) => {
|
|
342
|
+
if (!nodePath.node.loc) return false;
|
|
343
|
+
const lineNumber = nodePath.node.loc.start.line;
|
|
344
|
+
return (
|
|
345
|
+
shouldIgnoreLineByComment(file, lineNumber) ||
|
|
346
|
+
shouldIgnoreBlockByComment(file, lineNumber)
|
|
347
|
+
);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
traverse(ast, {
|
|
351
|
+
StringLiteral(astPath) {
|
|
352
|
+
const { value } = astPath.node;
|
|
353
|
+
if (
|
|
354
|
+
chnRegExp.test(value) &&
|
|
355
|
+
!isI18nCall(astPath.parent) &&
|
|
356
|
+
!astPath.findParent((p) => isI18nCall(p.node)) &&
|
|
357
|
+
!isInConsoleCall(astPath) &&
|
|
358
|
+
!shouldIgnoreNode(astPath) &&
|
|
359
|
+
!onlyContainsChinesePunctuation(value) &&
|
|
360
|
+
!onlyContainsEmoji(value)
|
|
361
|
+
) {
|
|
362
|
+
untranslatedTexts.push({
|
|
363
|
+
file,
|
|
364
|
+
line: astPath.node.loc?.start.line,
|
|
365
|
+
column: astPath.node.loc?.start.column,
|
|
366
|
+
type: 'StringLiteral',
|
|
367
|
+
text: value,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
TemplateLiteral(astPath) {
|
|
372
|
+
if (
|
|
373
|
+
!isI18nCall(astPath.parent) &&
|
|
374
|
+
!astPath.findParent((p) => isI18nCall(p.node)) &&
|
|
375
|
+
!isInConsoleCall(astPath) &&
|
|
376
|
+
!shouldIgnoreNode(astPath)
|
|
377
|
+
) {
|
|
378
|
+
const staticParts = astPath.node.quasis.map((q) => q.value.raw).join('');
|
|
379
|
+
if (
|
|
380
|
+
chnRegExp.test(staticParts) &&
|
|
381
|
+
!onlyContainsChinesePunctuation(staticParts) &&
|
|
382
|
+
!onlyContainsEmoji(staticParts)
|
|
383
|
+
) {
|
|
384
|
+
const fullText = astPath.node.quasis
|
|
385
|
+
.map((q, i) => {
|
|
386
|
+
const expr = astPath.node.expressions[i];
|
|
387
|
+
const exprStr = expr ? `\${${generate(expr, { compact: true }).code}}` : '';
|
|
388
|
+
return q.value.raw + exprStr;
|
|
389
|
+
})
|
|
390
|
+
.join('');
|
|
391
|
+
untranslatedTexts.push({
|
|
392
|
+
file,
|
|
393
|
+
line: astPath.node.loc?.start.line,
|
|
394
|
+
column: astPath.node.loc?.start.column,
|
|
395
|
+
type: 'TemplateLiteral',
|
|
396
|
+
text: fullText,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
JSXText(astPath) {
|
|
402
|
+
const { value } = astPath.node;
|
|
403
|
+
if (
|
|
404
|
+
chnRegExp.test(value) &&
|
|
405
|
+
!isI18nCall(astPath.parent) &&
|
|
406
|
+
!astPath.findParent((p) => isI18nCall(p.node)) &&
|
|
407
|
+
!isInConsoleCall(astPath) &&
|
|
408
|
+
!shouldIgnoreNode(astPath) &&
|
|
409
|
+
!onlyContainsChinesePunctuation(value) &&
|
|
410
|
+
!onlyContainsEmoji(value)
|
|
411
|
+
) {
|
|
412
|
+
const trimmedValue = value.replace(/^\s+|\s+$/g, '');
|
|
413
|
+
if (trimmedValue) {
|
|
414
|
+
untranslatedTexts.push({
|
|
415
|
+
file,
|
|
416
|
+
line: astPath.node.loc?.start.line,
|
|
417
|
+
column: astPath.node.loc?.start.column,
|
|
418
|
+
type: 'JSXText',
|
|
419
|
+
text: trimmedValue,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error(` # 扫描未翻译文案失败: ${file}`, error.message);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
return untranslatedTexts;
|
|
431
|
+
};
|
|
432
|
+
|
|
313
433
|
// 读取翻译文件
|
|
314
434
|
const readTranslationFiles = (localesFilePath, dashScope, checkLanguages) => {
|
|
315
435
|
// 读取中文翻译文件
|
|
@@ -376,7 +496,7 @@ const analyzeMissingAndUnusedKeys = (usedKeys, zhCN, languageFiles) => {
|
|
|
376
496
|
};
|
|
377
497
|
|
|
378
498
|
// 生成检查结果输出
|
|
379
|
-
const generateCheckOutput = (missingKeys, unusedKeys) => {
|
|
499
|
+
const generateCheckOutput = (missingKeys, unusedKeys, untranslatedTexts) => {
|
|
380
500
|
const outputLines = [];
|
|
381
501
|
outputLines.push('=== 检查结果 ===');
|
|
382
502
|
outputLines.push('');
|
|
@@ -392,7 +512,7 @@ const generateCheckOutput = (missingKeys, unusedKeys) => {
|
|
|
392
512
|
}
|
|
393
513
|
|
|
394
514
|
for (const [language, missing] of Object.entries(missingKeys)) {
|
|
395
|
-
if (language === 'zhCN') continue;
|
|
515
|
+
if (language === 'zhCN') continue;
|
|
396
516
|
|
|
397
517
|
if (missing.length > 0) {
|
|
398
518
|
outputLines.push('');
|
|
@@ -417,7 +537,7 @@ const generateCheckOutput = (missingKeys, unusedKeys) => {
|
|
|
417
537
|
}
|
|
418
538
|
|
|
419
539
|
for (const [language, unused] of Object.entries(unusedKeys)) {
|
|
420
|
-
if (language === 'zhCN') continue;
|
|
540
|
+
if (language === 'zhCN') continue;
|
|
421
541
|
|
|
422
542
|
if (unused.length > 0) {
|
|
423
543
|
outputLines.push('');
|
|
@@ -429,6 +549,20 @@ const generateCheckOutput = (missingKeys, unusedKeys) => {
|
|
|
429
549
|
}
|
|
430
550
|
}
|
|
431
551
|
|
|
552
|
+
outputLines.push('');
|
|
553
|
+
outputLines.push('3. 未翻译的中文文案:');
|
|
554
|
+
|
|
555
|
+
if (untranslatedTexts.length > 0) {
|
|
556
|
+
outputLines.push('');
|
|
557
|
+
outputLines.push(`共发现 ${untranslatedTexts.length} 条未翻译的中文文案:`);
|
|
558
|
+
untranslatedTexts.forEach((item) => {
|
|
559
|
+
outputLines.push(` ${item.file}:${item.line}:${item.column} "${item.text}"`);
|
|
560
|
+
});
|
|
561
|
+
} else {
|
|
562
|
+
outputLines.push('');
|
|
563
|
+
outputLines.push('没有发现未翻译的中文文案');
|
|
564
|
+
}
|
|
565
|
+
|
|
432
566
|
outputLines.push('');
|
|
433
567
|
outputLines.push('=== 检查完成 ===');
|
|
434
568
|
|
|
@@ -436,7 +570,13 @@ const generateCheckOutput = (missingKeys, unusedKeys) => {
|
|
|
436
570
|
};
|
|
437
571
|
|
|
438
572
|
// 处理输出和JSON文件生成
|
|
439
|
-
const handleOutputAndJsonGeneration = (
|
|
573
|
+
const handleOutputAndJsonGeneration = (
|
|
574
|
+
outputLines,
|
|
575
|
+
missingKeys,
|
|
576
|
+
unusedKeys,
|
|
577
|
+
untranslatedTexts,
|
|
578
|
+
autoDeleteUnused,
|
|
579
|
+
) => {
|
|
440
580
|
// 判断是否需要生成JSON文件(自动删除模式下不生成日志文件)
|
|
441
581
|
if (outputLines.length > 100 && !autoDeleteUnused) {
|
|
442
582
|
// 生成JSON文件
|
|
@@ -448,10 +588,12 @@ const handleOutputAndJsonGeneration = (outputLines, missingKeys, unusedKeys, aut
|
|
|
448
588
|
summary: {
|
|
449
589
|
totalMissingKeys: Object.values(missingKeys).reduce((sum, keys) => sum + keys.length, 0),
|
|
450
590
|
totalUnusedKeys: Object.values(unusedKeys).reduce((sum, keys) => sum + keys.length, 0),
|
|
591
|
+
totalUntranslatedTexts: untranslatedTexts.length,
|
|
451
592
|
totalOutputLines: outputLines.length,
|
|
452
593
|
},
|
|
453
594
|
missingKeys,
|
|
454
595
|
unusedKeys,
|
|
596
|
+
untranslatedTexts,
|
|
455
597
|
};
|
|
456
598
|
|
|
457
599
|
try {
|
|
@@ -463,6 +605,7 @@ const handleOutputAndJsonGeneration = (outputLines, missingKeys, unusedKeys, aut
|
|
|
463
605
|
console.log('\n📊 概要信息:');
|
|
464
606
|
console.log(` • 缺失的翻译key总数: ${jsonResult.summary.totalMissingKeys}`);
|
|
465
607
|
console.log(` • 未使用的翻译key总数: ${jsonResult.summary.totalUnusedKeys}`);
|
|
608
|
+
console.log(` • 未翻译的中文文案总数: ${jsonResult.summary.totalUntranslatedTexts}`);
|
|
466
609
|
|
|
467
610
|
// 显示各语言的统计
|
|
468
611
|
console.log('\n📋 各语言统计:');
|
|
@@ -478,10 +621,13 @@ const handleOutputAndJsonGeneration = (outputLines, missingKeys, unusedKeys, aut
|
|
|
478
621
|
console.log(` ${displayLang}.json: ${unused.length}个`);
|
|
479
622
|
}
|
|
480
623
|
|
|
624
|
+
if (untranslatedTexts.length > 0) {
|
|
625
|
+
console.log(` 未翻译的中文文案: ${untranslatedTexts.length}条`);
|
|
626
|
+
}
|
|
627
|
+
|
|
481
628
|
console.log('\n=== 检查完成 ===\n');
|
|
482
629
|
} catch (error) {
|
|
483
630
|
console.error(`\n❌ 生成JSON文件失败: ${error.message}`);
|
|
484
|
-
// 如果JSON文件生成失败,仍然输出完整结果(自动删除模式下不输出)
|
|
485
631
|
if (!autoDeleteUnused) {
|
|
486
632
|
outputLines.forEach((line) => console.log(line));
|
|
487
633
|
}
|
|
@@ -710,6 +856,7 @@ const check = async function (sourcePath, params) {
|
|
|
710
856
|
medusa,
|
|
711
857
|
autoDeleteUnused,
|
|
712
858
|
summaryOnly,
|
|
859
|
+
doNotTranslateFiles,
|
|
713
860
|
check: checkConfig,
|
|
714
861
|
} = params;
|
|
715
862
|
|
|
@@ -730,6 +877,15 @@ const check = async function (sourcePath, params) {
|
|
|
730
877
|
// 分析缺失和未使用的key
|
|
731
878
|
const { missingKeys, unusedKeys } = analyzeMissingAndUnusedKeys(usedKeys, zhCN, languageFiles);
|
|
732
879
|
|
|
880
|
+
// 扫描未翻译的中文文案
|
|
881
|
+
console.log(` # 扫描未翻译的中文文案...`);
|
|
882
|
+
const untranslatedTexts = scanUntranslatedTexts(files, doNotTranslateFiles);
|
|
883
|
+
if (untranslatedTexts.length > 0) {
|
|
884
|
+
console.log(` # 发现 ${untranslatedTexts.length} 条未翻译的中文文案`);
|
|
885
|
+
} else {
|
|
886
|
+
console.log(` # 未发现未翻译的中文文案`);
|
|
887
|
+
}
|
|
888
|
+
|
|
733
889
|
// summaryOnly 模式:只输出各语言缺失的key数量,不做删除/生成等操作
|
|
734
890
|
if (summaryOnly) {
|
|
735
891
|
console.log('');
|
|
@@ -755,19 +911,34 @@ const check = async function (sourcePath, params) {
|
|
|
755
911
|
}
|
|
756
912
|
|
|
757
913
|
console.log('');
|
|
758
|
-
if (
|
|
759
|
-
console.log(
|
|
914
|
+
if (untranslatedTexts.length > 0) {
|
|
915
|
+
console.log(` 未翻译的中文文案: ${untranslatedTexts.length} 条`);
|
|
916
|
+
} else {
|
|
917
|
+
console.log(` 未翻译的中文文案: ✓ 无`);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
console.log('');
|
|
921
|
+
if (allGood && untranslatedTexts.length === 0) {
|
|
922
|
+
console.log('✅ 所有语言翻译完整,无未翻译文案');
|
|
923
|
+
} else if (allGood) {
|
|
924
|
+
console.log('❌ 存在未翻译的中文文案,请运行 npx i18n check 查看详情');
|
|
760
925
|
} else {
|
|
761
926
|
console.log('❌ 存在翻译缺失,请运行 npx i18n check 查看详情');
|
|
762
927
|
}
|
|
763
928
|
|
|
764
|
-
return { missingKeys, unusedKeys, usedKeysData };
|
|
929
|
+
return { missingKeys, unusedKeys, usedKeysData, untranslatedTexts };
|
|
765
930
|
}
|
|
766
931
|
|
|
767
932
|
// 完整模式:输出详情、删除未使用key、生成Excel等
|
|
768
|
-
const outputLines = generateCheckOutput(missingKeys, unusedKeys);
|
|
933
|
+
const outputLines = generateCheckOutput(missingKeys, unusedKeys, untranslatedTexts);
|
|
769
934
|
|
|
770
|
-
handleOutputAndJsonGeneration(
|
|
935
|
+
handleOutputAndJsonGeneration(
|
|
936
|
+
outputLines,
|
|
937
|
+
missingKeys,
|
|
938
|
+
unusedKeys,
|
|
939
|
+
untranslatedTexts,
|
|
940
|
+
autoDeleteUnused,
|
|
941
|
+
);
|
|
771
942
|
|
|
772
943
|
await handleUnusedKeysDeletion(
|
|
773
944
|
unusedKeys,
|
|
@@ -783,6 +954,7 @@ const check = async function (sourcePath, params) {
|
|
|
783
954
|
missingKeys,
|
|
784
955
|
unusedKeys,
|
|
785
956
|
usedKeysData,
|
|
957
|
+
untranslatedTexts,
|
|
786
958
|
};
|
|
787
959
|
|
|
788
960
|
return result;
|
package/lib/parse-jsx.js
CHANGED
|
@@ -4,87 +4,13 @@ const generate = require('@babel/generator').default;
|
|
|
4
4
|
const t = require('@babel/types');
|
|
5
5
|
const fs = require('fs-extra');
|
|
6
6
|
const { shouldIgnoreLineByComment, shouldIgnoreBlockByComment } = require('./utils/file-utils');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const chinesePunctuationRegex =
|
|
15
|
-
/[,。!?;:""''()【】《》|、…—·~~@#$%^&*()_+\-={}\[\]|\\:;"'<>,.?\/]/g;
|
|
16
|
-
// 移除所有中文标点符号
|
|
17
|
-
const textWithoutPunctuation = text.replace(chinesePunctuationRegex, '');
|
|
18
|
-
// 如果移除标点后为空,说明只包含标点符号
|
|
19
|
-
return textWithoutPunctuation.trim() === '';
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const onlyContainsEmoji = (text) => {
|
|
23
|
-
// Emoji 正则表达式,包含常见的emoji Unicode范围
|
|
24
|
-
const emojiRegex =
|
|
25
|
-
/[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1F018}-\u{1F270}]|[\u{238C}-\u{2454}]|[\u{20D0}-\u{20FF}]|[\u{FE00}-\u{FE0F}]|[\u{1F200}-\u{1F2FF}]|[\u{1F700}-\u{1F77F}]|[\u{1F780}-\u{1F7FF}]|[\u{1F800}-\u{1F8FF}]|[\u{2300}-\u{23FF}]|[\u{2B00}-\u{2BFF}]|[\u{3030}]|[\u{303D}]|[\u{3297}]|[\u{3299}]/gu;
|
|
26
|
-
|
|
27
|
-
// 移除所有空白字符
|
|
28
|
-
const trimmedText = text.trim();
|
|
29
|
-
|
|
30
|
-
// 如果文本为空,返回false
|
|
31
|
-
if (!trimmedText) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 移除所有emoji字符
|
|
36
|
-
const textWithoutEmoji = trimmedText.replace(emojiRegex, '');
|
|
37
|
-
|
|
38
|
-
// 如果移除emoji后为空,说明只包含emoji
|
|
39
|
-
return textWithoutEmoji === '';
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// 检查节点是否是i18n调用
|
|
43
|
-
function isI18nCall(node) {
|
|
44
|
-
return (
|
|
45
|
-
node &&
|
|
46
|
-
node.type === 'CallExpression' &&
|
|
47
|
-
node.callee &&
|
|
48
|
-
node.callee.type === 'MemberExpression' &&
|
|
49
|
-
node.callee.object &&
|
|
50
|
-
node.callee.object.name === '$i18n' &&
|
|
51
|
-
node.callee.property &&
|
|
52
|
-
node.callee.property.name === 'get'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 检测是否在 console 方法中
|
|
57
|
-
function isInConsoleCall(path) {
|
|
58
|
-
const consoleMethods = [
|
|
59
|
-
'log',
|
|
60
|
-
'warn',
|
|
61
|
-
'error',
|
|
62
|
-
'info',
|
|
63
|
-
'debug',
|
|
64
|
-
'trace',
|
|
65
|
-
'group',
|
|
66
|
-
'groupEnd',
|
|
67
|
-
'groupCollapsed',
|
|
68
|
-
'assert',
|
|
69
|
-
'table',
|
|
70
|
-
'time',
|
|
71
|
-
'timeEnd',
|
|
72
|
-
'count',
|
|
73
|
-
'countReset',
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
return path.findParent((p) => {
|
|
77
|
-
return (
|
|
78
|
-
p.isCallExpression() &&
|
|
79
|
-
p.node.callee &&
|
|
80
|
-
p.node.callee.type === 'MemberExpression' &&
|
|
81
|
-
p.node.callee.object &&
|
|
82
|
-
p.node.callee.object.name === 'console' &&
|
|
83
|
-
p.node.callee.property &&
|
|
84
|
-
consoleMethods.includes(p.node.callee.property.name)
|
|
85
|
-
);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
7
|
+
const {
|
|
8
|
+
chnRegExp,
|
|
9
|
+
onlyContainsChinesePunctuation,
|
|
10
|
+
onlyContainsEmoji,
|
|
11
|
+
isI18nCall,
|
|
12
|
+
isInConsoleCall,
|
|
13
|
+
} = require('./utils/ast-utils');
|
|
88
14
|
|
|
89
15
|
// 生成i18n调用节点
|
|
90
16
|
const createI18nCallNode = (i18nKey, defaultMessage, variables = []) => {
|
package/lib/utils/ast-utils.js
CHANGED
|
@@ -93,8 +93,74 @@ function checkI18nCall(node) {
|
|
|
93
93
|
return extractI18nKey(node);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
const chnRegExp = /[^\x00-\xff]/;
|
|
97
|
+
|
|
98
|
+
const onlyContainsChinesePunctuation = (text) => {
|
|
99
|
+
const chinesePunctuationRegex =
|
|
100
|
+
/[,。!?;:""''()【】《》|、…—·~~@#$%^&*()_+\-={}\[\]|\\:;"'<>,.?\/]/g;
|
|
101
|
+
const textWithoutPunctuation = text.replace(chinesePunctuationRegex, '');
|
|
102
|
+
return textWithoutPunctuation.trim() === '';
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const onlyContainsEmoji = (text) => {
|
|
106
|
+
const emojiRegex =
|
|
107
|
+
/[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1F018}-\u{1F270}]|[\u{238C}-\u{2454}]|[\u{20D0}-\u{20FF}]|[\u{FE00}-\u{FE0F}]|[\u{1F200}-\u{1F2FF}]|[\u{1F700}-\u{1F77F}]|[\u{1F780}-\u{1F7FF}]|[\u{1F800}-\u{1F8FF}]|[\u{2300}-\u{23FF}]|[\u{2B00}-\u{2BFF}]|[\u{3030}]|[\u{303D}]|[\u{3297}]|[\u{3299}]/gu;
|
|
108
|
+
const trimmedText = text.trim();
|
|
109
|
+
if (!trimmedText) return false;
|
|
110
|
+
return trimmedText.replace(emojiRegex, '') === '';
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function isI18nCall(node) {
|
|
114
|
+
return (
|
|
115
|
+
node &&
|
|
116
|
+
node.type === 'CallExpression' &&
|
|
117
|
+
node.callee &&
|
|
118
|
+
node.callee.type === 'MemberExpression' &&
|
|
119
|
+
node.callee.object &&
|
|
120
|
+
node.callee.object.name === '$i18n' &&
|
|
121
|
+
node.callee.property &&
|
|
122
|
+
node.callee.property.name === 'get'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isInConsoleCall(path) {
|
|
127
|
+
const consoleMethods = [
|
|
128
|
+
'log',
|
|
129
|
+
'warn',
|
|
130
|
+
'error',
|
|
131
|
+
'info',
|
|
132
|
+
'debug',
|
|
133
|
+
'trace',
|
|
134
|
+
'group',
|
|
135
|
+
'groupEnd',
|
|
136
|
+
'groupCollapsed',
|
|
137
|
+
'assert',
|
|
138
|
+
'table',
|
|
139
|
+
'time',
|
|
140
|
+
'timeEnd',
|
|
141
|
+
'count',
|
|
142
|
+
'countReset',
|
|
143
|
+
];
|
|
144
|
+
return path.findParent((p) => {
|
|
145
|
+
return (
|
|
146
|
+
p.isCallExpression() &&
|
|
147
|
+
p.node.callee &&
|
|
148
|
+
p.node.callee.type === 'MemberExpression' &&
|
|
149
|
+
p.node.callee.object &&
|
|
150
|
+
p.node.callee.object.name === 'console' &&
|
|
151
|
+
p.node.callee.property &&
|
|
152
|
+
consoleMethods.includes(p.node.callee.property.name)
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
96
157
|
module.exports = {
|
|
97
158
|
extractStringValue,
|
|
98
159
|
extractI18nKey,
|
|
99
160
|
checkI18nCall,
|
|
161
|
+
chnRegExp,
|
|
162
|
+
onlyContainsChinesePunctuation,
|
|
163
|
+
onlyContainsEmoji,
|
|
164
|
+
isI18nCall,
|
|
165
|
+
isInConsoleCall,
|
|
100
166
|
};
|