当前位置: 首页 > 后端技术 > Node.js

Electron+Vue从零开始搭建了一个本地文件翻译器

时间:2023-04-03 10:31:03 Node.js

又一个愉快的周末来临了。当我轻快地回到家,正要和女友拥抱时,却发现女友正皱着眉头看着电脑上的电脑。堆满了英文文件夹,不停地呻吟,喃喃自语:“我好累”。于是,我定睛一看,看到她电脑上一堆英文文件夹,反复复制文件名,然后在百度翻译中翻译成中文,翻译完后又给文件重命名。这个很难(硬。想到女朋友的烦恼,身为她程序员男朋友的我怎么可能袖手旁观,我陷入了沉思,突然想到可以用Node来做她手上的操作,就这么干了,我立马抛下女朋友,打开电脑开始行动。毕竟,女人只会影响我出剑的速度。项目搭建项目搭建还是采用老组合,Electron+Vue+Node。这次就不说electron和vue怎么整合了。详见文章Electron+vue从零开始搭建本地音乐播放器。懒人可以直接克隆我的模板文件开发,点这里点这里。项目功能显然需要解决几个痛点:自动翻译、译名自动重命名、批量翻译、易操作。明确了能够拖放目录或拖放文件的要求,让我们逐步解决它们。实现效果项目实现项目的实现并不复杂,一一解决,有的放矢。自动翻译已通过有道翻译、百度翻译、谷歌翻译测试。经过比较,我最终选择了百度翻译。百度翻译一般翻译每月200万字免费(QPS=10),还是能满足我的需求。注册并申请翻译服务使用翻译服务需要到百度翻译开放平台申请权限。您可以选择通用翻译服务。注册为开发者后,可以新建项目获取appid。这个appid很重要,决定了能否正确发起翻译请求。.拼接翻译API可以查看文档了解。通用翻译API的HTTP地址为https://api.fanyi.baidu.com/api/trans/vip/translate,可以携带如下参数。由于安全限制,需要生成签名。签名需要按照appid+q+salt+key的顺序拼接得到一个字符串,然后md5加密这个字符串constsalt=parseInt(Math.random()*1000000000);constsign=md5(globalData.appid+q+salt+globalData.key);URLconstparams=encodeURI(`q=${q}&from=auto&to=${globalData.to}&appid=${globalData.appid}&salt=${salt}&sign=${sign}`);翻译函数代码constmd5=require("md5");varrp=require("request-promise");const{globalData}=require("./config.js");functiontranslate(msg){constq=消息;constsalt=parseInt(Math.random()*1000000000);//添加盐constsign=md5(globalData.appid+q+salt+globalData.key);//生成签名constparams=encodeURI(`q=${q}&from=auto&to=${globalData.to}&appid=${globalData.appid}&salt=${salt}&sign=${sign}`);constoptions={uri:`https://fanyi-api.baidu.com/api/trans/vip/translate?${params}`,};返回rnrp(options).then((res)=>JSON.parse(res).trans_result);}module.exports={translate,};主要功能实现主要功能分为:批量翻译,支持向下递归翻译拖拽无限文件,或拖拽文件夹翻译重命名批量翻译。实现批量翻译需要获取目标文件夹的路径,然后通过fs.readdir读取目录下的文件信息,遍历文件信息。如果是文件,检查文件名和后缀是否分开,然后进行翻译操作。如果是目录,则进行递归操作//读取目录下的所有文件/目录fs.readdir(dirPath,(err,files)=>{if(err){//throwerr;dialog.showMessageBox({type:'info',title:'Confirm',message:'请确认是否选择目录',});this.loading=false;throwerr;}files.forEach((fileItem)=>{//遍历文件fs.stat(fullPath,(err,stat)=>{if(err){throwerr;}//判断是否是文件if(stat.isFile()){//处理文件名}elseif(stat.isDirectory()){//递归翻译this.startTrans(fullPath);}});});});获取文件夹路径获取文件夹路径有两种方式:设置inputwebkitdirectory目录属性,然后监听change事件获取选中文件夹的路径getFile(e){this.path=e.target.files[0].path;},拖拽通过H5API监听拖拽事件,获取DataTransfer目的。DataTransfer对象用于保存拖放数据

addFile(e){//将伪数组转换为排列这个。droppedFiles=[...e.dataTransfer.files];//处理多个文件拖在一起if(this.droppedFiles.length>1){//只有在同一个目录下才能多选,所以得到第一个父目录即可this.path=path.dirname(this.droppedFiles[0].路径);//标记是否为多选this.isDropMulti=true;}else{this.path=this.droppedFiles[0].路径;}},拆分文件名和后缀由于是翻译文件名,所以需要通过path.extname将文件名和后缀分开,翻译后再重新组织文件名。需要注意的是,这里需要对文件名中的特殊字符进行处理。特殊字符会影响翻译结果,并可能导致翻译失败。//获取文件的后缀格式constsuffixName=path.extname(fileItem);//获取前缀constinitSubFileName=this.removeSymbol(fileItem.split(suffixName)[0]);//去除文件名中的特殊字符removeSymbol(fileName){constreg=/[`~_!@#$^&*%()=|{}':;',.<>\\/?~!@#¥...&*()——|{}';:""'.,,?\s]/克;constnewFile=fileName.replace(reg,"");returnnewFile;},将文件名翻译成分割后的文件名,然后更新文件名。这里需要注意的是,由于百度翻译是有限制的(QPS=10),所以需要对翻译请求进行限流,限制在每秒不超过10次。节流函数const{globalData}=require("./config.js");constthrottle=(function(delay=1500){constwait=[];letcanCall=true;returnfunctionthrottle(callback){if(!canCall){if(callback)wait.push(callback);返回;}回调();canCall=false;setTimeout(()=>{canCall=true;if(wait.length){throttle(wait.shift());}},delay);};})(globalData.delay);module.exports={油门};翻译后重组文件名throttle(()=>{translate(initSubFileName).then(res=>{if(res){//如果有【】,保留文件名,如果没有,添加【】consttarget=this.checkName(res[0].dst);//拼接文件名后缀constfullSuffixName=`${target}${suffixName}`;//翻译后的文件路径constnewPath=path.resolve(dirPath,fullSuffixName);}else{//翻译失败console.log("翻译接口服务错误");dialog.showMessageBox({type:"error",title:"Error",message:"翻译接口服务出错"});}});});renamerename使用节点自带的fs.renamefs.rename(oldPath,newPath,err=>{if(err){dialog.showErrorBox("Error","翻译失败,请关闭软件重试");this.loading=false;throwerr;}console.log(`${initSubFileName}已被translatedinto${fullSuffixName}`);this.path=`${initSubFileName}hasbeentranslatedinto${fullSuffixName}`;});终于搞定了,我伸了个懒腰,正兴高采烈的去找女朋友问为了学分,不小心摔倒了,突然爬起来,啊,没事,原来是做梦!不对!据此,闺蜜……我顿时伤心起来,伤心地开了网。对不起!原来这一切都是假的!!!那天晚上,泪湿了我的枕头!!!源码在这里