feat: 完成三个核心组件的国际化实现

- 完成 GroupManagementModal.vue 组件国际化
  * 添加分组管理相关的所有翻译键
  * 实现创建、编辑、删除分组功能的多语言支持

- 完成 OAuthFlow.vue 组件国际化
  * 支持 Claude、Gemini、OpenAI 三个平台的授权流程
  * 修复模板中的语法错误(缺少引号)
  * 保留技术性地址不进行翻译

- 完成 ProxyConfig.vue 组件国际化
  * 添加代理配置相关的翻译键
  * 支持 SOCKS5 和 HTTP 代理类型的多语言显示

- 更新语言文件
  * 在 zh-cn.js、en.js、zh-tw.js 中添加所有新的翻译键
  * 保持三种语言文件的同步

变更统计:6 文件修改,526 行新增,116 行删除
This commit is contained in:
Wangnov
2025-09-09 19:21:02 +08:00
parent 2b40552eab
commit 2de5191c05
6 changed files with 526 additions and 116 deletions

View File

@@ -12,9 +12,9 @@
<i class="fas fa-link text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-blue-900 dark:text-blue-200">Claude 账户授权</h4>
<h4 class="mb-3 font-semibold text-blue-900 dark:text-blue-200">{{ t('oauthFlow.claudeAccountAuth') }}</h4>
<p class="mb-4 text-sm text-blue-800 dark:text-blue-300">
请按照以下步骤完成 Claude 账户的授权
{{ t('oauthFlow.claudeAuthDescription') }}
</p>
<div class="space-y-4">
@@ -30,7 +30,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
点击下方按钮生成授权链接
{{ t('oauthFlow.step1Title') }}
</p>
<button
v-if="!authUrl"
@@ -40,7 +40,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? '生成中...' : '生成授权链接' }}
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -52,7 +52,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
title="复制链接"
:title="t('oauthFlow.copyLinkTooltip')"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -62,7 +62,7 @@
class="text-xs text-blue-600 hover:text-blue-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />重新生成
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
</button>
</div>
</div>
@@ -81,18 +81,18 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
在浏览器中打开链接并完成授权
{{ t('oauthFlow.step2Title') }}
</p>
<p class="mb-2 text-sm text-blue-700 dark:text-blue-300">
请在新标签页中打开授权链接登录您的 Claude 账户并授权
{{ t('oauthFlow.step2Description') }}
</p>
<div
class="rounded border border-yellow-300 bg-yellow-50 p-3 dark:border-yellow-700 dark:bg-yellow-900/30"
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
</p>
</div>
</div>
@@ -111,29 +111,29 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
输入 Authorization Code
{{ t('oauthFlow.step3Title') }}
</p>
<p class="mb-3 text-sm text-blue-700 dark:text-blue-300">
授权完成后页面会显示一个
<strong>Authorization Code</strong>请将其复制并粘贴到下方输入框
{{ t('oauthFlow.step3Description') }}
<strong>{{ t('oauthFlow.authorizationCode') }}</strong>{{ t('oauthFlow.step3DescriptionMiddle') }}
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-key mr-2 text-blue-500" />Authorization Code
<i class="fas fa-key mr-2 text-blue-500" />{{ t('oauthFlow.authorizationCode') }}
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
placeholder="粘贴从Claude页面获取的Authorization Code..."
:placeholder="t('oauthFlow.authCodePlaceholder')"
rows="3"
/>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
<i class="fas fa-info-circle mr-1" />
请粘贴从Claude页面复制的Authorization Code
{{ t('oauthFlow.authCodeHint') }}
</p>
</div>
</div>
@@ -157,9 +157,9 @@
<i class="fas fa-robot text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-green-900 dark:text-green-200">Gemini 账户授权</h4>
<h4 class="mb-3 font-semibold text-green-900 dark:text-green-200">{{ t('oauthFlow.geminiAccountAuth') }}</h4>
<p class="mb-4 text-sm text-green-800 dark:text-green-300">
请按照以下步骤完成 Gemini 账户的授权
{{ t('oauthFlow.geminiAuthDescription') }}
</p>
<div class="space-y-4">
@@ -175,7 +175,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
点击下方按钮生成授权链接
{{ t('oauthFlow.step1Title') }}
</p>
<button
v-if="!authUrl"
@@ -185,7 +185,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? '生成中...' : '生成授权链接' }}
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -197,7 +197,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
title="复制链接"
:title="t('oauthFlow.copyLinkTooltip')"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -207,7 +207,7 @@
class="text-xs text-green-600 hover:text-green-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />重新生成
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
</button>
</div>
</div>
@@ -226,18 +226,18 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
在浏览器中打开链接并完成授权
{{ t('oauthFlow.step2Title') }}
</p>
<p class="mb-2 text-sm text-green-700 dark:text-green-300">
请在新标签页中打开授权链接登录您的 Gemini 账户并授权
{{ t('oauthFlow.step2DescriptionGemini') }}
</p>
<div
class="rounded border border-yellow-300 bg-yellow-50 p-3 dark:border-yellow-700 dark:bg-yellow-900/30"
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
</p>
</div>
</div>
@@ -256,29 +256,29 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
输入 Authorization Code
{{ t('oauthFlow.step3Title') }}
</p>
<p class="mb-3 text-sm text-green-700 dark:text-green-300">
授权完成后页面会显示一个 Authorization Code请将其复制并粘贴到下方输入框
{{ t('oauthFlow.step3DescriptionGemini') }}
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-key mr-2 text-green-500" />Authorization Code
<i class="fas fa-key mr-2 text-green-500" />{{ t('oauthFlow.authorizationCode') }}
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
placeholder="粘贴从Gemini页面获取的Authorization Code..."
:placeholder="t('oauthFlow.authCodePlaceholderGemini')"
rows="3"
/>
</div>
<div class="mt-2 space-y-1">
<p class="text-xs text-gray-600 dark:text-gray-400">
<i class="fas fa-check-circle mr-1 text-green-500" />
请粘贴从Gemini页面复制的Authorization Code
{{ t('oauthFlow.authCodeHintGemini') }}
</p>
</div>
</div>
@@ -303,9 +303,9 @@
<i class="fas fa-brain text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-orange-900 dark:text-orange-200">OpenAI 账户授权</h4>
<h4 class="mb-3 font-semibold text-orange-900 dark:text-orange-200">{{ t('oauthFlow.openaiAccountAuth') }}</h4>
<p class="mb-4 text-sm text-orange-800 dark:text-orange-300">
请按照以下步骤完成 OpenAI 账户的授权
{{ t('oauthFlow.openaiAuthDescription') }}
</p>
<div class="space-y-4">
@@ -321,7 +321,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
点击下方按钮生成授权链接
{{ t('oauthFlow.step1Title') }}
</p>
<button
v-if="!authUrl"
@@ -331,7 +331,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? '生成中...' : '生成授权链接' }}
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -343,7 +343,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
title="复制链接"
:title="t('oauthFlow.copyLinkTooltip')"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -353,7 +353,7 @@
class="text-xs text-orange-600 hover:text-orange-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />重新生成
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
</button>
</div>
</div>
@@ -372,22 +372,22 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
在浏览器中打开链接并完成授权
{{ t('oauthFlow.step2Title') }}
</p>
<p class="mb-2 text-sm text-orange-700 dark:text-orange-300">
请在新标签页中打开授权链接登录您的 OpenAI 账户并授权
{{ t('oauthFlow.step2DescriptionOpenAI') }}
</p>
<div
class="mb-3 rounded border border-amber-300 bg-amber-50 p-3 dark:border-amber-700 dark:bg-amber-900/30"
>
<p class="text-xs text-amber-800 dark:text-amber-300">
<i class="fas fa-clock mr-1" />
<strong>重要提示</strong>授权后页面可能会加载较长时间请耐心等待
<strong>{{ t('oauthFlow.openaiImportantNote') }}</strong>{{ t('oauthFlow.openaiLoadingNote') }}
</p>
<p class="mt-2 text-xs text-amber-700 dark:text-amber-400">
当浏览器地址栏变为
{{ t('oauthFlow.openaiAddressNote') }}
<strong class="font-mono">http://localhost:1455/...</strong>
开头时表示授权已完成
{{ t('oauthFlow.openaiAddressNoteMiddle') }}
</p>
</div>
<div
@@ -395,8 +395,8 @@
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
</p>
</div>
</div>
@@ -415,23 +415,23 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
输入授权链接或 Code
{{ t('oauthFlow.step3TitleOpenAI') }}
</p>
<p class="mb-3 text-sm text-orange-700 dark:text-orange-300">
授权完成后当页面地址变为
<strong class="font-mono">http://localhost:1455/...</strong> 时:
{{ t('oauthFlow.step3DescriptionOpenAI') }}
<strong class="font-mono">http://localhost:1455/...</strong> {{ t('oauthFlow.step3DescriptionOpenAIMiddle') }}
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-link mr-2 text-orange-500" />授权链接或 Code
<i class="fas fa-link mr-2 text-orange-500" />{{ t('oauthFlow.authLinkOrCode') }}
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
placeholder="方式1复制完整的链接http://localhost:1455/auth/callback?code=...&#10;方式2仅复制 code 参数的值&#10;系统会自动识别并提取所需信息"
:placeholder="t('oauthFlow.authCodePlaceholderOpenAI')"
rows="3"
/>
</div>
@@ -440,16 +440,15 @@
>
<p class="text-xs text-blue-700 dark:text-blue-300">
<i class="fas fa-lightbulb mr-1" />
<strong>提示</strong>您可以直接复制整个链接或仅复制 code
参数值系统会自动识别
<strong>{{ t('oauthFlow.openaiTip') }}</strong>{{ t('oauthFlow.openaiTipText') }}
</p>
<p class="mt-1 text-xs text-blue-600 dark:text-blue-400">
完整链接示例<span class="font-mono"
{{ t('oauthFlow.openaiLinkExample') }}<span class="font-mono"
>http://localhost:1455/auth/callback?code=ac_4hm8...</span
>
</p>
<p class="text-xs text-blue-600">
Code 示例<span class="font-mono"
{{ t('oauthFlow.openaiCodeExample') }}<span class="font-mono"
>ac_4hm8iqmx9A2fzMy_cwye7U3W7...</span
>
</p>
@@ -470,7 +469,7 @@
type="button"
@click="$emit('back')"
>
上一步
{{ t('oauthFlow.previousStep') }}
</button>
<button
class="btn btn-primary flex-1 px-6 py-3 font-semibold"
@@ -479,7 +478,7 @@
@click="exchangeCode"
>
<div v-if="exchanging" class="loading-spinner mr-2" />
{{ exchanging ? '验证中...' : '完成授权' }}
{{ exchanging ? t('oauthFlow.verifying') : t('oauthFlow.completeAuth') }}
</button>
</div>
</div>
@@ -487,9 +486,12 @@
<script setup>
import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { showToast } from '@/utils/toast'
import { useAccountsStore } from '@/stores/accounts'
const { t } = useI18n()
const props = defineProps({
platform: {
type: String,
@@ -544,16 +546,16 @@ watch(authCode, (newValue) => {
if (code) {
// 成功提取授权码
authCode.value = code
showToast('成功提取授权码!', 'success')
showToast(t('oauthFlow.successExtractCode'), 'success')
console.log('Successfully extracted authorization code from URL')
} else {
// URL 中没有 code 参数
showToast('URL 中未找到授权码参数,请检查链接是否正确', 'error')
showToast(t('oauthFlow.errorCodeNotFound'), 'error')
}
} catch (error) {
// URL 解析失败
console.error('Failed to parse URL:', error)
showToast('链接格式错误,请检查是否为完整的 URL', 'error')
showToast(t('oauthFlow.errorLinkFormat'), 'error')
}
} else if (props.platform === 'gemini' || props.platform === 'openai') {
// Gemini 和 OpenAI 平台可能使用不同的回调URL
@@ -564,14 +566,14 @@ watch(authCode, (newValue) => {
if (code) {
authCode.value = code
showToast('成功提取授权码!', 'success')
showToast(t('oauthFlow.successExtractCode'), 'success')
}
} catch (error) {
// 不是有效的URL保持原值
}
} else {
// 错误的 URL不是正确的 localhost 回调地址)
showToast('请粘贴以 http://localhost:1455 或 http://localhost:45462 开头的链接', 'error')
showToast(t('oauthFlow.errorWrongUrlFormat'), 'error')
}
}
// 如果不是 URL保持原值兼容直接输入授权码
@@ -607,7 +609,7 @@ const generateAuthUrl = async () => {
sessionId.value = result.sessionId
}
} catch (error) {
showToast(error.message || '生成授权链接失败', 'error')
showToast(error.message || t('oauthFlow.generateAuthFailed'), 'error')
} finally {
loading.value = false
}
@@ -625,7 +627,7 @@ const copyAuthUrl = async () => {
try {
await navigator.clipboard.writeText(authUrl.value)
copied.value = true
showToast('链接已复制', 'success')
showToast(t('oauthFlow.linkCopied'), 'success')
setTimeout(() => {
copied.value = false
}, 2000)
@@ -638,7 +640,7 @@ const copyAuthUrl = async () => {
document.execCommand('copy')
document.body.removeChild(input)
copied.value = true
showToast('链接已复制', 'success')
showToast(t('oauthFlow.linkCopied'), 'success')
setTimeout(() => {
copied.value = false
}, 2000)
@@ -695,7 +697,7 @@ const exchangeCode = async () => {
emit('success', tokenInfo)
} catch (error) {
showToast(error.message || '授权失败,请检查授权码是否正确', 'error')
showToast(error.message || t('oauthFlow.authFailed'), 'error')
} finally {
exchanging.value = false
}