VScode 插件开发 国际化帮助工具

一、功能实现

javascript 复制代码
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');
const fs = require('fs');
const path = require('path')

const axios = require('axios');
const crypto = require('crypto');
const YOUDAO_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
 */
function activate(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.json
	const 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(new RegExp(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}')}}`
									}
								}else if(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);
											});
										}else if(section == 'javaScript'){
											const start = selection.start;
											const end = selection.end;
											// 获取当前选中内容的前后字符位置
											const newStart = new vscode.Position(start.line, Math.max(start.character - 1, 0));
											const newEnd = new vscode.Position(end.line, Math.min(end.character + 1, document.lineAt(end.line).text.length));
											// 设置新的选择区域
											editor.selection = new vscode.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);
}

// 添加词条到文件
async function addWordToFile(searchText, filePath4) {
	const translatedText = await translateText(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);
}

async function translateText(text, targetLanguage) {
    const appKey = ''; // 替换为您的 App Key
    const appSecret = ''; // 替换为您的 App Secret
    const 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];
}

function truncate(text) {
    return text.length > 200 ? text.slice(0, 200) : text;
}

function toCamelCase(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('');
}

function simplify(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 deactivated
function deactivate() {
	vscode.window.showInformationMessage(`Congratulations, your extension "gn-i18n-helper" is now active!`);
}

module.exports = {
	activate,
	deactivate
}

二、快捷键定义

javascript 复制代码
{
  "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"
  }
}
相关推荐
SongYuLong的博客2 小时前
VScode+ESP-IDF搭建ESP32开发环境
ide·vscode·编辑器
江湖有缘2 小时前
AI编程工具使用技巧:在Visual Studio Code中高效利用阿里云通义灵码
vscode·阿里云·ai编程
witton3 小时前
macOS使用LLVM官方发布的tar.xz来安装Clang编译器
vscode·macos·cmake·clang·llvm·qtcreator·clang++
千航@abc4 小时前
vim如何显示行号
linux·编辑器·vim
tiger13345 小时前
vscode如何安装vue语法支持
ide·vue.js·vscode
占疏17 小时前
pycharm 运行远程环境问题 Error:Failed to prepare environment.
ide·python·pycharm
红虾程序员20 小时前
HTML入门知识
前端·ide·pycharm·html·intellij-idea
shimh_凉茶20 小时前
VS Code i18n国际化组件代码code显示中文配置 i18n ally
前端·vue.js·vscode
Eiceblue1 天前
Python 在Word中添加、或删除超链接
vscode·python·pycharm·c#·word
weixin_421133411 天前
vscode 前端常用插件安装大全
前端·ide·vscode