diff --git a/check-translations.js b/check-translations.js new file mode 100644 index 0000000..f89fe00 --- /dev/null +++ b/check-translations.js @@ -0,0 +1,171 @@ +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 '); + console.log(' 检查整个目录: node check-translations.js --dir '); + 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'); \ No newline at end of file diff --git a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx index f940350..5c8155c 100644 --- a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx +++ b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx @@ -629,21 +629,7 @@ const PickOrderSearch: React.FC = ({ pickOrders }) => { {/* ✅ Updated print buttons with completion status */} -{/* - - */} +