|
- const fs = require('fs');
- const path = require('path');
-
- /**
- * 检查指定文件中使用的 t('...') 翻译键是否都在 JSON 文件中定义
- */
- function checkMissingTranslations(sourceFile, jsonFile) {
- // 读取源代码文件
- const sourceCode = fs.readFileSync(sourceFile, 'utf-8');
-
- // 读取翻译 JSON 文件
- const translations = JSON.parse(fs.readFileSync(jsonFile, 'utf-8'));
-
- // 只匹配 t('...') 和 t("...") 和 t(`...`),不包含模板变量
- const tRegex = /\bt\(["`']([^"`'${}]+)["`']\)/g;
- const matches = [...sourceCode.matchAll(tRegex)];
-
- // 获取所有使用的键(去重并清理空白)
- const usedKeys = [...new Set(
- matches
- .map(match => match[1].trim())
- .filter(key => key.length > 0)
- )];
-
- // 查找缺失的键
- const missingKeys = usedKeys.filter(key => !(key in translations));
-
- // 查找未使用的键
- const definedKeys = Object.keys(translations);
- const unusedKeys = definedKeys.filter(key => !usedKeys.includes(key));
-
- return {
- usedKeys: usedKeys.sort(),
- missingKeys: missingKeys.sort(),
- unusedKeys: unusedKeys.sort(),
- totalUsed: usedKeys.length,
- totalMissing: missingKeys.length,
- totalUnused: unusedKeys.length,
- totalDefined: definedKeys.length
- };
- }
-
- /**
- * 递归检查目录中所有文件
- */
- function checkDirectory(dir, jsonFile) {
- const results = {};
- let totalMissing = 0;
-
- function scanDir(directory) {
- const files = fs.readdirSync(directory);
-
- files.forEach(file => {
- const fullPath = path.join(directory, file);
- const stat = fs.statSync(fullPath);
-
- if (stat.isDirectory()) {
- scanDir(fullPath);
- } else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
- try {
- const result = checkMissingTranslations(fullPath, jsonFile);
- if (result.missingKeys.length > 0) {
- results[fullPath] = result;
- totalMissing += result.missingKeys.length;
- }
- } catch (err) {
- console.error(`❌ 处理文件出错 ${fullPath}:`, err.message);
- }
- }
- });
- }
-
- scanDir(dir);
- return { results, totalMissing };
- }
-
- // 主程序
- const args = process.argv.slice(2);
-
- if (args.length === 0) {
- console.log('📚 翻译键检查工具\n');
- console.log('用法:');
- console.log(' 检查单个文件: node check-translations.js <source-file> <json-file>');
- console.log(' 检查整个目录: node check-translations.js --dir <directory> <json-file>');
- console.log('\n示例:');
- console.log(' node check-translations.js src/components/Jodetail/JodetailSearch.tsx src/i18n/zh/jo.json');
- console.log(' node check-translations.js --dir src/components/Jodetail src/i18n/zh/jo.json');
- console.log('\n注意:');
- console.log(' 只检查 t("key") 调用');
- console.log(' ❌ 忽略 alert(), console.log() 等普通字符串');
- console.log(' ❌ 忽略模板字符串中的 ${} 变量部分');
- process.exit(0);
- }
-
- if (args[0] === '--dir') {
- // 检查整个目录
- const directory = args[1];
- const jsonFile = args[2];
-
- console.log(`\n🔍 正在检查目录: ${directory}`);
- console.log(`📖 使用翻译文件: ${jsonFile}\n`);
-
- const { results, totalMissing } = checkDirectory(directory, jsonFile);
-
- if (Object.keys(results).length === 0) {
- console.log(' 太棒了!没有发现缺失的翻译键!');
- } else {
- console.log(`⚠️ 发现 ${Object.keys(results).length} 个文件有缺失的翻译键\n`);
-
- // 收集所有缺失的键(去重)
- const allMissingKeys = new Set();
-
- Object.entries(results).forEach(([file, result]) => {
- const relativePath = file.replace(process.cwd(), '').replace(/\\/g, '/');
- console.log(`\n📄 ${relativePath}`);
- console.log(` 使用: ${result.totalUsed} 个键 | ❌ 缺失: ${result.totalMissing} 个键`);
- result.missingKeys.forEach(key => {
- console.log(` - "${key}"`);
- allMissingKeys.add(key);
- });
- });
-
- // 输出可以直接复制的 JSON 格式
- console.log(`\n\n📋 需要添加到 ${jsonFile} 的翻译键 (共 ${allMissingKeys.size} 个):`);
- console.log('─'.repeat(60));
- allMissingKeys.forEach(key => {
- console.log(` "${key}": "",`);
- });
- console.log('─'.repeat(60));
- }
- } else {
- // 检查单个文件
- const sourceFile = args[0];
- const jsonFile = args[1];
-
- console.log(`\n🔍 正在检查文件: ${sourceFile}`);
- console.log(`📖 使用翻译文件: ${jsonFile}\n`);
-
- try {
- const result = checkMissingTranslations(sourceFile, jsonFile);
-
- console.log(`📊 统计信息:`);
- console.log(` 代码中使用的键: ${result.totalUsed}`);
- console.log(` JSON 中定义的键: ${result.totalDefined}`);
- console.log(` ❌ 缺失的键: ${result.totalMissing}`);
- console.log(` ⚠️ 未使用的键: ${result.totalUnused}`);
-
- if (result.missingKeys.length > 0) {
- console.log(`\n❌ 缺失的翻译键 (需要添加到 ${jsonFile}):`);
- console.log('─'.repeat(60));
- result.missingKeys.forEach(key => {
- console.log(` "${key}": "",`);
- });
- console.log('─'.repeat(60));
- } else {
- console.log('\n 太棒了!所有使用的翻译键都已定义!');
- }
-
- if (result.unusedKeys.length > 0 && result.unusedKeys.length <= 20) {
- console.log(`\n⚠️ 未使用的翻译键 (在 JSON 中但代码中未使用):`);
- result.unusedKeys.forEach(key => {
- console.log(` - "${key}"`);
- });
- }
- } catch (err) {
- console.error('❌ 错误:', err.message);
- process.exit(1);
- }
- }
-
- console.log('\n');
- {/*
- # 检查单个文件
- node check-translations.js src/components/Jodetail/JodetailSearch.tsx src/i18n/zh/jo.json
-
- # 检查整个 Jodetail 目录
- node check-translations.js --dir src/components/Jodetail src/i18n/zh/jo.json
-
- # 检查所有组件
- node check-translations.js --dir src/components src/i18n/zh/jo.json
- */}
|