diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e4a009..6d56f78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,4 +35,4 @@ jobs: - name: Test package creation run: | npm install -g @vscode/vsce - vsce package --allow-missing-repository \ No newline at end of file + vsce package --allow-missing-repository diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json new file mode 100644 index 0000000..1931a8f --- /dev/null +++ b/l10n/bundle.l10n.json @@ -0,0 +1,114 @@ +{ + "API request failed: {0}": "API request failed: {0}", + "Authentication failed: {0}. Please check if the API Token is correct.": "Authentication failed: {0}. Please check if the API Token is correct.", + "Network connection error: {0}": "Network connection error: {0}", + "Input validation failed: {0}": "Input validation failed: {0}", + "Unknown error: {0}": "Unknown error: {0}", + "$(warning) Fetch failed": "$(warning) Fetch failed", + "Budget data fetch failed, click to retry": "Budget data fetch failed, click to retry", + "$(loading~spin) Fetching data...": "$(loading~spin) Fetching data...", + "Fetching budget data, please wait": "Fetching budget data, please wait", + "$(key) Token configuration needed": "$(key) Token configuration needed", + "Click to configure API Token": "Click to configure API Token", + "$(warning) No budget data": "$(warning) No budget data", + "Click to get budget data": "Click to get budget data", + "Daily Budget: {0}%": "Daily Budget: {0}%", + "Daily budget usage rate: {0}%\nUsed: ${1} / ${2}": "Daily budget usage rate: {0}%\nUsed: ${1} / ${2}", + "$(loading~spin) Initializing...": "$(loading~spin) Initializing...", + "Initializing budget data": "Initializing budget data", + "API Token has expired, please reconfigure": "API Token has expired, please reconfigure", + "Configure Now": "Configure Now", + "Data sync failed": "Data sync failed", + "Configuration Summary": "Configuration Summary", + "Endpoint": "Endpoint", + "Polling Interval": "Polling Interval", + "Enable Polling": "Enable Polling", + "Status Bar Refresh": "Status Bar Refresh", + "Status": "Status", + "Configured": "Configured", + "Not Configured": "Not Configured", + "Invalid API endpoint address": "Invalid API endpoint address", + "Polling interval cannot be less than 5 seconds": "Polling interval cannot be less than 5 seconds", + "Status bar refresh interval cannot be less than 100 milliseconds": "Status bar refresh interval cannot be less than 100 milliseconds", + "Authentication failed ({0}): {1}": "Authentication failed ({0}): {1}", + "API request failed ({0}): {1}": "API request failed ({0}): {1}", + "Request timeout": "Request timeout", + "Network connection failed": "Network connection failed", + "Today's budget usage": "Today's budget usage", + "Today's budget usage percentage": "Today's budget usage percentage", + "Today's total budget amount": "Today's total budget amount", + "Today's used budget amount": "Today's used budget amount", + "This month's budget usage": "This month's budget usage", + "This month's budget usage percentage": "This month's budget usage percentage", + "This month's total budget amount": "This month's total budget amount", + "This month's used budget amount": "This month's used budget amount", + "Click refresh to get the latest budget data": "Click refresh to get the latest budget data", + "API Token needs to be configured to get budget data": "API Token needs to be configured to get budget data", + "⚠️ Token not configured": "⚠️ Token not configured", + "Click to set API Token": "Click to set API Token", + "📊 Click refresh to get budget data": "📊 Click refresh to get budget data", + "🔧 Configuration": "🔧 Configuration", + "Daily Budget": "Daily Budget", + "Monthly Budget": "Monthly Budget", + "Used: ${0}": "Used: ${0}", + "Total Budget: ${0}": "Total Budget: ${0}", + "Usage Rate: {0}%": "Usage Rate: {0}%", + "🚀 Packy Usage Extension activated successfully": "🚀 Packy Usage Extension activated successfully", + "❌ Packy Usage Extension activation failed:": "❌ Packy Usage Extension activation failed:", + "Extension activation failed: {0}": "Extension activation failed: {0}", + "🔄 Packy Usage Extension deactivated": "🔄 Packy Usage Extension deactivated", + "Extension not initialized": "Extension not initialized", + "Running": "Running", + "Stopped": "Stopped", + "Data Loaded": "Data Loaded", + "Yes": "Yes", + "No": "No", + "Polling Status": "Polling Status", + "Configuration Status": "Configuration Status", + "Token Configured": "Token Configured", + "Token Not Configured": "Token Not Configured", + "Error during cleanup:": "Error during cleanup:", + "Error cleaning up service:": "Error cleaning up service:", + "📝 Configuration updated:": "📝 Configuration updated:", + "Please configure API Token first": "Please configure API Token first", + "Data fetch failed": "Data fetch failed", + "API Token for budget data access": "API Token for budget data access", + "Enter your API Token": "Enter your API Token", + "will expire at {0}": "will expire at {0}", + "API Token saved successfully{0}!": "API Token saved successfully{0}!", + "Failed to save Token: {0}": "Failed to save Token: {0}", + "You need to configure an API Token to get budget data": "You need to configure an API Token to get budget data", + "Configure Later": "Configure Later", + "You can configure it later by searching for \"Set API Token\" in the command palette.": "You can configure it later by searching for \"Set API Token\" in the command palette.", + "Token cannot be empty": "Token cannot be empty", + "Token seems too short": "Token seems too short", + "Invalid token format, please ensure it's a valid JWT Token": "Invalid token format, please ensure it's a valid JWT Token", + "Token has expired (expiration: {0})": "Token has expired (expiration: {0})", + "Invalid token format, cannot parse": "Invalid token format, cannot parse", + "Please select Token acquisition method": "Please select Token acquisition method", + "View acquisition instructions first": "View acquisition instructions first", + "Enter Token": "Enter Token", + "Permanently valid access token": "Permanently valid access token", + "Get API Token starting with 'sk-' directly from PackyCode Dashboard": "Get API Token starting with 'sk-' directly from PackyCode Dashboard", + "API Token (Recommended)": "API Token (Recommended)", + "Temporary token from PackyCode Dashboard": "Temporary token from PackyCode Dashboard", + "Visit PackyCode Dashboard, open browser developer tools (F12), find the cookie named 'token' in Application/Storage > Cookies": "Visit PackyCode Dashboard, open browser developer tools (F12), find the cookie named 'token' in Application/Storage > Cookies", + "JWT Token": "JWT Token", + "Select token type to view detailed instructions": "Select token type to view detailed instructions", + "Token acquisition instructions": "Token acquisition instructions", + "Enter API Token (sk-) or JWT Token": "Enter API Token (sk-) or JWT Token", + "Recommended: Use permanent API Token (sk-). JWT Token can be obtained from PackyCode Dashboard cookies": "Recommended: Use permanent API Token (sk-). JWT Token can be obtained from PackyCode Dashboard cookies", + "API Token saved successfully! This Token is permanently valid.": "API Token saved successfully! This Token is permanently valid.", + "JWT Token saved successfully{0}!": "JWT Token saved successfully{0}!", + "You need to configure an access token to get budget data": "You need to configure an access token to get budget data", + "View Help": "View Help", + "### Token Acquisition Methods\n\n**API Token (Recommended)**\n- Permanently valid access token\n- Get API Token starting with 'sk-' directly from PackyCode Dashboard\n\n**JWT Token**\n- Temporary token from PackyCode Dashboard\n- Acquisition steps:\n 1. Visit PackyCode Dashboard\n 2. Open browser developer tools (press F12 or right-click and select \"Inspect\")\n 3. Switch to \"Application\" or \"Storage\" tab\n 4. Find \"Cookies\" in the left panel and expand it\n 5. Select the current website domain\n 6. Find the cookie named \"token\" in the right list\n 7. Copy its value as your JWT Token": "### Token Acquisition Methods\n\n**API Token (Recommended)**\n- Permanently valid access token\n- Get API Token starting with 'sk-' directly from PackyCode Dashboard\n\n**JWT Token**\n- Temporary token from PackyCode Dashboard\n- Acquisition steps:\n 1. Visit PackyCode Dashboard\n 2. Open browser developer tools (press F12 or right-click and select \"Inspect\")\n 3. Switch to \"Application\" or \"Storage\" tab\n 4. Find \"Cookies\" in the left panel and expand it\n 5. Select the current website domain\n 6. Find the cookie named \"token\" in the right list\n 7. Copy its value as your JWT Token", + "JWT Token has expired (expiration: {0})": "JWT Token has expired (expiration: {0})", + "Invalid JWT Token format": "Invalid JWT Token format", + "Unrecognized Token format, please provide API Token (sk-) or JWT Token": "Unrecognized Token format, please provide API Token (sk-) or JWT Token", + "Token has expired, please reconfigure": "Token has expired, please reconfigure", + "Token: ": "Token: ", + "Configured (API Token)": "Configured (API Token)", + "Configured (JWT Token)": "Configured (JWT Token)", + "Set Token": "Set Token" +} \ No newline at end of file diff --git a/l10n/bundle.l10n.zh-CN.json b/l10n/bundle.l10n.zh-CN.json new file mode 100644 index 0000000..a972960 --- /dev/null +++ b/l10n/bundle.l10n.zh-CN.json @@ -0,0 +1,114 @@ +{ + "API request failed: {0}": "API请求失败: {0}", + "Authentication failed: {0}. Please check if the API Token is correct.": "认证失败: {0}。请检查API Token是否正确。", + "Network connection error: {0}": "网络连接错误: {0}", + "Input validation failed: {0}": "输入验证失败: {0}", + "Unknown error: {0}": "未知错误: {0}", + "$(warning) Fetch failed": "$(warning) 获取失败", + "Budget data fetch failed, click to retry": "预算数据获取失败,点击重试", + "$(loading~spin) Fetching data...": "$(loading~spin) 获取数据中...", + "Fetching budget data, please wait": "正在获取预算数据,请稍候", + "$(key) Token configuration needed": "$(key) 需要配置Token", + "Click to configure API Token": "点击配置API Token", + "$(warning) No budget data": "$(warning) 未获取预算数据", + "Click to get budget data": "点击获取预算数据", + "Daily Budget: {0}%": "日预算: {0}%", + "Daily budget usage rate: {0}%\nUsed: ${1} / ${2}": "日预算使用率: {0}%\n已用: ${1} / ${2}", + "$(loading~spin) Initializing...": "$(loading~spin) 初始化中...", + "Initializing budget data": "正在初始化预算数据", + "API Token has expired, please reconfigure": "API Token已过期,请重新配置", + "Configure Now": "立即配置", + "Data sync failed": "数据同步失败", + "Configuration Summary": "配置摘要", + "Endpoint": "端点", + "Polling Interval": "轮询间隔", + "Enable Polling": "启用轮询", + "Status Bar Refresh": "状态栏刷新", + "Status": "状态", + "Configured": "已配置", + "Not Configured": "未配置", + "Invalid API endpoint address": "API 端点地址无效", + "Polling interval cannot be less than 5 seconds": "轮询间隔不能少于5秒", + "Status bar refresh interval cannot be less than 100 milliseconds": "状态栏刷新间隔不能少于100毫秒", + "Authentication failed ({0}): {1}": "认证失败 ({0}): {1}", + "API request failed ({0}): {1}": "API请求失败 ({0}): {1}", + "Request timeout": "请求超时", + "Network connection failed": "网络连接失败", + "Today's budget usage": "今日预算使用情况", + "Today's budget usage percentage": "今日预算使用百分比", + "Today's total budget amount": "今日总预算金额", + "Today's used budget amount": "今日已使用的预算金额", + "This month's budget usage": "本月预算使用情况", + "This month's budget usage percentage": "本月预算使用百分比", + "This month's total budget amount": "本月总预算金额", + "This month's used budget amount": "本月已使用的预算金额", + "Click refresh to get the latest budget data": "点击刷新获取最新预算数据", + "API Token needs to be configured to get budget data": "需要配置API Token才能获取预算数据", + "⚠️ Token not configured": "⚠️ 未配置 Token", + "Click to set API Token": "点击设置 API Token", + "📊 Click refresh to get budget data": "📊 点击刷新获取预算数据", + "🔧 Configuration": "🔧 配置", + "Daily Budget": "日预算", + "Monthly Budget": "月预算", + "Used: ${0}": "已使用: ${0}", + "Total Budget: ${0}": "总预算: ${0}", + "Usage Rate: {0}%": "使用率: {0}%", + "🚀 Packy Usage Extension activated successfully": "🚀 Packy Usage Extension 激活成功", + "❌ Packy Usage Extension activation failed:": "❌ Packy Usage Extension 激活失败:", + "Extension activation failed: {0}": "插件激活失败: {0}", + "🔄 Packy Usage Extension deactivated": "🔄 Packy Usage Extension 已停用", + "Extension not initialized": "扩展未初始化", + "Running": "运行中", + "Stopped": "已停止", + "Data Loaded": "数据已加载", + "Yes": "是", + "No": "否", + "Polling Status": "轮询状态", + "Configuration Status": "配置状态", + "Token Configured": "Token已配置", + "Token Not Configured": "Token未配置", + "Error during cleanup:": "清理资源时出错:", + "Error cleaning up service:": "清理服务时出错:", + "📝 Configuration updated:": "📝 配置已更新:", + "Please configure API Token first": "请先配置 API Token", + "Data fetch failed": "数据获取失败", + "API Token for budget data access": "用于访问预算数据的 API Token", + "Enter your API Token": "输入您的 API Token", + "will expire at {0}": ",将于 {0} 过期", + "API Token saved successfully{0}!": "API Token保存成功{0}!", + "Failed to save Token: {0}": "保存Token失败: {0}", + "You need to configure an API Token to get budget data": "需要配置 API Token 来获取预算数据", + "Configure Later": "稍后配置", + "You can configure it later by searching for \"Set API Token\" in the command palette.": "您可以随时通过命令面板搜索 \"Set API Token\" 来配置。", + "Token cannot be empty": "Token不能为空", + "Token seems too short": "Token似乎太短", + "Invalid token format, please ensure it's a valid JWT Token": "Token格式无效,请确保是有效的JWT Token", + "Token has expired (expiration: {0})": "Token已过期(过期时间: {0})", + "Invalid token format, cannot parse": "Token格式无效,无法解析", + "Please select Token acquisition method": "请选择Token获取方式", + "View acquisition instructions first": "先查看获取说明", + "Enter Token": "输入Token", + "Permanently valid access token": "永久有效的访问令牌", + "Get API Token starting with 'sk-' directly from PackyCode Dashboard": "到PackyCode Dashboard直接获取以'sk-'开头的API Token", + "API Token (Recommended)": "API Token (推荐)", + "Temporary token from PackyCode Dashboard": "从PackyCode Dashboard获取的临时令牌", + "Visit PackyCode Dashboard, open browser developer tools (F12), find the cookie named 'token' in Application/Storage > Cookies": "访问PackyCode Dashboard,打开浏览器开发者工具(F12),在Application/Storage > Cookies中找到名为'token'的Cookie值", + "JWT Token": "JWT Token", + "Select token type to view detailed instructions": "选择Token类型查看详细说明", + "Token acquisition instructions": "Token获取说明", + "Enter API Token (sk-) or JWT Token": "输入API Token (sk-开头) 或 JWT Token", + "Recommended: Use permanent API Token (sk-). JWT Token can be obtained from PackyCode Dashboard cookies": "推荐使用永久有效的API Token (sk-开头)。JWT Token可从PackyCode Dashboard的Cookie中获取", + "API Token saved successfully! This Token is permanently valid.": "API Token保存成功!该Token永久有效。", + "JWT Token saved successfully{0}!": "JWT Token保存成功{0}!", + "You need to configure an access token to get budget data": "需要配置访问令牌来获取预算数据", + "View Help": "查看帮助", + "### Token Acquisition Methods\n\n**API Token (Recommended)**\n- Permanently valid access token\n- Get API Token starting with 'sk-' directly from PackyCode Dashboard\n\n**JWT Token**\n- Temporary token from PackyCode Dashboard\n- Acquisition steps:\n 1. Visit PackyCode Dashboard\n 2. Open browser developer tools (press F12 or right-click and select \"Inspect\")\n 3. Switch to \"Application\" or \"Storage\" tab\n 4. Find \"Cookies\" in the left panel and expand it\n 5. Select the current website domain\n 6. Find the cookie named \"token\" in the right list\n 7. Copy its value as your JWT Token": "### Token获取方式\n\n**API Token (推荐)**\n- 永久有效的访问令牌\n- 到PackyCode Dashboard直接获取以'sk-'开头的API Token\n\n**JWT Token**\n- 从PackyCode Dashboard获取的临时令牌\n- 获取步骤:\n 1. 访问PackyCode Dashboard\n 2. 打开浏览器开发者工具(按F12或右键选择"检查")\n 3. 切换到"Application"或"Storage"选项卡\n 4. 在左侧找到"Cookies"并展开\n 5. 选择当前网站域名\n 6. 在右侧列表中找到名为"token"的Cookie\n 7. 复制其值作为您的JWT Token", + "JWT Token has expired (expiration: {0})": "JWT Token已过期(过期时间: {0})", + "Invalid JWT Token format": "JWT Token格式无效", + "Unrecognized Token format, please provide API Token (sk-) or JWT Token": "无法识别的Token格式,请提供API Token (sk-开头) 或 JWT Token", + "Token has expired, please reconfigure": "Token已过期,请重新配置", + "Token: ": "Token: ", + "Configured (API Token)": "已配置 (API Token)", + "Configured (JWT Token)": "已配置 (JWT Token)", + "Set Token": "设置 Token" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 970a17a..65cef33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.1", "license": "MIT", "dependencies": { - "undici": "^6.19.8" + "undici": "^6.21.3" }, "devDependencies": { "@eslint/js": "^9.30.1", @@ -18,6 +18,7 @@ "@types/vscode": "^1.54.0", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", + "@vscode/l10n-dev": "^0.0.35", "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.6.0", @@ -49,6 +50,41 @@ "@azu/format-text": "^1.0.1" } }, + "node_modules/@azure-rest/ai-translation-text": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure-rest/ai-translation-text/-/ai-translation-text-1.0.1.tgz", + "integrity": "sha512-lUs1FfBXjik6EReUEYP1ogkhaSPHZdUV+EB215y7uejuyHgG1RXD2aLsqXQrluZwXcLMdN+bTzxylKBc5xDhgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.3.1", + "@azure/core-auth": "^1.9.0", + "@azure/core-rest-pipeline": "^1.18.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure-rest/core-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.0.tgz", + "integrity": "sha512-KMVIPxG6ygcQ1M2hKHahF7eddKejYsWTjoLIfTWiqnaj42dBkYzj4+S8rK9xxmlOaEHKZHcMrRbm0NfN4kgwHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-rest-pipeline": "^1.5.0", + "@azure/core-tracing": "^1.0.1", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", @@ -1292,6 +1328,28 @@ "node": ">=20.0.0" } }, + "node_modules/@vscode/l10n-dev": { + "version": "0.0.35", + "resolved": "https://registry.npmjs.org/@vscode/l10n-dev/-/l10n-dev-0.0.35.tgz", + "integrity": "sha512-s6uzBXsVDSL69Z85HSqpc5dfKswQkeucY8L00t1TWzGalw7wkLQUKMRwuzqTq+AMwQKrRd7Po14cMoTcd11iDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure-rest/ai-translation-text": "^1.0.0-beta.1", + "debug": "^4.3.4", + "deepmerge-json": "^1.5.0", + "glob": "^10.0.0", + "markdown-it": "^14.0.0", + "node-html-markdown": "^1.3.0", + "pseudo-localization": "^2.4.0", + "web-tree-sitter": "^0.20.8", + "xml2js": "^0.5.0", + "yargs": "^17.7.1" + }, + "bin": { + "vscode-l10n-dev": "dist/cli.js" + } + }, "node_modules/@vscode/test-cli": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.11.tgz", @@ -1399,34 +1457,6 @@ "@vscode/vsce-sign-win32-x64": "2.0.5" } }, - "node_modules/@vscode/vsce-sign-alpine-arm64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.5.tgz", - "integrity": "sha512-XVmnF40APwRPXSLYA28Ye+qWxB25KhSVpF2eZVtVOs6g7fkpOxsVnpRU1Bz2xG4ySI79IRuapDJoAQFkoOgfdQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "alpine" - ] - }, - "node_modules/@vscode/vsce-sign-alpine-x64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.5.tgz", - "integrity": "sha512-JuxY3xcquRsOezKq6PEHwCgd1rh1GnhyH6urVEWUzWn1c1PC4EOoyffMD+zLZtFuZF5qR1I0+cqDRNKyPvpK7Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "alpine" - ] - }, "node_modules/@vscode/vsce-sign-darwin-arm64": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.5.tgz", @@ -1441,90 +1471,6 @@ "darwin" ] }, - "node_modules/@vscode/vsce-sign-darwin-x64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.5.tgz", - "integrity": "sha512-ma9JDC7FJ16SuPXlLKkvOD2qLsmW/cKfqK4zzM2iJE1PbckF3BlR08lYqHV89gmuoTpYB55+z8Y5Fz4wEJBVDA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@vscode/vsce-sign-linux-arm": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.5.tgz", - "integrity": "sha512-cdCwtLGmvC1QVrkIsyzv01+o9eR+wodMJUZ9Ak3owhcGxPRB53/WvrDHAFYA6i8Oy232nuen1YqWeEohqBuSzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@vscode/vsce-sign-linux-arm64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.5.tgz", - "integrity": "sha512-Hr1o0veBymg9SmkCqYnfaiUnes5YK6k/lKFA5MhNmiEN5fNqxyPUCdRZMFs3Ajtx2OFW4q3KuYVRwGA7jdLo7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@vscode/vsce-sign-linux-x64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.5.tgz", - "integrity": "sha512-XLT0gfGMcxk6CMRLDkgqEPTyG8Oa0OFe1tPv2RVbphSOjFWJwZgK3TYWx39i/7gqpDHlax0AP6cgMygNJrA6zg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@vscode/vsce-sign-win32-arm64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.5.tgz", - "integrity": "sha512-hco8eaoTcvtmuPhavyCZhrk5QIcLiyAUhEso87ApAWDllG7djIrWiOCtqn48k4pHz+L8oCQlE0nwNHfcYcxOPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@vscode/vsce-sign-win32-x64": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.5.tgz", - "integrity": "sha512-1ixKFGM2FwM+6kQS2ojfY3aAelICxjiCzeg4nTHpkeU1Tfs4RC+lVLrgq5NwcBC7ZLr6UfY3Ct3D6suPeOf7BQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@vscode/vsce/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2436,6 +2382,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge-json": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/deepmerge-json/-/deepmerge-json-1.5.0.tgz", + "integrity": "sha512-jZRrDmBKjmGcqMFEUJ14FjMJwm05Qaked+1vxaALRtF0UAl7lPU8OLWXFxvoeg3jbQM249VPFVn8g2znaQkEtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/default-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", @@ -3324,21 +3280,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3411,6 +3352,16 @@ "node": ">= 0.4" } }, + "node_modules/get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -4689,6 +4640,30 @@ "license": "MIT", "optional": true }, + "node_modules/node-html-markdown": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", + "integrity": "sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-html-parser": "^6.1.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/node-html-parser": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", + "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-sarif-builder": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", @@ -5284,6 +5259,36 @@ "dev": true, "license": "MIT" }, + "node_modules/pseudo-localization": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pseudo-localization/-/pseudo-localization-2.4.0.tgz", + "integrity": "sha512-ISYMOKY8+f+PmiXMFw2y6KLY74LBrv/8ml/VjjoVEV2k+MS+OJZz7ydciK5ntJwxPrKQPTU1+oXq9Mx2b0zEzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat": "^5.0.2", + "get-stdin": "^7.0.0", + "typescript": "^4.7.4", + "yargs": "^17.2.1" + }, + "bin": { + "pseudo-localization": "bin/pseudo-localize" + } + }, + "node_modules/pseudo-localization/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -6620,6 +6625,13 @@ "url": "https://bevry.me/fund" } }, + "node_modules/web-tree-sitter": { + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.20.8.tgz", + "integrity": "sha512-weOVgZ3aAARgdnb220GqYuh7+rZU0Ka9k9yfKtGAzEYMa6GgiCzW9JjQRJyCJakvibQW+dfjJdihjInKuuCAUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", diff --git a/package.json b/package.json index 4dfdb36..f4036cf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "packy-usage", - "displayName": "Packy Usage", - "description": "实时监控API预算使用情况,跟踪每日和每月的API消费", + "displayName": "%displayName%", + "description": "%description%", "version": "1.1.1", "publisher": "MashiroWang", "pricing": "Free", @@ -34,6 +34,7 @@ "engines": { "vscode": "^1.54.0" }, + "l10n": "./l10n", "categories": [ "Other", "Visualization" @@ -46,40 +47,40 @@ "commands": [ { "command": "packy-usage.setToken", - "title": "Set API Token", + "title": "%command.setToken.title%", "category": "Packy Usage" }, { "command": "packy-usage.refresh", - "title": "Refresh Budget Data", + "title": "%command.refresh.title%", "category": "Packy Usage", "icon": "$(refresh)" }, { "command": "packy-usage.showExplorer", - "title": "Show Usage Explorer", + "title": "%command.showExplorer.title%", "category": "Packy Usage" } ], "configuration": { - "title": "Packy Usage", + "title": "%config.title%", "properties": { "packy-usage.apiToken": { "type": "string", "default": "", - "description": "API Token for budget data access", + "description": "%config.apiToken.description%", "scope": "application" }, "packy-usage.apiEndpoint": { "type": "string", "default": "https://www.packycode.com/api/backend/users/info", - "description": "API endpoint for budget data", + "description": "%config.apiEndpoint.description%", "scope": "application" }, "packy-usage.proxy": { "type": "string", "default": "", - "description": "要使用的代理设置。如果未设置,则将从http_proxy和https_proxy环境变量继承", + "description": "%config.proxy.description%", "scope": "application" } } @@ -88,7 +89,7 @@ "activitybar": [ { "id": "packy-usage", - "title": "Packy Usage", + "title": "%view.container.title%", "icon": "$(package)" } ] @@ -97,7 +98,7 @@ "packy-usage": [ { "id": "packy-usage.explorer", - "name": "Usage Explorer", + "name": "%view.explorer.name%", "when": "true" } ] @@ -120,7 +121,7 @@ "lint": "eslint src", "lint:fix": "eslint src --fix", "test": "vscode-test", - "package": "vsce package --allow-missing-repository" + "package": "npm run vsce -- package --allow-missing-repository" }, "devDependencies": { "@eslint/js": "^9.30.1", @@ -129,6 +130,7 @@ "@types/vscode": "^1.54.0", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", + "@vscode/l10n-dev": "^0.0.35", "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.6.0", @@ -140,6 +142,6 @@ "typescript-eslint": "^8.36.0" }, "dependencies": { - "undici": "^6.19.8" + "undici": "^6.21.3" } } diff --git a/package.nls.json b/package.nls.json new file mode 100644 index 0000000..af4d013 --- /dev/null +++ b/package.nls.json @@ -0,0 +1,13 @@ +{ + "displayName": "Packy Usage", + "description": "Monitor API budget usage in real-time, track daily and monthly API consumption", + "command.setToken.title": "Set API Token", + "command.refresh.title": "Refresh Budget Data", + "command.showExplorer.title": "Show Usage Explorer", + "config.title": "Packy Usage", + "config.apiToken.description": "API Token for budget data access", + "config.apiEndpoint.description": "API endpoint for budget data", + "config.proxy.description": "Proxy settings to use. If not set, will inherit from http_proxy and https_proxy environment variables", + "view.container.title": "Packy Usage", + "view.explorer.name": "Usage Explorer" +} \ No newline at end of file diff --git a/package.nls.zh-CN.json b/package.nls.zh-CN.json new file mode 100644 index 0000000..bb0b86b --- /dev/null +++ b/package.nls.zh-CN.json @@ -0,0 +1,13 @@ +{ + "displayName": "Packy Usage", + "description": "实时监控API预算使用情况,跟踪每日和每月的API消费", + "command.setToken.title": "设置 API Token", + "command.refresh.title": "刷新预算数据", + "command.showExplorer.title": "显示使用情况浏览器", + "config.title": "Packy Usage", + "config.apiToken.description": "用于访问预算数据的 API Token", + "config.apiEndpoint.description": "预算数据的 API 端点", + "config.proxy.description": "要使用的代理设置。如果未设置,则将从http_proxy和https_proxy环境变量继承", + "view.container.title": "Packy Usage", + "view.explorer.name": "使用情况浏览器" +} \ No newline at end of file diff --git a/src/controllers/command.controller.ts b/src/controllers/command.controller.ts index 3760e11..ef4c18c 100644 --- a/src/controllers/command.controller.ts +++ b/src/controllers/command.controller.ts @@ -70,11 +70,13 @@ export class CommandController { this.dataService.updateData(data) } else { this.statusBarService.showNoTokenStatus() - vscode.window.showWarningMessage("Please configure API Token first") + vscode.window.showWarningMessage( + vscode.l10n.t("Please configure API Token first") + ) } } catch (error) { ErrorHandler.handle(error as Error) - this.statusBarService.showErrorStatus("数据获取失败") + this.statusBarService.showErrorStatus(vscode.l10n.t("Data fetch failed")) } } @@ -91,33 +93,40 @@ export class CommandController { private async handleSetToken(): Promise { // 显示获取Token的帮助信息 const helpChoice = await vscode.window.showInformationMessage( - "请选择Token获取方式", - "先查看获取说明", - "输入Token" + vscode.l10n.t("Please select Token acquisition method"), + vscode.l10n.t("View acquisition instructions first"), + vscode.l10n.t("Enter Token") ) if (!helpChoice) { return } - if (helpChoice === "先查看获取说明") { + if (helpChoice === vscode.l10n.t("View acquisition instructions first")) { const tokenType = await vscode.window.showQuickPick( [ { - description: "永久有效的访问令牌", - detail: "到PackyCode Dashboard直接获取以'sk-'开头的API Token", - label: "API Token (推荐)" + description: vscode.l10n.t("Permanently valid access token"), + detail: vscode.l10n.t( + "Get API Token starting with 'sk-' directly from PackyCode Dashboard" + ), + label: vscode.l10n.t("API Token (Recommended)") }, { - description: "从PackyCode Dashboard获取的临时令牌", - detail: - "访问PackyCode Dashboard,打开浏览器开发者工具(F12),在Application/Storage > Cookies中找到名为'token'的Cookie值", - label: "JWT Token" + description: vscode.l10n.t( + "Temporary token from PackyCode Dashboard" + ), + detail: vscode.l10n.t( + "Visit PackyCode Dashboard, open browser developer tools (F12), find the cookie named 'token' in Application/Storage > Cookies" + ), + label: vscode.l10n.t("JWT Token") } ], { - placeHolder: "选择Token类型查看详细说明", - title: "Token获取说明" + placeHolder: vscode.l10n.t( + "Select token type to view detailed instructions" + ), + title: vscode.l10n.t("Token acquisition instructions") } ) @@ -128,9 +137,10 @@ export class CommandController { const token = await vscode.window.showInputBox({ password: true, - placeHolder: "输入API Token (sk-开头) 或 JWT Token", - prompt: - "推荐使用永久有效的API Token (sk-开头)。JWT Token可从PackyCode Dashboard的Cookie中获取", + placeHolder: vscode.l10n.t("Enter API Token (sk-) or JWT Token"), + prompt: vscode.l10n.t( + "Recommended: Use permanent API Token (sk-). JWT Token can be obtained from PackyCode Dashboard cookies" + ), validateInput: (value) => this.validateTokenInput(value) }) @@ -144,16 +154,18 @@ export class CommandController { // 显示Token类型和过期时间信息 if (token.startsWith("sk-")) { vscode.window.showInformationMessage( - "API Token保存成功!该Token永久有效。" + vscode.l10n.t( + "API Token saved successfully! This Token is permanently valid." + ) ) } else { const expiration = this.secretService.getTokenExpiration(token) const expirationText = expiration - ? `,将于 ${expiration.toLocaleString()} 过期` + ? vscode.l10n.t("will expire at {0}", expiration.toLocaleString()) : "" vscode.window.showInformationMessage( - `JWT Token保存成功${expirationText}!` + vscode.l10n.t("JWT Token saved successfully{0}!", expirationText) ) } @@ -162,7 +174,7 @@ export class CommandController { await this.fetchAndUpdateData() } catch (error) { vscode.window.showErrorMessage( - `保存Token失败: ${(error as Error).message}` + vscode.l10n.t("Failed to save Token: {0}", (error as Error).message) ) } } @@ -179,30 +191,40 @@ export class CommandController { */ private async showTokenSetupPrompt(): Promise { const choice = await vscode.window.showInformationMessage( - "需要配置访问令牌来获取预算数据", - "立即配置", - "查看帮助", - "稍后配置" + vscode.l10n.t("You need to configure an access token to get budget data"), + vscode.l10n.t("Configure Now"), + vscode.l10n.t("View Help"), + vscode.l10n.t("Configure Later") ) switch (choice) { - case "查看帮助": { - const helpText = `### Token获取方式 + case vscode.l10n.t("Configure Later"): + vscode.window.showInformationMessage( + vscode.l10n.t( + 'You can configure it later by searching for "Set API Token" in the command palette.' + ) + ) + break + case vscode.l10n.t("Configure Now"): + vscode.commands.executeCommand("packy-usage.setToken") + break + case vscode.l10n.t("View Help"): { + const helpText = vscode.l10n.t(`### Token Acquisition Methods -**API Token (推荐)** -- 永久有效的访问令牌 -- 到PackyCode Dashboard直接获取以'sk-'开头的API Token +**API Token (Recommended)** +- Permanently valid access token +- Get API Token starting with 'sk-' directly from PackyCode Dashboard **JWT Token** -- 从PackyCode Dashboard获取的临时令牌 -- 获取步骤: - 1. 访问PackyCode Dashboard - 2. 打开浏览器开发者工具(按F12或右键选择"检查") - 3. 切换到"Application"或"Storage"选项卡 - 4. 在左侧找到"Cookies"并展开 - 5. 选择当前网站域名 - 6. 在右侧列表中找到名为"token"的Cookie - 7. 复制其值作为您的JWT Token` +- Temporary token from PackyCode Dashboard +- Acquisition steps: + 1. Visit PackyCode Dashboard + 2. Open browser developer tools (press F12 or right-click and select "Inspect") + 3. Switch to "Application" or "Storage" tab + 4. Find "Cookies" in the left panel and expand it + 5. Select the current website domain + 6. Find the cookie named "token" in the right list + 7. Copy its value as your JWT Token`) vscode.window.showInformationMessage(helpText, { modal: true }) // 显示帮助后再提示配置 @@ -211,14 +233,6 @@ export class CommandController { }, 500) break } - case "稍后配置": - vscode.window.showInformationMessage( - '您可以随时通过命令面板搜索 "Set API Token" 来配置。' - ) - break - case "立即配置": - vscode.commands.executeCommand("packy-usage.setToken") - break } } @@ -227,7 +241,7 @@ export class CommandController { */ private validateTokenInput(value: string): null | string { if (!value || value.trim().length === 0) { - return "Token不能为空" + return vscode.l10n.t("Token cannot be empty") } // 优先检查是否为SK Token @@ -250,15 +264,20 @@ export class CommandController { const currentTime = Math.floor(Date.now() / 1000) if (currentTime >= parsedPayload.exp) { const expiredDate = new Date(parsedPayload.exp * 1000) - return `JWT Token已过期(过期时间: ${expiredDate.toLocaleString()})` + return vscode.l10n.t( + "JWT Token has expired (expiration: {0})", + expiredDate.toLocaleString() + ) } } + return null } catch { - return "JWT Token格式无效" + return vscode.l10n.t("Invalid JWT Token format") } - return null } - return "无法识别的Token格式,请提供API Token (sk-开头) 或 JWT Token" + return vscode.l10n.t( + "Unrecognized Token format, please provide API Token (sk-) or JWT Token" + ) } } diff --git a/src/controllers/extension.manager.ts b/src/controllers/extension.manager.ts index e5198cb..0dfd6bf 100644 --- a/src/controllers/extension.manager.ts +++ b/src/controllers/extension.manager.ts @@ -47,11 +47,19 @@ export class ExtensionManager { // 启动服务 await this.startServices() - console.log("🚀 Packy Usage Extension 激活成功") + console.log( + vscode.l10n.t("🚀 Packy Usage Extension activated successfully") + ) } catch (error) { - console.error("❌ Packy Usage Extension 激活失败:", error) + console.error( + vscode.l10n.t("❌ Packy Usage Extension activation failed:"), + error + ) vscode.window.showErrorMessage( - `插件激活失败: ${(error as Error).message}` + vscode.l10n.t( + "Extension activation failed: {0}", + (error as Error).message + ) ) } } @@ -61,7 +69,7 @@ export class ExtensionManager { */ deactivate(): void { this.dispose() - console.log("🔄 Packy Usage Extension 已停用") + console.log(vscode.l10n.t("🔄 Packy Usage Extension deactivated")) } /** @@ -69,19 +77,21 @@ export class ExtensionManager { */ getStatus(): string { if (!this.configService) { - return "扩展未初始化" + return vscode.l10n.t("Extension not initialized") } const config = this.configService.getConfig() const dataLoaded = this.dataService?.isDataLoaded ?? false - const pollingActive = this.pollingService ? "运行中" : "已停止" - - return `Packy Usage 状态: -- 数据已加载: ${dataLoaded ? "是" : "否"} -- 轮询状态: ${pollingActive} -- 配置状态: ${config.apiToken ? "Token已配置" : "Token未配置"} -- API端点: ${config.apiEndpoint} -- 轮询间隔: ${config.pollingInterval}ms` + const pollingActive = this.pollingService + ? vscode.l10n.t("Running") + : vscode.l10n.t("Stopped") + + return `Packy Usage ${vscode.l10n.t("Status")}: +- ${vscode.l10n.t("Data Loaded")}: ${dataLoaded ? vscode.l10n.t("Yes") : vscode.l10n.t("No")} +- ${vscode.l10n.t("Polling Status")}: ${pollingActive} +- ${vscode.l10n.t("Configuration Status")}: ${config.apiToken ? vscode.l10n.t("Token Configured") : vscode.l10n.t("Token Not Configured")} +- API${vscode.l10n.t("Endpoint")}: ${config.apiEndpoint} +- ${vscode.l10n.t("Polling Interval")}: ${config.pollingInterval}ms` } /** @@ -93,7 +103,7 @@ export class ExtensionManager { try { disposable.dispose() } catch (error) { - console.error("清理资源时出错:", error) + console.error(vscode.l10n.t("Error during cleanup:"), error) } }) @@ -104,7 +114,7 @@ export class ExtensionManager { this.statusBarService?.dispose() this.pollingService?.dispose() } catch (error) { - console.error("清理服务时出错:", error) + console.error(vscode.l10n.t("Error cleaning up service:"), error) } this.disposables = [] @@ -201,7 +211,7 @@ export class ExtensionManager { // 监听配置变更事件 const configChangeListener = this.configService.onConfigurationChanged( (config) => { - console.log("📝 配置已更新:", config) + console.log(vscode.l10n.t("📝 Configuration updated:"), config) // 如果轮询设置改变,重启轮询服务 if (config.enablePolling) { this.pollingService.start() diff --git a/src/providers/usage-explorer.provider.ts b/src/providers/usage-explorer.provider.ts index c0266e0..d1f7a46 100644 --- a/src/providers/usage-explorer.provider.ts +++ b/src/providers/usage-explorer.provider.ts @@ -40,13 +40,13 @@ export class UsageExplorerProvider if (!token) { return [ new UsageItem( - "⚠️ 未配置 Token", + vscode.l10n.t("⚠️ Token not configured"), vscode.TreeItemCollapsibleState.None, "noToken", "$(warning)" ), new UsageItem( - "点击设置 API Token", + vscode.l10n.t("Click to set API Token"), vscode.TreeItemCollapsibleState.None, "setToken", "$(gear)" @@ -57,13 +57,13 @@ export class UsageExplorerProvider if (!this.dataService.isDataLoaded) { return [ new UsageItem( - "📊 点击刷新获取预算数据", + vscode.l10n.t("📊 Click refresh to get budget data"), vscode.TreeItemCollapsibleState.None, "noData", "$(info)" ), new UsageItem( - "🔧 配置", + vscode.l10n.t("🔧 Configuration"), vscode.TreeItemCollapsibleState.Collapsed, "settings", "$(gear)" @@ -73,19 +73,19 @@ export class UsageExplorerProvider return [ new UsageItem( - "日预算", + vscode.l10n.t("Daily Budget"), vscode.TreeItemCollapsibleState.Expanded, "dailyBudget", "$(calendar)" ), new UsageItem( - "月预算", + vscode.l10n.t("Monthly Budget"), vscode.TreeItemCollapsibleState.Expanded, "monthlyBudget", "$(calendar)" ), new UsageItem( - "🔧 配置", + vscode.l10n.t("🔧 Configuration"), vscode.TreeItemCollapsibleState.Collapsed, "settings", "$(gear)" @@ -117,19 +117,19 @@ export class UsageExplorerProvider return Promise.resolve([ new UsageItem( - `已使用: $${dailyUsed.toFixed(2)}`, + vscode.l10n.t("Used: ${0}", dailyUsed.toFixed(2)), vscode.TreeItemCollapsibleState.None, "dailyUsed", "$(circle-filled)" ), new UsageItem( - `总预算: $${dailyTotal.toFixed(2)}`, + vscode.l10n.t("Total Budget: ${0}", dailyTotal.toFixed(2)), vscode.TreeItemCollapsibleState.None, "dailyTotal", "$(circle-outline)" ), new UsageItem( - `使用率: ${dailyPercentage.toFixed(1)}%`, + vscode.l10n.t("Usage Rate: {0}%", dailyPercentage.toFixed(1)), vscode.TreeItemCollapsibleState.None, "dailyPercentage", this.dataService.getPercentageIcon(dailyPercentage) @@ -145,19 +145,19 @@ export class UsageExplorerProvider return Promise.resolve([ new UsageItem( - `已使用: $${monthlyUsed.toFixed(2)}`, + vscode.l10n.t("Used: ${0}", monthlyUsed.toFixed(2)), vscode.TreeItemCollapsibleState.None, "monthlyUsed", "$(circle-filled)" ), new UsageItem( - `总预算: $${monthlyTotal.toFixed(2)}`, + vscode.l10n.t("Total Budget: ${0}", monthlyTotal.toFixed(2)), vscode.TreeItemCollapsibleState.None, "monthlyTotal", "$(circle-outline)" ), new UsageItem( - `使用率: ${monthlyPercentage.toFixed(1)}%`, + vscode.l10n.t("Usage Rate: {0}%", monthlyPercentage.toFixed(1)), vscode.TreeItemCollapsibleState.None, "monthlyPercentage", this.dataService.getPercentageIcon(monthlyPercentage) @@ -172,15 +172,15 @@ export class UsageExplorerProvider const endpoint = config.get("apiEndpoint") // 构建token状态显示 - let tokenLabel = "Token: " + let tokenLabel = vscode.l10n.t("Token: ") if (!token) { - tokenLabel += "未配置" + tokenLabel += vscode.l10n.t("Not Configured") } else if (tokenType === TokenType.API_KEY) { - tokenLabel += "已配置 (API Token)" + tokenLabel += vscode.l10n.t("Configured (API Token)") } else if (tokenType === TokenType.JWT) { - tokenLabel += "已配置 (JWT Token)" + tokenLabel += vscode.l10n.t("Configured (JWT Token)") } else { - tokenLabel += "已配置" + tokenLabel += vscode.l10n.t("Configured") } return [ @@ -191,7 +191,7 @@ export class UsageExplorerProvider token ? "$(check)" : "$(x)" ), new UsageItem( - "设置 Token", + vscode.l10n.t("Set Token"), vscode.TreeItemCollapsibleState.None, "setToken", "$(edit)" diff --git a/src/providers/usage-item.ts b/src/providers/usage-item.ts index fe97858..df55c28 100644 --- a/src/providers/usage-item.ts +++ b/src/providers/usage-item.ts @@ -36,27 +36,29 @@ export class UsageItem extends vscode.TreeItem { private getTooltip(): string { switch (this.contextValue) { case "dailyBudget": - return "今日预算使用情况" + return vscode.l10n.t("Today's budget usage") case "dailyPercentage": - return "今日预算使用百分比" + return vscode.l10n.t("Today's budget usage percentage") case "dailyTotal": - return "今日总预算金额" + return vscode.l10n.t("Today's total budget amount") case "dailyUsed": - return "今日已使用的预算金额" + return vscode.l10n.t("Today's used budget amount") case "monthlyBudget": - return "本月预算使用情况" + return vscode.l10n.t("This month's budget usage") case "monthlyPercentage": - return "本月预算使用百分比" + return vscode.l10n.t("This month's budget usage percentage") case "monthlyTotal": - return "本月总预算金额" + return vscode.l10n.t("This month's total budget amount") case "monthlyUsed": - return "本月已使用的预算金额" + return vscode.l10n.t("This month's used budget amount") case "noData": - return "点击刷新获取最新预算数据" + return vscode.l10n.t("Click refresh to get the latest budget data") case "noToken": - return "需要配置API Token才能获取预算数据" + return vscode.l10n.t( + "API Token needs to be configured to get budget data" + ) case "setToken": - return "点击设置API Token" + return vscode.l10n.t("Click to configure API Token") default: return this.label } diff --git a/src/services/api.service.ts b/src/services/api.service.ts index 8c4cf69..39baa88 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -57,11 +57,19 @@ export class ApiService { // 认证失败可能是Token过期,触发Token清理 await this.handleAuthFailure() throw ErrorHandler.createAuthError( - `认证失败 (${response.status}): ${response.statusText}` + vscode.l10n.t( + "Authentication failed ({0}): {1}", + response.status, + response.statusText + ) ) } throw ErrorHandler.createApiError( - `API请求失败 (${response.status}): ${response.statusText}` + vscode.l10n.t( + "API request failed ({0}): {1}", + response.status, + response.statusText + ) ) } @@ -70,10 +78,16 @@ export class ApiService { } catch (error) { if (error instanceof Error) { if (error.name === "AbortError") { - throw ErrorHandler.createNetworkError("请求超时", error) + throw ErrorHandler.createNetworkError( + vscode.l10n.t("Request timeout"), + error + ) } if (error.message.includes("fetch")) { - throw ErrorHandler.createNetworkError("网络连接失败", error) + throw ErrorHandler.createNetworkError( + vscode.l10n.t("Network connection failed"), + error + ) } } throw error diff --git a/src/services/config.service.ts b/src/services/config.service.ts index 5e8bbb1..a207ace 100644 --- a/src/services/config.service.ts +++ b/src/services/config.service.ts @@ -89,12 +89,12 @@ export class ConfigService { */ getConfigSummary(): string { const config = this.getConfig() - return `配置摘要: -- API端点: ${config.apiEndpoint} -- 轮询间隔: ${config.pollingInterval}ms -- 启用轮询: ${config.enablePolling} -- 状态栏刷新: ${config.statusBarRefreshInterval}ms -- Token配置: ${config.apiToken ? "已配置" : "未配置"}` + return `${vscode.l10n.t("Configuration Summary")}: +- API ${vscode.l10n.t("Endpoint")}: ${config.apiEndpoint} +- ${vscode.l10n.t("Polling Interval")}: ${config.pollingInterval}ms +- ${vscode.l10n.t("Enable Polling")}: ${config.enablePolling} +- ${vscode.l10n.t("Status Bar Refresh")}: ${config.statusBarRefreshInterval}ms +- Token ${vscode.l10n.t("Status")}: ${config.apiToken ? vscode.l10n.t("Configured") : vscode.l10n.t("Not Configured")}` } /** @@ -216,17 +216,23 @@ export class ConfigService { // 验证 API 端点 if (!config.apiEndpoint || !this.isValidUrl(config.apiEndpoint)) { - errors.push("API 端点地址无效") + errors.push(vscode.l10n.t("Invalid API endpoint address")) } // 验证轮询间隔 if (config.pollingInterval < 5000) { - errors.push("轮询间隔不能少于5秒") + errors.push( + vscode.l10n.t("Polling interval cannot be less than 5 seconds") + ) } // 验证状态栏刷新间隔 if (config.statusBarRefreshInterval < 100) { - errors.push("状态栏刷新间隔不能少于100毫秒") + errors.push( + vscode.l10n.t( + "Status bar refresh interval cannot be less than 100 milliseconds" + ) + ) } return { diff --git a/src/services/polling.service.ts b/src/services/polling.service.ts index 48ae024..24cd6b0 100644 --- a/src/services/polling.service.ts +++ b/src/services/polling.service.ts @@ -1,3 +1,5 @@ +import * as vscode from "vscode" + import { ErrorHandler } from "../utils/error-handler" import { ApiService } from "./api.service" import { DataService } from "./data.service" @@ -28,7 +30,7 @@ export class PollingService { } catch (error) { ErrorHandler.handle(error as Error) // 轮询失败时更新状态栏 - this.statusBarService.showErrorStatus("数据同步失败") + this.statusBarService.showErrorStatus(vscode.l10n.t("Data sync failed")) } }, this.pollingInterval) } diff --git a/src/services/secret.service.ts b/src/services/secret.service.ts index e5b6877..bddb740 100644 --- a/src/services/secret.service.ts +++ b/src/services/secret.service.ts @@ -124,9 +124,12 @@ export class SecretService { if (validator && validator.isExpired(token)) { await this.deleteToken() vscode.window - .showWarningMessage("Token已过期,请重新配置", "立即配置") + .showWarningMessage( + vscode.l10n.t("Token has expired, please reconfigure"), + vscode.l10n.t("Configure Now") + ) .then((choice) => { - if (choice === "立即配置") { + if (choice === vscode.l10n.t("Configure Now")) { vscode.commands.executeCommand("packy-usage.setToken") } }) @@ -162,17 +165,17 @@ export class SecretService { const tokenType = this.detectTokenType(token) if (!tokenType) { throw new Error( - "无法识别Token类型,请提供有效的API Token (sk-开头) 或 JWT Token" + "Unrecognized Token type, please provide valid API Token (sk-) or JWT Token" ) } const validator = this.validators.get(tokenType)! if (!validator.validate(token)) { - throw new Error(`无效的${tokenType} Token格式`) + throw new Error(`Invalid ${tokenType} Token format`) } if (validator.isExpired(token)) { - throw new Error("Token已过期") + throw new Error("Token has expired") } await this.context.secrets.store(this.TOKEN_KEY, token) diff --git a/src/services/status-bar.service.ts b/src/services/status-bar.service.ts index 72fedb1..71a4494 100644 --- a/src/services/status-bar.service.ts +++ b/src/services/status-bar.service.ts @@ -24,22 +24,25 @@ export class StatusBarService { } showErrorStatus(message?: string): void { - this.statusBarItem.text = "$(warning) 获取失败" - this.statusBarItem.tooltip = message || "预算数据获取失败,点击重试" + this.statusBarItem.text = vscode.l10n.t("$(warning) Fetch failed") + this.statusBarItem.tooltip = + message || vscode.l10n.t("Budget data fetch failed, click to retry") this.statusBarItem.command = "packy-usage.refresh" this.statusBarItem.show() } showLoadingStatus(): void { - this.statusBarItem.text = "$(loading~spin) 获取数据中..." - this.statusBarItem.tooltip = "正在获取预算数据,请稍候" + this.statusBarItem.text = vscode.l10n.t("$(loading~spin) Fetching data...") + this.statusBarItem.tooltip = vscode.l10n.t( + "Fetching budget data, please wait" + ) this.statusBarItem.command = "packy-usage.refresh" this.statusBarItem.show() } showNoTokenStatus(): void { - this.statusBarItem.text = "$(key) 需要配置Token" - this.statusBarItem.tooltip = "点击配置API Token" + this.statusBarItem.text = vscode.l10n.t("$(key) Token configuration needed") + this.statusBarItem.tooltip = vscode.l10n.t("Click to configure API Token") this.statusBarItem.command = "packy-usage.setToken" this.statusBarItem.show() } @@ -48,16 +51,24 @@ export class StatusBarService { this.hasDataLoaded = isDataLoaded if (!isDataLoaded || !data) { - this.statusBarItem.text = "$(warning) 未获取预算数据" - this.statusBarItem.tooltip = "点击获取预算数据" + this.statusBarItem.text = vscode.l10n.t("$(warning) No budget data") + this.statusBarItem.tooltip = vscode.l10n.t("Click to get budget data") this.statusBarItem.command = "packy-usage.refresh" } else { const dailyPercentage = data.daily.percentage const dailyUsed = data.daily.used const dailyTotal = data.daily.total - this.statusBarItem.text = `日预算: ${dailyPercentage.toFixed(1)}%` - this.statusBarItem.tooltip = `日预算使用率: ${dailyPercentage.toFixed(1)}%\n已用: $${dailyUsed.toFixed(2)} / $${dailyTotal.toFixed(2)}` + this.statusBarItem.text = vscode.l10n.t( + "Daily Budget: {0}%", + dailyPercentage.toFixed(1) + ) + this.statusBarItem.tooltip = vscode.l10n.t( + "Daily budget usage rate: {0}%\nUsed: ${1} / ${2}", + dailyPercentage.toFixed(1), + dailyUsed.toFixed(2), + dailyTotal.toFixed(2) + ) this.statusBarItem.command = "packy-usage.showExplorer" } @@ -65,8 +76,8 @@ export class StatusBarService { } private showInitialStatus(): void { - this.statusBarItem.text = "$(loading~spin) 初始化中..." - this.statusBarItem.tooltip = "正在初始化预算数据" + this.statusBarItem.text = vscode.l10n.t("$(loading~spin) Initializing...") + this.statusBarItem.tooltip = vscode.l10n.t("Initializing budget data") this.statusBarItem.command = "packy-usage.refresh" this.statusBarItem.show() } diff --git a/src/utils/error-handler.ts b/src/utils/error-handler.ts index 8feb507..c749edd 100644 --- a/src/utils/error-handler.ts +++ b/src/utils/error-handler.ts @@ -50,26 +50,39 @@ export class ErrorHandler { private static handleAppError(error: AppError): void { switch (error.type) { case ErrorType.API_ERROR: - vscode.window.showErrorMessage(`API请求失败: ${error.message}`) + vscode.window.showErrorMessage( + vscode.l10n.t("API request failed: {0}", error.message) + ) break case ErrorType.AUTH_ERROR: vscode.window.showErrorMessage( - `认证失败: ${error.message}。请检查API Token是否正确。` + vscode.l10n.t( + "Authentication failed: {0}. Please check if the API Token is correct.", + error.message + ) ) break case ErrorType.NETWORK_ERROR: - vscode.window.showErrorMessage(`网络连接错误: ${error.message}`) + vscode.window.showErrorMessage( + vscode.l10n.t("Network connection error: {0}", error.message) + ) break case ErrorType.VALIDATION_ERROR: - vscode.window.showWarningMessage(`输入验证失败: ${error.message}`) + vscode.window.showWarningMessage( + vscode.l10n.t("Input validation failed: {0}", error.message) + ) break default: - vscode.window.showErrorMessage(`未知错误: ${error.message}`) + vscode.window.showErrorMessage( + vscode.l10n.t("Unknown error: {0}", error.message) + ) break } } private static handleUnknownError(error: Error): void { - vscode.window.showErrorMessage(`未知错误: ${error.message}`) + vscode.window.showErrorMessage( + vscode.l10n.t("Unknown error: {0}", error.message) + ) } }