// The module 'vscode' contains the VS Code extensibility API// Import the module and reference it with the alias vscode in your code belowconst vscode =require('vscode');const fs =require('fs');const path =require('path')const axios =require('axios');const crypto =require('crypto');constYOUDAO_API_URL='https://openapi.youdao.com/api';const targetLanguage ='zh';// 目标语言,例如中文// This method is called when your extension is activated// Your extension is activated the very first time the command is executed/**
* @param {vscode.ExtensionContext} context
*/functionactivate(context){// Use the console to output diagnostic information (console.log) and errors (console.error)// This line of code will only be executed once when your extension is activated// console.log('Congratulations, your extension "gn-i18n-helper" is now active!');// The command has been defined in the package.json file// Now provide the implementation of the command with registerCommand// The commandId parameter must match the command field in package.jsonconst disposable = vscode.commands.registerCommand('extension.getSelectedText',// 这个名称必须与package.json中的命令名称一致async()=>{const editor = vscode.window.activeTextEditor;if(editor){// 选中的文本const selection = editor.selection;const selectedText = editor.document.getText(selection);// 获取当前工作区的文件夹const workspaceFolders = vscode.workspace.workspaceFolders;// 获取文件内容const filePath1 = path.join(workspaceFolders[0].uri.fsPath,'LANGUAGEI18n/LANGUAGEI18nCommon.md');const filePath2 = path.join(workspaceFolders[0].uri.fsPath,'LANGUAGEI18n/LANGUAGEI18nFrontend.md');const filePath3 = path.join(workspaceFolders[0].uri.fsPath,'LANGUAGEI18n/LANGUAGEI18nPage.md');const filePath4 = path.join(workspaceFolders[0].uri.fsPath,'LANGUAGEI18n/LANGUAGEI18nGeneral.md');const fileContent1 = fs.readFileSync(filePath1,'utf8');const fileContent2 = fs.readFileSync(filePath2,'utf8');const fileContent3 = fs.readFileSync(filePath3,'utf8');const fileContent4 = fs.readFileSync(filePath4,'utf8');let fileContent = fileContent1 + fileContent2 + fileContent3 + fileContent4;if(editor.document.languageId ==='vue'){const document = editor.document;const position = editor.selection.active;// 获取光标位置const lineText = document.lineAt(position.line).text;// 获取当前行文本const currentFileContent = document.getText();// 获取文件内容// 默认 template,防止出现多个 template 标签// 使用正则表达式匹配 <javaScript> 和 </javaScript> 之间的内容let section ='template';const javaScriptRegex =/<script>([\s\S]*?)<\/script>/i;const javaScriptMatch = currentFileContent.match(javaScriptRegex);if(javaScriptMatch && javaScriptMatch[1]){const matchContent = javaScriptMatch[1].trim();// 提取内容并去除多余空格if(matchContent.includes(lineText)){
section ='javaScript';}}// 处理选中的内容,截取需要搜索的文字let searchText ='';if(selectedText.includes('=')){
searchText = selectedText.split('=')[1].replace(/"/g,"").trim();}else{
searchText = selectedText;}const searchResult = fileContent.search(newRegExp(searchText,'g'));let isMatch =false;if(searchResult !==-1){// 截取文件内容 - 提高搜索效率// let copyFileContent = fileContent.slice(searchResult - 1000, searchResult + 1000);const lines = fileContent.split('\n');// 按行分割文本const regex =/(\w+)\("([^"]+)", "([^"]+)", "([^"]+)",/;for(const line of lines){const match = line.match(regex);if(match){const docKey = match[2];const docValue = match[3];const docText = match[4].replace(/"/g,"");let newText ='';if(section =='template'){if(selectedText.includes('=')){// placeholder="验证码" -> :placeholder="$t('common.verifyCode')"const prop = selectedText.split('=')[0];
newText =`:${prop}="$t('${docKey}.${docValue}')"`}else{// 验证码 -> {{$t('common.username')}}
newText =`{{$t('${docKey}.${docValue}')}}`}}elseif(section =='javaScript'){// "验证码" -> this.$t('common.username')
newText =`this.$t('${docKey}.${docValue}')`}if(docText == searchText){
isMatch =true;if(docKey && docValue){
vscode.window.showInformationMessage(editor.selection, newText);if(section =='template'){
editor.edit(editBuilder=>{
editBuilder.replace(editor.selection, newText);});}elseif(section =='javaScript'){const start = selection.start;const end = selection.end;// 获取当前选中内容的前后字符位置const newStart =newvscode.Position(start.line, Math.max(start.character -1,0));const newEnd =newvscode.Position(end.line, Math.min(end.character +1, document.lineAt(end.line).text.length));// 设置新的选择区域
editor.selection =newvscode.Selection(newStart, newEnd);
editor.edit(editBuilder=>{
editBuilder.replace(editor.selection, newText);// 替换选中的内容});}break;// 找到后退出循环}}}}// 如果没有找到精准匹配的内容if(!isMatch){addWordToFile(searchText, filePath4);// vscode.window.showInformationMessage(`未找到匹配内容:${searchText}`);}}else{addWordToFile(searchText, filePath4);}}}else{
vscode.window.showInformationMessage('No active editor found.');}},);
context.subscriptions.push(disposable);}// 添加词条到文件asyncfunctionaddWordToFile(searchText, filePath4){const translatedText =awaittranslateText(searchText, targetLanguage);const simplifyText =simplify(translatedText);const camelCaseText =toCamelCase(simplifyText);// 向文件中添加翻译后的文本const generationFilePath = filePath4;const generationFileContent = fs.readFileSync(generationFilePath,'utf8');// GENERAL_UPDATELOG("general", "updateLog", "更新日志", "updateLog", "更新日志"),const insertText =`GENERAL_${camelCaseText.toUpperCase()}("general", "${camelCaseText}", "${searchText}", "${simplifyText}", "${searchText}"),`;const newGenerationFileContent = generationFileContent +'\n'+ insertText;
fs.writeFileSync(generationFilePath, newGenerationFileContent);}asyncfunctiontranslateText(text, targetLanguage){const appKey ='';// 替换为您的 App Keyconst appSecret ='';// 替换为您的 App Secretconst salt = Date.now();const curtime = Math.floor(Date.now()/1000);const sign = crypto.createHash('sha256').update(appKey +truncate(text)+ salt + curtime + appSecret).digest('hex');const response =await axios.get(YOUDAO_API_URL,{params:{q: text,from:'zh-CHS',to: targetLanguage,appKey: appKey,salt: salt,sign: sign,signType:"v3",curtime: curtime,},});return response.data.translation[0];}functiontruncate(text){return text.length >200? text.slice(0,200): text;}functiontoCamelCase(str){// 将 - 替换为 " "
str = str.replace(/-/g," ");return str
.split(/\s+/)// 按空格分割.map((word, index)=>{if(index ===0){return word.toLowerCase();// 第一个单词小写}return word.charAt(0).toUpperCase()+ word.slice(1).toLowerCase();// 其他单词首字母大写}).join('');}functionsimplify(text){// 示例: 删除多余空格和停用词const stopWords =['the','is','in','at','and','of','to','a','that','it','on','for'];const words = text.split(' ').filter(word=>!stopWords.includes(word.toLowerCase()));return words.join(' ').trim();// 返回精简后的文本}// This method is called when your extension is deactivatedfunctiondeactivate(){
vscode.window.showInformationMessage(`Congratulations, your extension "gn-i18n-helper" is now active!`);}
module.exports ={
activate,
deactivate
}
二、快捷键定义
{"name":"gn-i18n-helper","displayName":"gn-i18n-helper","description":"A VS Code extension to help with i18n.","publisher":"guniao","version":"2.0.2","icon":"resources/i18n.png","engines":{"vscode":"^1.96.0"},"categories":["Other"],"activationEvents":[],"main":"./extension.js","contributes":{"commands":[{"command":"extension.getSelectedText","title":"Get Selected Text"}],"keybindings":[{"command":"extension.getSelectedText","key":"alt+r","when":"editorTextFocus"}]},"scripts":{"lint":"eslint .","pretest":"npm run lint","test":"vscode-test"},"devDependencies":{"@types/mocha":"^10.0.10","@types/node":"20.x","@types/vscode":"^1.96.0","@vscode/test-cli":"^0.0.10","@vscode/test-electron":"^2.4.1","eslint":"^9.16.0"},"dependencies":{"axios":"^1.7.9"}}