commit 2c3e29141113bf60ee4c237c05db93b20bd6ce62 Author: tianjianyong <11429339@qq.com> Date: Sun Mar 9 14:36:27 2025 +0800 新建项目,实现基本功能,可使用百度翻译API diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88cb115 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/out +/node_modules \ No newline at end of file diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..ebd0f34 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,141 @@ +# VSCode 中英文变量名翻译插件设计文档 + +## 1. 项目概述 + +### 1.1 项目目标 +开发一个VSCode插件,实现以下核心功能: +- 把选中的中文翻译成英文变量名并替换 +- 支持选中英文文本翻译为中文 +- 提供变量命名规范化处理(支持驼峰命名、帕斯卡命名、下划线命名) + +### 1.2 技术栈选择 +- 开发语言:TypeScript +- 开发框架:VSCode Extension API +- 翻译服务:OpenAI API(提供更准确的上下文翻译) +- 开发工具:VSCode、Node.js + +## 2. 系统架构 + +### 2.1 核心模块 +1. **选中文本翻译模块** + - 获取用户选中的中文文本 + - 处理选中的中文内容 + - 触发翻译流程 + +2. **翻译服务模块** + - 集成Google Translate API 和百度翻译API + - 处理翻译请求 + - 缓存常用翻译结果 + +3. **变量命名处理模块** + - 驼峰命名转换 + - 变量命名规范检查 + - 特殊字符处理 + +4. **用户界面模块** + - 翻译结果展示 + - 快捷命令注册 + - 配置界面 + +### 2.2 数据流 +1. 用户输入/选择文本 +2. 触发翻译事件 +3. 调用翻译服务 +4. 处理翻译结果 +5. 更新编辑器文本 + +## 3. 功能详细设计 + +### 3.1 中文翻译为英文变量名 +- 获取用户选中的中文文本 +- 调用翻译API获取英文翻译 +- 应用变量命名规范 +- 替换原文本 + +### 3.2 选中文本翻译 +- 注册命令`translate.toEnglish`和`translate.toChinese` +- 获取选中文本范围和内容 +- 调用翻译服务 +- 在状态栏或悬浮窗显示翻译结果 +- 提供快速替换选项 + +### 3.3 变量命名规范化 +- 支持驼峰命名(camelCase) +- 支持帕斯卡命名(PascalCase) +- 支持下划线命名(snake_case) +- 自动移除特殊字符 +- 处理多词组合 + +### 3.4 配置选项 +- 翻译服务API配置 +- 默认变量命名风格 +- 快捷键自定义 +- 自动翻译开关 +- 翻译结果展示方式 + +## 4. 性能优化 + +### 4.1 翻译性能 +- 实现本地翻译缓存 +- 批量翻译请求合并 +- 设置翻译频率限制 + +### 4.2 编辑器性能 +- 防抖处理输入事件 +- 异步处理翻译请求 +- 优化文本替换操作 + +## 5. 错误处理 + +### 5.1 翻译服务异常 +- API调用失败重试机制 +- 错误信息友好展示 +- 降级使用备用翻译服务 + +### 5.2 输入异常 +- 非法字符检测 +- 超长输入限制 +- 特殊格式处理 + +## 6. 安全性考虑 + +### 6.1 API密钥管理 +- 安全存储API密钥 +- 加密传输敏感信息 +- 访问权限控制 + +### 6.2 数据安全 +- 本地缓存加密 +- 用户数据保护 +- 隐私政策遵守 + +## 7. 后续优化方向 + +### 7.1 功能扩展 +- 支持更多翻译服务 +- 添加自定义词典 +- 批量翻译功能 +- 翻译历史记录 + +### 7.2 用户体验 +- 智能上下文理解 +- 实时翻译预览 +- 多语言支持 +- 统计分析功能 + +## 8. 开发计划 + +### 8.1 第一阶段 +- 基础框架搭建 +- 核心翻译功能实现 +- 基本UI组件开发 + +### 8.2 第二阶段 +- 性能优化 +- 错误处理完善 +- 配置功能实现 + +### 8.3 第三阶段 +- 功能测试与修复 +- 文档编写 +- 发布与维护 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..67fe0be --- /dev/null +++ b/package-lock.json @@ -0,0 +1,484 @@ +{ + "name": "vscode-translate-plugin", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vscode-translate-plugin", + "version": "0.0.1", + "dependencies": { + "@types/axios": "^0.9.36", + "@vitalets/google-translate-api": "^9.2.0", + "axios": "^1.8.2", + "crypto": "^1.0.1" + }, + "devDependencies": { + "@types/node": "^20.10.5", + "@types/node-fetch": "^2.6.12", + "@types/vscode": "^1.85.0", + "typescript": "^5.3.3" + }, + "engines": { + "vscode": "^1.60.0" + } + }, + "node_modules/@types/axios": { + "version": "0.9.36", + "resolved": "https://registry.npmmirror.com/@types/axios/-/axios-0.9.36.tgz", + "integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-1.8.2.tgz", + "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.17.24", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.17.24.tgz", + "integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.98.0", + "resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.98.0.tgz", + "integrity": "sha512-+KuiWhpbKBaG2egF+51KjbGWatTH5BbmWQjSLMDCssb4xF8FJnW4nGH4nuAdOOfMbpD0QlHtI+C3tPq+DoKElg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitalets/google-translate-api": { + "version": "9.2.1", + "resolved": "https://registry.npmmirror.com/@vitalets/google-translate-api/-/google-translate-api-9.2.1.tgz", + "integrity": "sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "^1.8.2", + "http-errors": "^2.0.0", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.8.2.tgz", + "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "license": "ISC" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ffb6d36 --- /dev/null +++ b/package.json @@ -0,0 +1,109 @@ +{ + "name": "vscode-translate-plugin", + "displayName": "中英文变量名翻译", + "description": "VSCode中英文变量名翻译插件,支持中英互译和变量命名规范化", + "version": "0.0.1", + "publisher": "tianjianyong", + "engines": { + "vscode": "^1.60.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:translate.toEnglish", + "onCommand:translate.toChinese" + ], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "translate.toEnglish", + "title": "翻译为英文变量名" + }, + { + "command": "translate.toChinese", + "title": "翻译为中文" + } + ], + "menus": { + "editor/context": [ + { + "when": "editorHasSelection", + "command": "translate.toEnglish", + "group": "translation" + }, + { + "when": "editorHasSelection", + "command": "translate.toChinese", + "group": "translation" + } + ] + }, + "configuration": { + "title": "中英文变量名翻译", + "properties": { + "translate.namingConvention": { + "type": "string", + "default": "camelCase", + "enum": [ + "camelCase", + "PascalCase", + "snake_case" + ], + "description": "变量命名规范" + }, + "translate.translationService": { + "type": "string", + "default": "google", + "enum": [ + "google", + "baidu" + ], + "description": "翻译服务提供商" + }, + "translate.baiduAppId": { + "type": "string", + "default": "", + "description": "百度翻译API的AppID" + }, + "translate.baiduSecretKey": { + "type": "string", + "default": "", + "description": "百度翻译API的密钥" + } + } + }, + "keybindings": [ + { + "command": "translate.toEnglish", + "key": "ctrl+alt+e", + "mac": "cmd+alt+e", + "when": "editorHasSelection" + }, + { + "command": "translate.toChinese", + "key": "ctrl+alt+c", + "mac": "cmd+alt+c", + "when": "editorHasSelection" + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./" + }, + "devDependencies": { + "@types/node": "^20.10.5", + "@types/node-fetch": "^2.6.12", + "@types/vscode": "^1.85.0", + "typescript": "^5.3.3" + }, + "dependencies": { + "@types/axios": "^0.9.36", + "@vitalets/google-translate-api": "^9.2.0", + "axios": "^1.8.2", + "crypto": "^1.0.1" + } +} diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..714f336 --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,92 @@ +import * as vscode from 'vscode'; +// 从翻译服务模块导入翻译函数 +// 从翻译服务模块导入翻译函数 +import { translateToChinese, translateToEnglish } from './translationService'; +// 导入命名格式化工具函数 +import { formatVariableName } from './namingFormatter'; + +export function activate(context: vscode.ExtensionContext) { + console.log('中英文变量名翻译插件已激活'); + + // 注册翻译为英文变量名命令 + let toEnglishDisposable = vscode.commands.registerCommand('translate.toEnglish', async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + const selection = editor.selection; + if (selection.isEmpty) { + vscode.window.showInformationMessage('请先选择要翻译的文本'); + return; + } + + const text = editor.document.getText(selection); + try { + // 显示状态栏加载信息 + vscode.window.setStatusBarMessage('正在翻译...', 2000); + + // 调用翻译服务 + const translatedText = await translateToEnglish(text); + + // 获取用户配置的命名规范 + const config = vscode.workspace.getConfiguration('translate'); + const namingConvention = config.get('namingConvention', 'camelCase'); + + // 格式化变量名 + const formattedName = formatVariableName(translatedText, namingConvention); + + // 替换选中文本 + editor.edit(editBuilder => { + editBuilder.replace(selection, formattedName); + }); + + vscode.window.setStatusBarMessage(`已翻译: ${formattedName}`, 3000); + } catch (error) { + vscode.window.showErrorMessage(`翻译失败: ${error instanceof Error ? error.message : String(error)}`); + } + }); + + // 注册翻译为中文命令 + let toChineseDisposable = vscode.commands.registerCommand('translate.toChinese', async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + const selection = editor.selection; + if (selection.isEmpty) { + vscode.window.showInformationMessage('请先选择要翻译的文本'); + return; + } + + const text = editor.document.getText(selection); + try { + // 显示状态栏加载信息 + vscode.window.setStatusBarMessage('正在翻译...', 2000); + + // 调用翻译服务 + const translatedText = await translateToChinese(text); + + // 显示翻译结果 + const showResult = await vscode.window.showInformationMessage( + `翻译结果: ${translatedText}`, + '替换选中文本', + '关闭' + ); + + // 如果用户选择替换,则替换选中文本 + if (showResult === '替换选中文本') { + editor.edit(editBuilder => { + editBuilder.replace(selection, translatedText); + }); + } + } catch (error) { + vscode.window.showErrorMessage(`翻译失败: ${error instanceof Error ? error.message : String(error)}`); + } + }); + + context.subscriptions.push(toEnglishDisposable, toChineseDisposable); +} + +export function deactivate() {} \ No newline at end of file diff --git a/src/namingFormatter.ts b/src/namingFormatter.ts new file mode 100644 index 0000000..e5a4ba8 --- /dev/null +++ b/src/namingFormatter.ts @@ -0,0 +1,55 @@ +/** + * 变量命名格式化模块 + * 用于将翻译后的文本转换为符合特定命名规范的变量名 + */ + +/** + * 格式化变量名 + * @param text 要格式化的文本 + * @param convention 命名规范(camelCase、PascalCase、snake_case) + * @returns 格式化后的变量名 + */ +export function formatVariableName(text: string, convention: string): string { + // 清理文本,移除特殊字符和多余空格 + let cleanedText = text + .trim() + .replace(/[^\w\s]/g, ' ') // 将特殊字符替换为空格 + .replace(/\s+/g, ' '); // 将多个空格替换为单个空格 + + // 分割单词 + const words = cleanedText.split(' '); + + // 根据不同命名规范格式化变量名 + switch (convention) { + case 'camelCase': + return words + .map((word, index) => { + if (index === 0) { + return word.toLowerCase(); + } + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }) + .join(''); + + case 'PascalCase': + return words + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(''); + + case 'snake_case': + return words + .map(word => word.toLowerCase()) + .join('_'); + + default: + // 默认使用驼峰命名法 + return words + .map((word, index) => { + if (index === 0) { + return word.toLowerCase(); + } + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }) + .join(''); + } +} \ No newline at end of file diff --git a/src/translationService.ts b/src/translationService.ts new file mode 100644 index 0000000..ef601bd --- /dev/null +++ b/src/translationService.ts @@ -0,0 +1,141 @@ +import { translate } from '@vitalets/google-translate-api'; +import axios from 'axios'; +import * as crypto from 'crypto'; +import * as vscode from 'vscode'; + +// 翻译服务类型 +type TranslationService = 'google' | 'baidu'; + +/** + * 获取当前配置的翻译服务 + * @returns 当前配置的翻译服务类型 + */ +function getTranslationService(): TranslationService { + const config = vscode.workspace.getConfiguration('translate'); + return config.get('translationService', 'google'); +} + +/** + * 获取百度翻译API配置 + * @returns 百度翻译API的appId和密钥 + */ +function getBaiduApiConfig(): { appId: string; secretKey: string } { + const config = vscode.workspace.getConfiguration('translate'); + const appId = config.get('baiduAppId', ''); + const secretKey = config.get('baiduSecretKey', ''); + return { appId, secretKey }; +} + +/** + * 使用百度翻译API进行翻译 + * @param text 要翻译的文本 + * @param from 源语言 + * @param to 目标语言 + * @returns 翻译后的文本 + */ +async function baiduTranslate(text: string, from: string, to: string): Promise { + const { appId, secretKey } = getBaiduApiConfig(); + + if (!appId || !secretKey) { + throw new Error('请先在设置中配置百度翻译API的appId和密钥'); + } + + const salt = Date.now().toString(); + const sign = crypto.createHash('md5').update(appId + text + salt + secretKey).digest('hex'); + + try { + const response = await axios.get('https://api.fanyi.baidu.com/api/trans/vip/translate', { + params: { + q: text, + from, + to, + appid: appId, + salt, + sign + } + }); + + // 定义百度翻译API响应的接口类型 + interface BaiduTranslateResponse { + trans_result?: Array<{ + dst: string; + }>; + } + + const translationData = response.data as BaiduTranslateResponse; + if (translationData.trans_result && translationData.trans_result.length > 0) { + return translationData.trans_result[0].dst; + } else { + throw new Error('翻译结果解析失败'); + } + } catch (error) { + console.error('百度翻译API请求失败:', error); + throw new Error('百度翻译服务出错,请检查API配置或网络连接'); + } +} + +/** + * 使用Google翻译API进行翻译 + * @param text 要翻译的文本 + * @param to 目标语言 + * @returns 翻译后的文本 + */ +async function googleTranslate(text: string, to: string): Promise { + try { + const result = await translate(text, { to }); + return result.text; + } catch (error) { + console.error('Google翻译API请求失败:', error); + throw new Error('Google翻译服务出错,请稍后再试'); + } +} + +/** + * 将中文文本翻译为英文 + * @param text 要翻译的中文文本 + * @returns 翻译后的英文文本 + */ +export async function translateToEnglish(text: string): Promise { + try { + // 检查文本是否包含中文字符 + if (!/[\u4e00-\u9fa5]/.test(text)) { + return text; // 如果不包含中文,直接返回原文本 + } + + const service = getTranslationService(); + + if (service === 'baidu') { + return await baiduTranslate(text, 'zh', 'en'); + } else { + return await googleTranslate(text, 'en'); + } + } catch (error) { + console.error('翻译为英文失败:', error); + throw new Error(`翻译服务出错: ${error instanceof Error ? error.message : '请稍后再试'}`); + } +} + +/** + * 将英文文本翻译为中文 + * @param text 要翻译的英文文本 + * @returns 翻译后的中文文本 + */ +export async function translateToChinese(text: string): Promise { + try { + // 检查文本是否全是中文字符 + if (/^[\u4e00-\u9fa5]+$/.test(text)) { + return text; // 如果全是中文,直接返回原文本 + } + + const service = getTranslationService(); + + if (service === 'baidu') { + return await baiduTranslate(text, 'en', 'zh'); + } else { + return await googleTranslate(text, 'zh-CN'); + } + } catch (error) { + console.error('翻译为中文失败:', error); + throw new Error(`翻译服务出错: ${error instanceof Error ? error.message : '请稍后再试'}`); + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f39597d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": ["ES2020"], + "sourceMap": true, + "rootDir": "src", + "strict": true, + "esModuleInterop": true + }, + "exclude": ["node_modules", ".vscode-test"] +} \ No newline at end of file