通过脚本自动检查项目中的中文

现在越来越多的公司在做出海项目,出海项目首先要解决的就是语言国际化的问题,有很多如l18n、l10n的工具可以用,这些工具可以提供解决方案,但是不能约束开发者的开发行为。开发者仍然可能在代码中存留没有做过国际化处理的部分,假设后续 MR 的Code Review也没有查出来,这就是很大的隐患了,甚至会有监管风险,因为很多项目不想让别人知道是中国团队做的,所以要严格禁止控制台输出中文日志。

我最近也是接到一个这样的需求,本来需求是让我人肉检查一下项目中有没有输出中文日志的情况。人肉是不可能人肉的,我觉得这个可以通过写脚本来实现检查,因为核心要素其实就两个点:

  1. 需要检查中文
  2. 需要全局检查所有文件

这些要素都可以通过 Node.js 脚本来实现。

公众号:Code程序人生,个人网站:https://creatorblog.cn

这个脚本会实现以下能力:

  1. 读取当前项目下所有目录、所有文件
  2. 能有效检查文件中是否包含中文字符
  3. 可以提前声明要忽略的文件夹/文件
  4. 主动忽略所有注释,包含///* ... */{/* ... */}
  5. 输出详细的检查结果日志,包含 文件路径、文件行数、相关代码,并且高亮检查出来的中文的部分

image.png

在项目根目录新建一个文件,如check-chinese.js。写入以下内容:

const fs = require('fs');
const path = require('path');

// 中文字符的正则表达式
const chineseRegex = /[\u4e00-\u9fa5]/;

// 要忽略的文件或目录
const ignorePatterns = [
  'node_modules',
  '.git',
  '.next',
  'public',
  'locales',
  'dist',
  'build',
  'check-chinese'
];

// 要检查的文件扩展名
const extensions = ['.ts', '.tsx', '.js', '.jsx'];

// 控制台颜色
const colors = {
  reset: '\x1b[0m',
  bright: '\x1b[1m',
  dim: '\x1b[2m',
  underscore: '\x1b[4m',
  blink: '\x1b[5m',
  reverse: '\x1b[7m',
  hidden: '\x1b[8m',
  
  black: '\x1b[30m',
  red: '\x1b[31m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  magenta: '\x1b[35m',
  cyan: '\x1b[36m',
  white: '\x1b[37m',
  
  bgBlack: '\x1b[40m',
  bgRed: '\x1b[41m',
  bgGreen: '\x1b[42m',
  bgYellow: '\x1b[43m',
  bgBlue: '\x1b[44m',
  bgMagenta: '\x1b[45m',
  bgCyan: '\x1b[46m',
  bgWhite: '\x1b[47m'
};

// 使用第三方库来解析代码和注释
function parseFileContent(content, filePath) {
  // 首先通过简单的方式移除所有注释
  let processedContent = content;
  
  // 1. 移除多行注释 /* ... */
  processedContent = processedContent.replace(/\/\*[\s\S]*?\*\//g, match => {
    // 保留换行符以保持行号
    return match.replace(/[^\n\r]/g, ' ');
  });
  
  // 2. 移除单行注释 // ...
  processedContent = processedContent.replace(/\/\/.*$/gm, match => {
    // 保留相同长度的空格
    return ' '.repeat(match.length);
  });
  
  // 3. 移除JSX注释 {/* ... */}
  processedContent = processedContent.replace(/\{\/\*[\s\S]*?\*\/\}/g, match => {
    // 保留换行符以保持行号
    return match.replace(/[^\n\r]/g, ' ');
  });
  
  return processedContent;
}

// 检查文件中的中文字符
function checkFileForChinese(filePath) {
  try {
    const content = fs.readFileSync(filePath, 'utf8');
    
    // 处理文件内容,移除注释
    const processedContent = parseFileContent(content, filePath);
    
    // 按行检查处理后的内容
    const lines = processedContent.split('\n');
    const originalLines = content.split('\n');
    
    const results = [];
    
    lines.forEach((line, index) => {
      // 检查行中是否包含中文字符
      if (chineseRegex.test(line)) {
        results.push({
          file: filePath,
          line: index + 1,
          content: originalLines[index].trim()
        });
      }
    });
    
    return results;
  } catch (error) {
    console.error(`${colors.red}无法读取文件 ${filePath}: ${error.message}${colors.reset}`);
    return [];
  }
}

// 递归遍历目录
function traverseDirectory(dir) {
  const results = [];
  
  try {
    const files = fs.readdirSync(dir, { withFileTypes: true });
    
    for (const file of files) {
      const fullPath = path.join(dir, file.name);
      
      // 检查是否应该忽略此路径
      if (ignorePatterns.some(pattern => fullPath.includes(pattern))) {
        continue;
      }
      
      if (file.isDirectory()) {
        results.push(...traverseDirectory(fullPath));
      } else if (file.isFile() && extensions.includes(path.extname(file.name))) {
        results.push(...checkFileForChinese(fullPath));
      }
    }
  } catch (error) {
    console.error(`${colors.red}无法读取目录 ${dir}: ${error.message}${colors.reset}`);
  }
  
  return results;
}

// 高亮显示中文字符
function highlightChinese(text) {
  return text.replace(/([\u4e00-\u9fa5]+)/g, `${colors.bgYellow}${colors.black}$1${colors.reset}`);
}

// 主函数
function main() {
  console.log(`${colors.cyan}${colors.bright}=== 开始扫描中文字符... ===${colors.reset}`);
  
  const results = traverseDirectory('.');
  
  if (results.length === 0) {
    console.log(`${colors.green}${colors.bright}✓ 没有找到非注释中的中文字符!${colors.reset}`);
  } else {
    console.log(`${colors.yellow}${colors.bright}! 找到 ${results.length} 处可能需要国际化的中文字符:${colors.reset}\n`);
    
    // 按文件分组
    const fileGroups = {};
    results.forEach(result => {
      if (!fileGroups[result.file]) {
        fileGroups[result.file] = [];
      }
      fileGroups[result.file].push(result);
    });
    
    // 输出分组结果
    Object.keys(fileGroups).forEach(file => {
      console.log(`${colors.blue}${colors.bright}文件: ${file}${colors.reset}`);
      fileGroups[file].forEach(result => {
        console.log(`  ${colors.magenta}${result.line}:${colors.reset} ${highlightChinese(result.content)}`);
      });
      console.log(''); // 空行分隔不同文件
    });
    
    console.log(`${colors.yellow}${colors.bright}请检查上述中文字符,并考虑使用国际化方案替换它们。${colors.reset}`);
  }
}

// 执行主函数
main();

然后通过node ./check-chinese.js来执行脚本

也可以在package.json中新增一个scripts

{
   ...
  "scripts": {
    ...
    "check-chinese": "node ./check-chinese.js"
  },
}

然后通过npm run check-chinese来执行脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CreatorRay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值