在工作中遇到问题,首先用现有的技术手段解决它,至于方法蠢不蠢笨不笨就先不管了,最重要的是先实现。
所以现在我工作中遇到的一个问题是:同事使用脚本将代码中的出现的中文添加一个多语言接口去包住文本,来实现后续的多语言翻译处理。例如,在原来的代码中有一句
let a:string = "我是一个中文字符串";
同事运行脚本后得到的结果就是
let a:string = i18n.t("我是一个中文字符串");
由于同事的脚本考虑情况不够周全,所以经过一段时间后,问题暴露了。我发现了项目代码中存在大量的的重复设置,就像这样:
let a:string = i18n.t(i18n.t(i18n.t("我是一个中文字符串")));
看了同事写的脚本,发现他只是从代码字符串中抽出中文字符,然后给中文字符包上多语言接口,竟然没有检查这串中文字符是否已经被多语言接口包着了,于是导致了这样的情况。
然后我全局搜索了一下项目到底有多少个“i18n.t(i18n.t(”,结果有几百个代码文件被“感染”了…如果手动修改那真是痛不欲生,那么我也写个脚本进行去重吧。
练习一下 nodeJs 的文件操作好了。反正我不熟。
思路还是很简单的:
- 首先遍历项目目录,获取到所有指定后缀的文件目录路径列表;
- 逐条读取文件目录路径的文件,转换为字符串,检查其中是否包含目标字符串(这里我指定为“i18n.t(i18n.t(”);
- 如果是需要去重处理的文件,将文件以换行符“\r\n”切割成字符串数组,将包含去重目标的字符串进行具体的处理;
因为这里要处理的字符串是一个重复调用的接口,不能把一串连续的字符替换掉就完事,它是有括号的,可以说是对括号两端处理,中间的内容不动,这个是有点麻烦的地方,所以要怎么把
i18n.t(i18n.t("哈哈哈哈哈"));
//需要修改为
i18n.t("哈哈哈哈哈");
我的想法是这样的:
- 在完整的字符串中获取到重复接口字符串的位置 i;
- 根据这个位置 i 把完整的字符串截成两端:索引 0 ~ i 的子字符串 str1,和 i+重复字符长度+1 ~ 完整字符串末尾的子字符串 str2;
- str1 + 一个正常的接口 + str2 这样就拼接处理正常的只调用一次多语言接口的字符串了
然后把代码的字符串数组用换行符 “\r\n” join 一下,最后就把整个代码文件的字符串写入到路径指向的文件。
就这样的打完收工。
let fs = require("fs");
let path = require("path");
const basePath = "这里填目标文件目录路径";
let myIncludes = ['.ts'];//我需要的文件类型的后缀
function mapDir(dir, list) {
const files = fs.readdirSync(dir);
files.forEach((item, index) => {
var fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
mapDir(path.join(dir, item), list); //递归读取文件
} else {
if(isInclude(fullPath)){
list.push(fullPath);
}
}
});
return list;
}
let myList = [];
mapDir(
basePath,
myList
);
console.log("遍历到目标文件数:"+myList.length);
/**判断一个文件是否是设定类型集合中包含的文件类型*/
function isInclude(filePath){
var pArr = filePath.split('.');
let len = pArr.length;
if(pArr.length >= 2 && myIncludes.indexOf('.'+pArr[len-1]) != -1){
return true;
}
return false;
}
//代码中重复的方法接口
const repeatStr = "i18n.t(i18n.t(";
let count = 0;
for(let i = 0; i < myList.length; i++){
let tsUrl = myList[i];
let tsCode = fs.readFileSync(tsUrl);
let str = tsCode.toString();
if(str.indexOf(repeatStr) != -1){
let resultStr = delRepeatMyi18n(str);
fs.writeFileSync(tsUrl, resultStr);
count++;
console.log("翻译设置重复调用接口移除完成:" + tsUrl);
}
}
console.log("\n---------------- 修改完成,总共修改 " + count + " 处")
function delRepeatMyi18n(str){
let result = "";
let tempList = str.split('\r\n');
let arr = [];
tempList.forEach((line)=>{
if(line.indexOf(repeatStr) != -1){
line = cutRepeat(line);
}
arr.push(line);
});
result = arr.join("\r\n");
return result;
}
function cutRepeat(str){
let idx = str.indexOf(repeatStr);
let str1 = str.substring(0, idx);
let str2 = str.substring(idx+repeatStr.length);
str1 += "i18n.t(";
let leftNum = 0;
let rightNum = 0;
for(let i = 0; i < str2.length; i++){
let tempStr = str2[i];
if(tempStr == "("){
leftNum++;
}else if(tempStr == ")"){
rightNum++;
if(rightNum - leftNum == 2){
str2 = str2.substring(0, i) + str2.substring(i+1)
leftNum = 0;
rightNum = 0;
break;
}
}
}
str = str1 + str2;
if(str.indexOf(repeatStr) != -1){
str = cutRepeat(str);
}
return str;
}
最后总结一下,这里主要用到了 nodejs 的文件读写,不过为了方便而用了同步方式。更好的应该是用异步方式进行处理。不过就像开头说的,无论方法是蠢还是笨,先实现出来,解决了眼前问题再追求更高层次的东西哈哈。