From 96f213e3e21f04a7206c642ca1eef22148c3d520 Mon Sep 17 00:00:00 2001 From: Wesley Liddick Date: Fri, 12 Sep 2025 11:09:52 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"feat:=20=E5=AE=8C=E6=95=B4=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96=E6=94=AF=E6=8C=81=20-=20Web=20=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=95=8C=E9=9D=A2=E5=A4=9A=E8=AF=AD=E8=A8=80=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierignore | 18 - VERSION | 2 +- web/admin-spa/.eslintrc.cjs | 3 +- web/admin-spa/auto-imports.d.ts | 102 - web/admin-spa/components.d.ts | 16 - web/admin-spa/package-lock.json | 70 +- web/admin-spa/package.json | 1 - web/admin-spa/src/App.vue | 24 +- .../src/components/accounts/AccountForm.vue | 1830 ++++++---- .../accounts/GroupManagementModal.vue | 97 +- .../src/components/accounts/OAuthFlow.vue | 150 +- .../src/components/accounts/ProxyConfig.vue | 59 +- .../src/components/admin/ChangeRoleModal.vue | 45 +- .../components/admin/UserUsageStatsModal.vue | 95 +- .../components/apikeys/BatchApiKeyModal.vue | 107 +- .../apikeys/BatchEditApiKeyModal.vue | 273 +- .../components/apikeys/CreateApiKeyModal.vue | 457 ++- .../components/apikeys/EditApiKeyModal.vue | 349 +- .../components/apikeys/ExpiryEditModal.vue | 115 +- .../src/components/apikeys/NewApiKeyModal.vue | 82 +- .../components/apikeys/RenewApiKeyModal.vue | 58 +- .../components/apikeys/UsageDetailModal.vue | 72 +- .../components/apikeys/WindowCountdown.vue | 39 +- .../apistats/AggregatedStatsCard.vue | 20 +- .../src/components/apistats/ApiKeyInput.vue | 35 +- .../src/components/apistats/LimitConfig.vue | 81 +- .../components/apistats/ModelUsageStats.vue | 33 +- .../src/components/apistats/StatsOverview.vue | 107 +- .../components/apistats/TokenDistribution.vue | 21 +- .../src/components/common/AccountSelector.vue | 172 +- .../src/components/common/ConfirmDialog.vue | 12 +- .../src/components/common/ConfirmModal.vue | 18 +- .../src/components/common/CustomDropdown.vue | 9 +- .../src/components/common/LanguageSwitch.vue | 188 - .../src/components/common/LogoTitle.vue | 2 +- .../src/components/common/ThemeToggle.vue | 26 +- .../components/common/ToastNotification.vue | 12 +- .../dashboard/ModelDistribution.vue | 33 +- .../src/components/dashboard/UsageTrend.vue | 34 +- .../src/components/layout/AppHeader.vue | 114 +- .../src/components/layout/MainLayout.vue | 5 +- .../src/components/layout/TabBar.vue | 42 +- .../src/components/user/CreateApiKeyModal.vue | 46 +- .../components/user/UserApiKeysManager.vue | 78 +- .../src/components/user/UserUsageStats.vue | 90 +- .../src/components/user/ViewApiKeyModal.vue | 93 +- .../src/composables/useChartConfig.js | 5 +- web/admin-spa/src/composables/useConfirm.js | 12 +- web/admin-spa/src/config/apiStats.js | 6 +- web/admin-spa/src/config/app.js | 2 +- web/admin-spa/src/i18n/index.js | 90 - web/admin-spa/src/i18n/locales/en.js | 3005 --------------- web/admin-spa/src/i18n/locales/zh-cn.js | 2920 --------------- web/admin-spa/src/i18n/locales/zh-tw.js | 2922 --------------- web/admin-spa/src/main.js | 11 +- web/admin-spa/src/router/index.js | 5 +- web/admin-spa/src/stores/accounts.js | 69 +- web/admin-spa/src/stores/apiKeys.js | 21 +- web/admin-spa/src/stores/apistats.js | 31 +- web/admin-spa/src/stores/auth.js | 9 +- web/admin-spa/src/stores/clients.js | 5 +- web/admin-spa/src/stores/dashboard.js | 49 +- web/admin-spa/src/stores/locale.js | 47 - web/admin-spa/src/stores/settings.js | 11 +- web/admin-spa/src/stores/user.js | 7 +- web/admin-spa/src/utils/format.js | 12 +- web/admin-spa/src/views/AccountsView.vue | 339 +- web/admin-spa/src/views/ApiKeysView.vue | 3246 +++++++++++------ web/admin-spa/src/views/ApiStatsView.vue | 51 +- web/admin-spa/src/views/DashboardView.vue | 218 +- web/admin-spa/src/views/LoginView.vue | 28 +- web/admin-spa/src/views/SettingsView.vue | 617 ++-- web/admin-spa/src/views/TutorialView.vue | 1696 ++++++++- web/admin-spa/src/views/UserDashboardView.vue | 67 +- web/admin-spa/src/views/UserLoginView.vue | 24 +- .../src/views/UserManagementView.vue | 109 +- .../src/views/tutorials/TutorialView-en.vue | 1758 --------- .../views/tutorials/TutorialView-zh-cn.vue | 1690 --------- .../views/tutorials/TutorialView-zh-tw.vue | 1690 --------- web/admin-spa/vite.config.js | 6 +- 80 files changed, 7026 insertions(+), 19087 deletions(-) delete mode 100644 .prettierignore delete mode 100644 web/admin-spa/auto-imports.d.ts delete mode 100644 web/admin-spa/components.d.ts delete mode 100644 web/admin-spa/src/components/common/LanguageSwitch.vue delete mode 100644 web/admin-spa/src/i18n/index.js delete mode 100644 web/admin-spa/src/i18n/locales/en.js delete mode 100644 web/admin-spa/src/i18n/locales/zh-cn.js delete mode 100644 web/admin-spa/src/i18n/locales/zh-tw.js delete mode 100644 web/admin-spa/src/stores/locale.js delete mode 100644 web/admin-spa/src/views/tutorials/TutorialView-en.vue delete mode 100644 web/admin-spa/src/views/tutorials/TutorialView-zh-cn.vue delete mode 100644 web/admin-spa/src/views/tutorials/TutorialView-zh-tw.vue diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 859cee34..00000000 --- a/.prettierignore +++ /dev/null @@ -1,18 +0,0 @@ -# Ignore frontend directory as it has its own prettier config and plugins -web/admin-spa/ - -# Ignore node_modules -node_modules/ - -# Ignore build outputs -dist/ -build/ -coverage/ - -# Ignore logs -logs/ -*.log - -# Ignore temporary files -*.tmp -*.temp \ No newline at end of file diff --git a/VERSION b/VERSION index 93599c69..88cf65eb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.138 +1.1.137 diff --git a/web/admin-spa/.eslintrc.cjs b/web/admin-spa/.eslintrc.cjs index 59ceab17..2a625058 100644 --- a/web/admin-spa/.eslintrc.cjs +++ b/web/admin-spa/.eslintrc.cjs @@ -18,8 +18,7 @@ module.exports = { rules: { 'vue/multi-word-component-names': 'off', 'vue/no-v-html': 'off', - // 允许在所有环境中使用 console 语句以避免构建警告 - 'no-console': 'off', + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'prettier/prettier': 'error', 'vue/attributes-order': [ diff --git a/web/admin-spa/auto-imports.d.ts b/web/admin-spa/auto-imports.d.ts deleted file mode 100644 index 9954071d..00000000 --- a/web/admin-spa/auto-imports.d.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// noinspection JSUnusedGlobalSymbols -// Generated by unplugin-auto-import -export {} -declare global { - const EffectScope: (typeof import('vue'))['EffectScope'] - const acceptHMRUpdate: (typeof import('pinia'))['acceptHMRUpdate'] - const computed: (typeof import('vue'))['computed'] - const createApp: (typeof import('vue'))['createApp'] - const createPinia: (typeof import('pinia'))['createPinia'] - const customRef: (typeof import('vue'))['customRef'] - const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'] - const defineComponent: (typeof import('vue'))['defineComponent'] - const defineStore: (typeof import('pinia'))['defineStore'] - const effectScope: (typeof import('vue'))['effectScope'] - const getActivePinia: (typeof import('pinia'))['getActivePinia'] - const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'] - const getCurrentScope: (typeof import('vue'))['getCurrentScope'] - const h: (typeof import('vue'))['h'] - const inject: (typeof import('vue'))['inject'] - const isProxy: (typeof import('vue'))['isProxy'] - const isReactive: (typeof import('vue'))['isReactive'] - const isReadonly: (typeof import('vue'))['isReadonly'] - const isRef: (typeof import('vue'))['isRef'] - const mapActions: (typeof import('pinia'))['mapActions'] - const mapGetters: (typeof import('pinia'))['mapGetters'] - const mapState: (typeof import('pinia'))['mapState'] - const mapStores: (typeof import('pinia'))['mapStores'] - const mapWritableState: (typeof import('pinia'))['mapWritableState'] - const markRaw: (typeof import('vue'))['markRaw'] - const nextTick: (typeof import('vue'))['nextTick'] - const onActivated: (typeof import('vue'))['onActivated'] - const onBeforeMount: (typeof import('vue'))['onBeforeMount'] - const onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave'] - const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate'] - const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'] - const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'] - const onDeactivated: (typeof import('vue'))['onDeactivated'] - const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'] - const onMounted: (typeof import('vue'))['onMounted'] - const onRenderTracked: (typeof import('vue'))['onRenderTracked'] - const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'] - const onScopeDispose: (typeof import('vue'))['onScopeDispose'] - const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'] - const onUnmounted: (typeof import('vue'))['onUnmounted'] - const onUpdated: (typeof import('vue'))['onUpdated'] - const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup'] - const provide: (typeof import('vue'))['provide'] - const reactive: (typeof import('vue'))['reactive'] - const readonly: (typeof import('vue'))['readonly'] - const ref: (typeof import('vue'))['ref'] - const resolveComponent: (typeof import('vue'))['resolveComponent'] - const setActivePinia: (typeof import('pinia'))['setActivePinia'] - const setMapStoreSuffix: (typeof import('pinia'))['setMapStoreSuffix'] - const shallowReactive: (typeof import('vue'))['shallowReactive'] - const shallowReadonly: (typeof import('vue'))['shallowReadonly'] - const shallowRef: (typeof import('vue'))['shallowRef'] - const storeToRefs: (typeof import('pinia'))['storeToRefs'] - const toRaw: (typeof import('vue'))['toRaw'] - const toRef: (typeof import('vue'))['toRef'] - const toRefs: (typeof import('vue'))['toRefs'] - const toValue: (typeof import('vue'))['toValue'] - const triggerRef: (typeof import('vue'))['triggerRef'] - const unref: (typeof import('vue'))['unref'] - const useAttrs: (typeof import('vue'))['useAttrs'] - const useCssModule: (typeof import('vue'))['useCssModule'] - const useCssVars: (typeof import('vue'))['useCssVars'] - const useId: (typeof import('vue'))['useId'] - const useLink: (typeof import('vue-router'))['useLink'] - const useModel: (typeof import('vue'))['useModel'] - const useRoute: (typeof import('vue-router'))['useRoute'] - const useRouter: (typeof import('vue-router'))['useRouter'] - const useSlots: (typeof import('vue'))['useSlots'] - const useTemplateRef: (typeof import('vue'))['useTemplateRef'] - const watch: (typeof import('vue'))['watch'] - const watchEffect: (typeof import('vue'))['watchEffect'] - const watchPostEffect: (typeof import('vue'))['watchPostEffect'] - const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'] -} -// for type re-export -declare global { - // @ts-ignore - export type { - Component, - ComponentPublicInstance, - ComputedRef, - DirectiveBinding, - ExtractDefaultPropTypes, - ExtractPropTypes, - ExtractPublicPropTypes, - InjectionKey, - PropType, - Ref, - MaybeRef, - MaybeRefOrGetter, - VNode, - WritableComputedRef - } from 'vue' - import('vue') -} diff --git a/web/admin-spa/components.d.ts b/web/admin-spa/components.d.ts deleted file mode 100644 index 4eb0ab56..00000000 --- a/web/admin-spa/components.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 -export {} - -declare module 'vue' { - export interface GlobalComponents { - ElConfigProvider: (typeof import('element-plus/es'))['ElConfigProvider'] - ElDatePicker: (typeof import('element-plus/es'))['ElDatePicker'] - ElTooltip: (typeof import('element-plus/es'))['ElTooltip'] - RouterLink: (typeof import('vue-router'))['RouterLink'] - RouterView: (typeof import('vue-router'))['RouterView'] - } -} diff --git a/web/admin-spa/package-lock.json b/web/admin-spa/package-lock.json index 0b959aa3..efe90dff 100644 --- a/web/admin-spa/package-lock.json +++ b/web/admin-spa/package-lock.json @@ -15,7 +15,6 @@ "element-plus": "^2.4.4", "pinia": "^2.1.7", "vue": "^3.3.4", - "vue-i18n": "^9.14.5", "vue-router": "^4.2.5", "xlsx": "^0.18.5", "xlsx-js-style": "^1.2.0" @@ -658,50 +657,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@intlify/core-base": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz", - "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", - "license": "MIT", - "dependencies": { - "@intlify/message-compiler": "9.14.5", - "@intlify/shared": "9.14.5" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/message-compiler": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", - "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", - "license": "MIT", - "dependencies": { - "@intlify/shared": "9.14.5", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/shared": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz", - "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3834,10 +3789,9 @@ }, "node_modules/prettier-plugin-tailwindcss": { "version": "0.6.14", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", + "resolved": "https://registry.npmmirror.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", "dev": true, - "license": "MIT", "engines": { "node": ">=14.21.3" }, @@ -5227,26 +5181,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/vue-i18n": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz", - "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", - "license": "MIT", - "dependencies": { - "@intlify/core-base": "9.14.5", - "@intlify/shared": "9.14.5", - "@vue/devtools-api": "^6.5.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, "node_modules/vue-router": { "version": "4.5.1", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", @@ -5444,7 +5378,7 @@ }, "node_modules/xlsx-js-style": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/xlsx-js-style/-/xlsx-js-style-1.2.0.tgz", + "resolved": "https://registry.npmmirror.com/xlsx-js-style/-/xlsx-js-style-1.2.0.tgz", "integrity": "sha512-DDT4FXFSWfT4DXMSok/m3TvmP1gvO3dn0Eu/c+eXHW5Kzmp7IczNkxg/iEPnImbG9X0Vb8QhROda5eatSR/97Q==", "license": "Apache-2.0", "dependencies": { diff --git a/web/admin-spa/package.json b/web/admin-spa/package.json index 5feb58d8..af353d80 100644 --- a/web/admin-spa/package.json +++ b/web/admin-spa/package.json @@ -18,7 +18,6 @@ "element-plus": "^2.4.4", "pinia": "^2.1.7", "vue": "^3.3.4", - "vue-i18n": "^9.14.5", "vue-router": "^4.2.5", "xlsx": "^0.18.5", "xlsx-js-style": "^1.2.0" diff --git a/web/admin-spa/src/App.vue b/web/admin-spa/src/App.vue index 2b41d216..c68bfba7 100644 --- a/web/admin-spa/src/App.vue +++ b/web/admin-spa/src/App.vue @@ -1,39 +1,25 @@ + + diff --git a/web/admin-spa/src/components/accounts/GroupManagementModal.vue b/web/admin-spa/src/components/accounts/GroupManagementModal.vue index 8c16eaec..b793c356 100644 --- a/web/admin-spa/src/components/accounts/GroupManagementModal.vue +++ b/web/admin-spa/src/components/accounts/GroupManagementModal.vue @@ -11,9 +11,7 @@ > -

- {{ t('groupManagement.title') }} -

+

账户分组管理

-

- {{ t('groupManagement.createGroup') }} -

+

创建新分组

- +
- +
- +
@@ -76,7 +73,7 @@ type="button" @click="$emit('close')" > - {{ t('user.createApiKeyModal.buttons.cancel') }} + Cancel
@@ -124,13 +121,11 @@
-

- {{ t('user.createApiKeyModal.success.title') }} -

+

API Key Created Successfully!

- {{ t('user.createApiKeyModal.success.warning.important') }} - {{ t('user.createApiKeyModal.success.warning.message') }} + Important: Copy your API key now. You won't be able to see it + again!

@@ -154,7 +149,7 @@ stroke-width="2" /> - {{ t('user.createApiKeyModal.buttons.copy') }} + Copy
@@ -164,7 +159,7 @@ class="rounded-md border border-transparent bg-green-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2" @click="handleClose" > - {{ t('user.createApiKeyModal.buttons.done') }} + Done
@@ -177,7 +172,6 @@ diff --git a/web/admin-spa/src/composables/useChartConfig.js b/web/admin-spa/src/composables/useChartConfig.js index 351ceb3a..4cd403b9 100644 --- a/web/admin-spa/src/composables/useChartConfig.js +++ b/web/admin-spa/src/composables/useChartConfig.js @@ -1,5 +1,4 @@ import { Chart } from 'chart.js/auto' -import i18n from '@/i18n' export function useChartConfig() { // 设置Chart.js默认配置 @@ -52,9 +51,7 @@ export function useChartConfig() { label += ': ' } if (context.parsed.y !== null) { - const localeMap = { 'zh-cn': 'zh-CN', 'zh-tw': 'zh-TW', en: 'en-US' } - const currentLocale = localeMap[i18n.global.locale.value] || 'en-US' - label += new Intl.NumberFormat(currentLocale).format(context.parsed.y) + label += new Intl.NumberFormat('zh-CN').format(context.parsed.y) } return label } diff --git a/web/admin-spa/src/composables/useConfirm.js b/web/admin-spa/src/composables/useConfirm.js index 15041bfb..d08fbdde 100644 --- a/web/admin-spa/src/composables/useConfirm.js +++ b/web/admin-spa/src/composables/useConfirm.js @@ -1,22 +1,16 @@ import { ref } from 'vue' -import i18n from '@/i18n' const showConfirmModal = ref(false) const confirmOptions = ref({ title: '', message: '', - confirmText: i18n.global.t('common.confirmModal.continue'), - cancelText: i18n.global.t('common.confirmModal.cancel') + confirmText: '继续', + cancelText: '取消' }) const confirmResolve = ref(null) export function useConfirm() { - const showConfirm = ( - title, - message, - confirmText = i18n.global.t('common.confirmModal.continue'), - cancelText = i18n.global.t('common.confirmModal.cancel') - ) => { + const showConfirm = (title, message, confirmText = '继续', cancelText = '取消') => { return new Promise((resolve) => { confirmOptions.value = { title, diff --git a/web/admin-spa/src/config/apiStats.js b/web/admin-spa/src/config/apiStats.js index e6999e8f..f581b1fe 100644 --- a/web/admin-spa/src/config/apiStats.js +++ b/web/admin-spa/src/config/apiStats.js @@ -1,8 +1,6 @@ // API Stats 专用 API 客户端 // 与管理员 API 隔离,不需要认证 -import i18n from '@/i18n' - class ApiStatsClient { constructor() { this.baseURL = window.location.origin @@ -28,9 +26,7 @@ class ApiStatsClient { const data = await response.json() if (!response.ok) { - throw new Error( - data.message || i18n.global.t('common.errors.requestFailed', { status: response.status }) - ) + throw new Error(data.message || `请求失败: ${response.status}`) } return data diff --git a/web/admin-spa/src/config/app.js b/web/admin-spa/src/config/app.js index 2f65accf..23fbe856 100644 --- a/web/admin-spa/src/config/app.js +++ b/web/admin-spa/src/config/app.js @@ -4,7 +4,7 @@ export const APP_CONFIG = { basePath: import.meta.env.VITE_APP_BASE_URL || (import.meta.env.DEV ? '/admin/' : '/web/admin/'), // 应用标题 - title: import.meta.env.VITE_APP_TITLE || 'Claude Relay Service - Admin Panel', + title: import.meta.env.VITE_APP_TITLE || 'Claude Relay Service - 管理后台', // 是否为开发环境 isDev: import.meta.env.DEV, diff --git a/web/admin-spa/src/i18n/index.js b/web/admin-spa/src/i18n/index.js deleted file mode 100644 index 31dd2b49..00000000 --- a/web/admin-spa/src/i18n/index.js +++ /dev/null @@ -1,90 +0,0 @@ -import { createI18n } from 'vue-i18n' -import zhCn from './locales/zh-cn.js' -import zhTw from './locales/zh-tw.js' -import en from './locales/en.js' - -// 获取浏览器语言设置 -function getBrowserLocale() { - const navigatorLocale = navigator.languages ? navigator.languages[0] : navigator.language - - if (!navigatorLocale) { - return 'zh-cn' - } - - const trimmedLocale = navigatorLocale.trim().split(';')[0].toLowerCase() - - if (trimmedLocale.includes('zh')) { - if ( - trimmedLocale.includes('tw') || - trimmedLocale.includes('hk') || - trimmedLocale.includes('mo') - ) { - return 'zh-tw' - } - return 'zh-cn' - } - - if (trimmedLocale.includes('en')) { - return 'en' - } - - return 'zh-cn' // 默认简体中文 -} - -// 获取保存的语言设置或浏览器语言 -const savedLocale = localStorage.getItem('app-locale') -const defaultLocale = savedLocale || getBrowserLocale() - -// 创建一个函数来获取本地化的语言信息 -export function getSupportedLocalesWithI18n(t) { - return { - 'zh-cn': { - name: t('common.languageSwitch.zhCnName'), - flag: t('common.languageSwitch.zhCnFlag'), - shortName: t('common.languageSwitch.zhCnFlag') - }, - 'zh-tw': { - name: t('common.languageSwitch.zhTwName'), - flag: t('common.languageSwitch.zhTwFlag'), - shortName: t('common.languageSwitch.zhTwFlag') - }, - en: { - name: t('common.languageSwitch.enName'), - flag: t('common.languageSwitch.enFlag'), - shortName: t('common.languageSwitch.enFlag') - } - } -} - -// 保持原有的SUPPORTED_LOCALES作为默认值,用于不依赖i18n的场景 -export const SUPPORTED_LOCALES = { - 'zh-cn': { - name: '简体中文', - flag: '简', - shortName: '简' - }, - 'zh-tw': { - name: '繁體中文', - flag: '繁', - shortName: '繁' - }, - en: { - name: 'English', - flag: 'EN', - shortName: 'EN' - } -} - -export const i18n = createI18n({ - legacy: false, // 使用 Composition API 模式 - locale: defaultLocale, - fallbackLocale: 'zh-cn', - messages: { - 'zh-cn': zhCn, - 'zh-tw': zhTw, - en: en - }, - globalInjection: true // 全局注入 $t 函数 -}) - -export default i18n diff --git a/web/admin-spa/src/i18n/locales/en.js b/web/admin-spa/src/i18n/locales/en.js deleted file mode 100644 index 62ffbd70..00000000 --- a/web/admin-spa/src/i18n/locales/en.js +++ /dev/null @@ -1,3005 +0,0 @@ -export default { - layout: { - mainLayout: { - comments: { - topNavigation: 'Top Navigation', - mainContentArea: 'Main Content Area', - tabBar: 'Tab Bar', - contentArea: 'Content Area' - }, - routing: { - routeChangeError: 'Route change failed:', - routeNotFound: 'Route not found', - defaultToTab: 'Default to dashboard' - } - }, - tabBar: { - tabs: { - dashboard: { - name: 'Dashboard', - shortName: 'Dashboard' - }, - apiKeys: { - name: 'API Keys', - shortName: 'API' - }, - accounts: { - name: 'Account Management', - shortName: 'Accounts' - }, - userManagement: { - name: 'User Management', - shortName: 'Users' - }, - tutorial: { - name: 'Tutorial', - shortName: 'Tutorial' - }, - settings: { - name: 'System Settings', - shortName: 'Settings' - } - } - } - }, - common: { - save: 'Save', - cancel: 'Cancel', - confirm: 'Confirm', - loading: 'Loading...', - edit: 'Edit', - delete: 'Delete', - create: 'Create', - update: 'Update', - search: 'Search', - reset: 'Reset', - locale: 'en', - toastNotification: { - defaultTitles: { - success: 'Success', - error: 'Error', - warning: 'Warning', - info: 'Information' - } - }, - errors: { - loadDashboardFailed: 'Failed to load dashboard data', - loadUsageTrendFailed: 'Failed to load usage trend', - loadModelStatsFailed: 'Failed to load model statistics', - loadApiKeysTrendFailed: 'Failed to load API Keys trend', - createClaudeConsoleAccountFailed: 'Failed to create Claude Console account', - createAzureOpenAIAccountFailed: 'Failed to create Azure OpenAI account', - updateClaudeConsoleAccountFailed: 'Failed to update Claude Console account', - updateAzureOpenAIAccountFailed: 'Failed to update Azure OpenAI account', - generateSetupTokenUrlFailed: 'Failed to generate Setup Token URL', - exchangeSetupTokenFailed: 'Failed to exchange Setup Token authorization code', - allApiKeysInvalid: 'All API Keys are invalid', - loadOemSettingsFailed: 'Failed to load OEM settings', - getApiKeyStatsFailed: 'Failed to get API Key statistics', - getTagsFailed: 'Failed to get tags', - requestFailed: 'Request failed: {status}', - loadSupportedClientsFailed: 'Failed to load supported clients' - }, - system: { - status: { - normal: 'Normal', - abnormal: 'Abnormal' - } - }, - confirmDialog: { - confirm: 'Confirm', - cancel: 'Cancel' - }, - confirmModal: { - continue: 'Continue', - cancel: 'Cancel' - }, - themeToggle: { - light: { - label: 'Light Mode', - shortLabel: 'Light' - }, - dark: { - label: 'Dark Mode', - shortLabel: 'Dark' - }, - auto: { - label: 'Follow System', - shortLabel: 'Auto' - }, - toggleTheme: 'Toggle Theme', - clickToSwitch: 'Click to switch theme' - }, - logoTitle: { - logoAlt: 'Logo' - }, - languageSwitch: { - zhCnName: 'Simplified Chinese', - zhTwName: 'Traditional Chinese', - enName: 'English', - zhCnFlag: 'CN', - zhTwFlag: 'TW', - enFlag: 'EN' - }, - accountSelector: { - searchPlaceholder: 'Search account name...', - schedulingGroups: 'Scheduling Groups', - membersUnit: ' members', - claudeOAuthAccounts: 'Claude OAuth Dedicated Accounts', - oauthAccounts: 'OAuth Dedicated Accounts', - claudeConsoleAccounts: 'Claude Console Dedicated Accounts', - noResultsFound: 'No matching accounts found', - selectAccount: 'Please select an account', - useSharedPool: 'Use shared account pool', - accountStatus: { - unknown: 'Unknown', - unauthorized: 'Unauthorized', - tokenError: 'Token Error', - pending: 'Pending', - rateLimited: 'Rate Limited', - error: 'Error', - active: 'Active' - }, - dateFormat: { - today: 'Created today', - yesterday: 'Created yesterday', - daysAgo: ' days ago' - } - }, - customDropdown: { - placeholder: 'Please select' - }, - // Common time and errors - time: { - justNow: 'Just now', - minutesAgo: '{minutes} minutes ago', - hoursAgo: '{hours} hours ago', - daysAgo: '{days} days ago' - }, - - // API Keys page buttons - model: 'Model', - stats: 'Stats' - }, - language: { - zh: '简体中文', - 'zh-tw': '繁體中文', - en: 'English', - current: 'Current Language', - switch: 'Switch Language' - }, - header: { - adminPanel: 'Admin Panel', - userMenu: 'User Menu', - logout: 'Logout', - settings: 'Settings', - - // Version related - currentVersion: 'Current Version', - newVersionAvailable: 'New version available', - newVersion: 'New Version', - hasUpdate: 'New Version Available', - viewUpdate: 'View Update', - checkingUpdate: 'Checking for updates...', - alreadyLatest: 'Already the latest version', - checkUpdate: 'Check Update', - - // User menu items - changeAccountInfo: 'Change Account Info', - - // Change password modal - changePasswordModal: { - title: 'Change Account Information', - currentUsername: 'Current Username', - currentUsernameHint: 'Current username, enter new username to modify', - newUsername: 'New Username', - newUsernamePlaceholder: 'Enter new username (leave empty to keep unchanged)', - newUsernameHint: 'Leave empty to keep username unchanged', - currentPassword: 'Current Password', - currentPasswordPlaceholder: 'Please enter current password', - newPassword: 'New Password', - newPasswordPlaceholder: 'Please enter new password', - newPasswordHint: 'Password must be at least 8 characters', - confirmPassword: 'Confirm New Password', - confirmPasswordPlaceholder: 'Please enter new password again', - saving: 'Saving...', - save: 'Save Changes', - - // Messages - passwordMismatch: 'Passwords do not match', - passwordTooShort: 'New password must be at least 8 characters', - accountInfoChangeSuccess: 'Account information changed successfully, please log in again', - passwordChangeSuccess: 'Password changed successfully, please log in again', - changeFailed: 'Change failed', - changePasswordFailed: 'Failed to change password' - }, - - // Logout - logoutConfirm: 'Are you sure you want to logout?', - logoutSuccess: 'Logged out safely' - }, - apiStats: { - title: 'API Key Usage Statistics', - tutorialTitle: 'Tutorial', - userLogin: 'User Login', - adminPanel: 'Admin Panel', - statsQuery: 'Statistics Query', - tutorial: 'Tutorial', - timeRange: 'Statistics Time Range', - today: 'Today', - thisMonth: 'This Month', - - // API Key Input - usageStatsQuery: 'Usage Statistics Query', - apiKeyDescription: 'Query your API Key usage and statistical data', - enterApiKey: 'Enter your API Key', - enterApiKeys: 'Enter your API Keys (one per line or comma separated)', - singleMode: 'Single', - aggregateMode: 'Aggregate', - singleModeTitle: 'Single Mode', - aggregateModeTitle: 'Aggregate Mode', - queryButton: 'Query', - - // Stats Overview - batchQuerySummary: 'Batch Query Summary', - apiKeyInfo: 'API Key Information', - queryKeysCount: 'Query Keys Count', - activeKeysCount: 'Active Keys Count', - totalRequests: 'Total Requests', - totalTokens: 'Total Tokens', - totalCost: 'Total Cost', - individual: '', - - // Aggregated Stats Card - usageRatio: 'Usage Ratio', - requests: ' requests', - otherKeys: 'Other', - keys: 'Keys', - - // Model Usage Stats - modelUsageStats: 'Model Usage Statistics', - loadingModelStats: 'Loading model statistics...', - requestCount: ' requests', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - cacheCreateTokens: 'Cache Create', - cacheReadTokens: 'Cache Read', - noModelData: 'No {period} model usage data available', - - // Token Distribution - tokenDistribution: 'Token Usage Distribution', - inputToken: 'Input Tokens', - outputToken: 'Output Tokens', - cacheCreateToken: 'Cache Create Tokens', - cacheReadToken: 'Cache Read Tokens', - - // Limit Config - limitConfig: 'Limit Configuration', - limitConfigAggregate: 'Limit Configuration (Aggregate Query Mode)', - apiKeysOverview: 'API Keys Overview', - totalKeys: 'Total Keys', - activeKeys: 'Active Keys', - aggregatedStats: 'Aggregated Statistics', - dailyLimit: 'Daily Limit', - monthlyLimit: 'Monthly Limit', - usageToday: 'Usage Today', - usageThisMonth: 'Usage This Month', - remaining: 'Remaining', - - // Stats Overview - Additional keys - name: 'Name', - status: 'Status', - permissions: 'Permissions', - createdAt: 'Created At', - expiresAt: 'Expires At', - active: 'Active', - inactive: 'Inactive', - notActivated: 'Not Activated', - expired: 'Expired', - neverExpires: 'Never Expires', - allModels: 'All Models', - unknown: 'Unknown', - none: 'None', - formatError: 'Format Error', - usageStatsOverview: 'Usage Statistics Overview', - keyContribution: 'Key Contribution Ratio', - firstUseDays: 'Expires {days} days after first use', - todayRequests: 'Today Requests', - todayTokens: 'Today Tokens', - todayCost: 'Today Cost', - todayInputTokens: 'Today Input Tokens', - monthlyRequests: 'Monthly Requests', - monthlyTokens: 'Monthly Tokens', - monthlyCost: 'Monthly Cost', - monthlyInputTokens: 'Monthly Input Tokens', - - // Limit Config - Additional keys - dailyCostLimit: 'Daily Cost Limit', - concurrencyLimit: 'Concurrency Limit', - modelLimit: 'Model Limit', - clientLimit: 'Client Limit', - restrictedModelsCount: 'Restricted {count} models', - allowAllModels: 'Allow All Models', - restrictedClientsCount: 'Restricted {count} clients', - allowAllClients: 'Allow All Clients', - detailedLimitInfo: 'Detailed Limit Information', - restrictedModelsList: 'Restricted Models List', - restrictedModelsNote: 'This API Key cannot access the models listed above', - allowedClientsList: 'Allowed Clients', - allowedClientsNote: 'This API Key can only be used by the clients listed above', - timeWindowLimit: 'Time Window Limit', - aggregateStatsNote: - 'Each API Key has independent limit settings, individual limit configurations are not shown in aggregate mode', - aggregateStatsSummary: 'Aggregate Statistics Summary', - invalidKeysCount: '{count} invalid API Keys', - orRelationshipRequests: - 'Request count and cost limits have an "OR" relationship, rate limiting is triggered when either limit is reached', - orRelationshipTokens: - 'Request count and token usage have an "OR" relationship, rate limiting is triggered when either limit is reached', - onlyRequestsLimit: 'Only request count is limited', - - // Token Distribution - Additional keys - totalAmount: 'Total', - todayTotal: 'Today Total', - monthlyTotal: 'Monthly Total', - - // Additional missing keys - usageRatioOnlyInMultiMode: 'Usage ratio is only displayed in multi-key query mode', - noData: 'No Data', - - // ApiKeyInput placeholders and texts - apiKeyPlaceholder: 'Please enter your API Key (cr_...)', - apiKeysPlaceholder: - 'Please enter your API Keys, supporting the following formats:\ncr_xxx\ncr_yyy\nor\ncr_xxx, cr_yyy', - clearInput: 'Clear Input', - securityNoticeSingle: - 'Your API Key is only used to query your own statistical data and will not be stored or used for other purposes', - securityNoticeMulti: - 'Your API Keys are only used to query statistical data and will not be stored. Some individual information will not be displayed in aggregate mode.', - multiKeyTip: - 'Tip: Supports querying up to 30 API Keys simultaneously. Use Ctrl+Enter for quick query.', - errors: { - queryStatsFailed: 'Failed to query statistics, please check your API Key', - enterAtLeastOneKey: 'Please enter at least one valid API Key', - batchQueryFailed: 'Batch query failed', - batchModelStatsFailed: 'Failed to load batch model statistics', - loadModelStatsFailed: 'Failed to load model statistics', - allInvalidKeys: 'All API Keys are invalid' - } - }, - - // Login page - login: { - title: 'Admin Panel', - username: 'Username', - usernamePlaceholder: 'Please enter username', - password: 'Password', - passwordPlaceholder: 'Please enter password', - loginButton: 'Login', - loggingIn: 'Logging in...', - loginFailed: 'Login failed', - loginFailedCheck: 'Login failed, please check username and password' - }, - - // Dashboard page - dashboard: { - // Main stats cards - totalApiKeys: 'Total API Keys', - activeApiKeys: 'Active', - serviceAccounts: 'Service Accounts', - normalAccounts: 'Normal', - abnormalAccounts: 'Abnormal', - pausedAccounts: 'Paused', - rateLimitedAccounts: 'Rate Limited', - todayRequests: 'Today Requests', - totalRequests: 'Total Requests', - systemStatus: 'System Status', - uptime: 'Uptime', - - // Platform accounts tooltip - claudeAccount: 'Claude: {total} accounts (Normal: {normal})', - consoleAccount: 'Console: {total} accounts (Normal: {normal})', - geminiAccount: 'Gemini: {total} accounts (Normal: {normal})', - bedrockAccount: 'Bedrock: {total} accounts (Normal: {normal})', - openaiAccount: 'OpenAI: {total} accounts (Normal: {normal})', - azureOpenaiAccount: 'Azure OpenAI: {total} accounts (Normal: {normal})', - - // Token stats cards - todayToken: 'Today Tokens', - totalTokenConsumption: 'Total Token Consumption', - inputTokens: 'Input', - outputTokens: 'Output', - cacheCreateTokens: 'Cache Create', - cacheReadTokens: 'Cache Read', - - // Real-time metrics - realtimeRPM: 'Realtime RPM', - realtimeTPM: 'Realtime TPM', - requestsPerMinute: 'Requests per Minute', - tokensPerMinute: 'Tokens per Minute', - historicalData: 'Historical Data', - minutes: 'minutes', - // Uptime display formats - uptimeFormat: { - daysHours: '{days} days {hours} hours', - hoursMinutes: '{hours} hours {minutes} minutes', - minutes: '{minutes} minutes' - }, - - // Charts section - modelDistributionAndTrend: 'Model Usage Distribution & Token Usage Trends', - - // Date filter presets - today: 'Today', - yesterday: 'Yesterday', - dayBefore: 'Day before yesterday', - last7Days: 'Last 7 Days', - last30Days: 'Last 30 Days', - thisWeek: 'This Week', - lastWeek: 'Last Week', - thisMonth: 'This Month', - lastMonth: 'Last Month', - - // Granularity buttons - byDay: 'By Day', - byHour: 'By Hour', - - // Date picker - startDatePlaceholder: 'Start Date', - endDatePlaceholder: 'End Date', - dateSeparator: 'to', - maxHours24: 'Maximum 24 hours', - - // Auto refresh controls - autoRefresh: 'Auto Refresh', - refresh: 'Refresh', - refreshing: 'Refreshing', - refreshDataNow: 'Refresh data now', - - // Charts - tokenUsageDistribution: 'Token Usage Distribution', - detailedStatistics: 'Detailed Statistics', - noModelUsageData: 'No model usage data available', - - // Table headers - model: 'Model', - requestCount: 'Requests', - totalTokens: 'Total Tokens', - cost: 'Cost', - percentage: 'Percentage', - - // Trend charts - tokenUsageTrend: 'Token Usage Trend', - apiKeysUsageTrend: 'API Keys Usage Trend', - requestsCount: 'Requests Count', - tokenCount: 'Token Count', - totalApiKeysCount: 'Total {count} API Keys', - showingTop10: 'Total {count} API Keys, showing top 10 by usage', - - // Chart labels - inputTokensLabel: 'Input Tokens', - outputTokensLabel: 'Output Tokens', - cacheCreateTokensLabel: 'Cache Create Tokens', - cacheReadTokensLabel: 'Cache Read Tokens', - costLabel: 'Cost (USD)', - requestsLabel: 'Requests', - time: 'Time', - date: 'Date', - tokenQuantity: 'Token Quantity', - requestsQuantity: 'Requests Count', - - // Usage Trend component - usageTrend: { - title: 'Usage Trend', - granularity: { - byDay: 'By Day', - byHour: 'By Hour' - }, - periodOptions: { - last24Hours: '24 Hours', - last7Days: '7 Days', - last30Days: '30 Days', - recentDays: 'Last {days} Days' - }, - chartLabels: { - requests: 'Request Count', - tokens: 'Token Usage', - requestsAxis: 'Request Count', - tokensAxis: 'Token Usage' - } - }, - - // Model Distribution component - modelDistribution: { - title: 'Model Usage Distribution', - periods: { - daily: 'Today', - total: 'Total' - }, - noData: 'No model usage data available', - units: { - requests: 'requests', - tokens: 'tokens' - }, - chart: { - tooltip: { - requests: 'Requests', - tokens: 'Tokens' - } - } - }, - errors: { - rangeTooLongHour: 'For hourly granularity, date range cannot exceed 24 hours', - rangeTooLongDay: 'Date range cannot exceed 31 days', - rangeTooLongHourSwitched: 'Hourly range cannot exceed 24 hours, switched to last 24 hours' - } - }, - - // Accounts page - accounts: { - title: 'Account Management', - description: - 'Manage your Claude, Gemini, OpenAI and Azure OpenAI accounts and proxy configurations', - - // Filters and sorting - sortBy: 'Select Sort', - selectPlatform: 'Select Platform', - selectGroup: 'Select Group', - refresh: 'Refresh', - refreshTooltip: 'Refresh data (Ctrl/⌘+click to force refresh all caches)', - addAccount: 'Add Account', - - // Sort options - sortByName: 'Sort by Name', - sortByDailyTokens: 'Sort by Daily Tokens', - sortByDailyRequests: 'Sort by Daily Requests', - sortByTotalTokens: 'Sort by Total Tokens', - sortByLastUsed: 'Sort by Last Used', - - // Platform options - allPlatforms: 'All Platforms', - claudePlatform: 'Claude', - claudeConsolePlatform: 'Claude Console', - geminiPlatform: 'Gemini', - openaiPlatform: 'OpenAI', - azureOpenaiPlatform: 'Azure OpenAI', - bedrockPlatform: 'Bedrock', - - // Group options - allAccounts: 'All Accounts', - ungroupedAccounts: 'Ungrouped Accounts', - - // Loading states - loadingAccounts: 'Loading accounts...', - noAccounts: 'No Accounts', - noAccountsHint: 'Click the button above to add your first account', - - // Table headers - name: 'Name', - platformType: 'Platform/Type', - status: 'Status', - priority: 'Priority', - proxy: 'Proxy', - dailyUsage: 'Daily Usage', - sessionWindow: 'Session Window', - lastUsed: 'Last Used', - actions: 'Actions', - - // Account types - dedicated: 'Dedicated', - groupScheduling: 'Group Scheduling', - shared: 'Shared', - belongsToGroup: 'Belongs to group: {name}', - - // Platform labels - unknown: 'Unknown', - apiKey: 'API Key', - oauth: 'OAuth', - setup: 'Setup', - aws: 'AWS', - - // Account status - normal: 'Normal', - abnormal: 'Abnormal', - blocked: 'Blocked', - tempError: 'Temporary Error', - rateLimited: 'Rate Limited', - notSchedulable: 'Not Schedulable', - bound: 'Bound: {count} API Keys', - - // Proxy status - noProxy: 'No Proxy', - - // Usage statistics - requests: ' requests', - noData: 'No Data', - averageRpm: 'Average {rpm} RPM', - - // Session window tooltip - sessionWindowTooltip: { - title: 'Session window progress indicates the time progress of the 5-hour window', - normal: 'Normal: Requests processed normally', - warning: 'Warning: Approaching limit', - rejected: 'Rejected: Rate limit reached' - }, - - // Session window status - remaining: 'Remaining {time}', - ended: 'Ended', - - // Console quota - quotaProgress: 'Quota Progress', - remainingQuota: 'Remaining $${amount}', - reset: 'Reset {time}', - - // Mobile view labels - dailyUsageLabel: 'Daily Usage', - sessionWindowLabel: 'Session Window', - lastUsedLabel: 'Last Used', - proxyLabel: 'Proxy', - priorityLabel: 'Priority', - neverUsed: 'Never Used', - sessionWindowTooltipMobile: - 'Session window progress does not represent usage, it only shows the remaining time until the next 5-hour window', - - // Action buttons - resetStatus: 'Reset Status', - resetting: 'Resetting...', - resetStatusTooltip: 'Reset all abnormal statuses', - scheduling: 'Scheduling', - disabled: 'Disabled', - enableTooltip: 'Click to enable scheduling', - disableTooltip: 'Click to disable scheduling', - edit: 'Edit', - editTooltip: 'Edit account', - delete: 'Delete', - deleteTooltip: 'Delete account', - pause: 'Pause', - enable: 'Enable', - - // Time formatting - justNow: 'Just now', - minutesAgo: '{minutes} minutes ago', - hoursAgo: '{hours} hours ago', - daysAgo: '{days} days ago', - hoursAndMinutes: '{hours} hours {minutes} minutes', - hoursOnly: '{hours} hours', - minutesOnly: '{minutes} minutes', - daysAndHours: '{days} days {hours} hours', - daysOnly: '{days} days', - - // Rate limit time - rateLimitTime: '({time})', - - // Messages and confirmations - resetStatusConfirmTitle: 'Reset Account Status', - resetStatusConfirmMessage: - 'Are you sure you want to reset all abnormal statuses for this account? This will clear rate limit status, 401 error counts, and all other abnormal flags.', - resetStatusConfirmButton: 'Confirm Reset', - resetStatusCancelButton: 'Cancel', - statusResetSuccess: 'Account status has been reset', - statusResetFailed: 'Status reset failed', - - deleteAccountTitle: 'Delete Account', - deleteAccountMessage: - 'Are you sure you want to delete account "{name}"?\n\nThis action cannot be undone.', - deleteAccountButton: 'Delete', - deleteAccountCancel: 'Cancel', - cannotDeleteBoundAccount: - 'Cannot delete this account, {count} API Keys are bound to this account, please unbind all API Keys first', - accountDeleted: 'Account deleted', - deleteFailed: 'Delete failed', - - enabledScheduling: 'Scheduling enabled', - disabledScheduling: 'Scheduling disabled', - schedulingToggleFailed: 'Failed to toggle scheduling status', - unsupportedAccountType: 'This account type does not support scheduling control', - operationFailed: 'Operation failed', - - accountCreateSuccess: 'Account created successfully', - accountUpdateSuccess: 'Account updated successfully', - loadAccountsFailed: 'Failed to load accounts', - unsupportedAccountTypeReset: 'Unsupported account type', - - // Schedulable reasons - invalidApiKey: 'API Key invalid or expired (401 error)', - serviceOverload: 'Service overloaded (529 error)', - rateLimitTriggered: 'Rate limit triggered (429 error)', - authFailed: 'Authentication failed (401 error)', - manualStop: 'Manually stopped scheduling', - - // Account type display - claudeMax: 'Claude Max', - claudePro: 'Claude Pro', - claudeFree: 'Claude Free', - - // Platform display - openaiResponsesPlatform: 'OpenAI-Responses', - ccrPlatform: 'CCR' - }, - apiKeys: { - // Page title and description - title: 'API Keys Management', - description: 'Manage and monitor your API keys', - - // Tab navigation - activeTab: 'Active API Keys', - deletedTab: 'Deleted API Keys', - - // Toolbar and actions - refresh: 'Refresh', - refreshTooltip: 'Refresh API Keys list (Ctrl+click for force refresh)', - createNew: 'Create New Key', - bulkEdit: 'Edit Selected', - bulkDelete: 'Delete Selected', - - // Table headers - name: 'Name', - tags: 'Tags', - status: 'Status', - usageStats: 'Usage Statistics', - createdAt: 'Created At', - expiresAt: 'Expires At', - actions: 'Actions', - - // Filter options - timeRange: { - today: 'Today', - week: 'Last 7 Days', - month: 'This Month', - all: 'All Time' - }, - - // Status - active: 'Active', - disabled: 'Disabled', - expired: 'Expired', - expiringSoon: 'Expiring Soon', - neverExpire: 'Never Expire', - notActivated: 'Not Activated', - - // Usage statistics - dailyCost: 'Daily Cost', - totalCost: 'Total Cost', - dailyRequests: 'Daily Requests', - lastUsed: 'Last Used', - neverUsed: 'Never Used', - minutesAgo: '{minutes} minutes ago', - hoursAgo: '{hours} hours ago', - daysAgo: '{days} days ago', - justNow: 'Just now', - requests: 'requests', - - // Search and filter - searchPlaceholder: 'Search name...', - searchPlaceholderWithOwner: 'Search name or owner...', - allTags: 'All Tags', - noTags: 'No Tags', - - // Binding information - shared: 'Using Shared Pool', - dedicated: 'Dedicated', - consoleAccount: 'Console Account', - bindingWarning: '⚠️ Account not found', - - // Limits and quotas - dailyLimit: 'Daily Cost', - weeklyOpusLimit: 'Opus Weekly Cost', - remainingQuota: 'Remaining: ${amount}', - reset: 'Reset at {time}', - quotaProgress: 'Quota Progress', - - // Model statistics - modelStats: 'Model Usage Distribution', - modelStatsCount: '{count} models', - totalTokens: 'Total Tokens', - inputTokens: 'Input', - outputTokens: 'Output', - cacheCreate: 'Cache Create', - cacheRead: 'Cache Read', - totalRequests: 'Total Requests', - noModelData: 'No model usage data', - resetFilter: 'Refresh', - adjustTimeRange: 'Try adjusting the time range or click refresh to reload data', - - // Date filter - dateFilter: { - today: 'Today', - days7: '7 Days', - days30: '30 Days' - }, - - // Actions - viewDetails: 'View Detailed Statistics', - edit: 'Edit', - renew: 'Renew', - activate: 'Activate', - disable: 'Disable', - copy: 'Copy', - copyStatsLink: 'Copy Stats Page Link', - - // Pagination - totalRecords: 'Total {count} records', - pageSize: 'Show', - records: 'per page', - - // Empty states - noApiKeys: 'No API Keys', - noApiKeysHint: 'Click the button above to create your first API Key', - noDeletedKeys: 'No deleted API Keys', - noDeletedKeysHint: 'Deleted API Keys will appear here', - loading: 'Loading API Keys...', - loadingDeleted: 'Loading deleted API Keys...', - loadingModelStats: 'Loading model statistics...', - - // Deleted keys table - creator: 'Creator', - deletedBy: 'Deleted By', - deletedAt: 'Deleted At', - canRestore: 'Restore', - permanentDelete: 'Permanent Delete', - clearAllDeleted: 'Clear All Deleted', - - // User types - admin: 'Admin', - user: 'User', - unknown: 'Unknown', - system: 'System', - - // Confirmation dialogs - confirmDisable: - 'Are you sure to disable API Key "{name}"? All requests using this key will return 401 errors after disabled.', - confirmDelete: 'Are you sure to delete this API Key? This action cannot be undone.', - confirmBatchDelete: - 'Are you sure to delete selected {count} API Keys? This action cannot be undone.', - confirmRestore: 'Are you sure to restore this API Key? It can be used again after restoration.', - confirmPermanentDelete: - 'Are you sure to permanently delete this API Key? This action cannot be undone and all related data will be permanently deleted.', - confirmClearAll: - 'Are you sure to permanently delete all {count} deleted API Keys? This action cannot be undone and all related data will be permanently deleted.', - - // Success messages - keyActivated: 'API Key activated', - keyDisabled: 'API Key disabled', - keyDeleted: 'API Key deleted', - keyRestored: 'API Key successfully restored', - keyPermanentDeleted: 'API Key permanently deleted', - allDeletedCleared: 'All deleted API Keys cleared', - linkCopied: 'Stats page link copied', - expiryUpdated: 'Expiry time updated', - - // Error messages - selectKeysFirst: 'Please select API Keys to edit first', - loadFailed: 'Failed to load API Keys', - operationFailed: 'Operation failed', - copyFailed: 'Copy failed, please copy manually', - updateFailed: 'Update failed', - deleteFailed: 'Delete failed', - restoreFailed: 'Restore failed', - clearFailed: 'Clear failed', - - // Tooltips and helpers - editExpiry: 'Edit expiry time', - activationDays: 'Not activated ({days} days)', - boundTo: 'Bound to', - belongsToGroup: 'Belongs to group: {name}', - - // Batch operations - batchSuccess: 'Successfully processed {count} items', - batchPartialFail: '{failed} items failed to process', - batchAllFailed: 'All items failed to process', - - // Batch API Key Modal - batchApiKeyModal: { - title: 'Batch Creation Successful', - successMessage: 'Successfully created {count} API Keys', - importantReminder: 'Important Reminder', - warningMessage: - 'This is your only chance to see all API Keys. After closing this window, the system will no longer display the complete API Keys. Please download and save them immediately.', - - // Statistics cards - createdCount: 'Created Count', - baseName: 'Base Name', - permissionScope: 'Permission Scope', - expiryTime: 'Expiry Time', - - // Permission texts - permissions: { - all: 'All Services', - claude: 'Claude Only', - gemini: 'Gemini Only', - unknown: 'Unknown' - }, - - // Expiry time texts - neverExpire: 'Never Expires', - daysFormat: '{days} days', - weeksFormat: '{weeks} weeks', - monthsFormat: '{months} months', - yearsFormat: '{years} years', - - // Preview section - previewTitle: 'API Keys Preview', - hide: 'Hide', - show: 'Show', - preview: 'Preview', - maxDisplayNote: '(Maximum 10 displayed)', - moreKeysNote: '... and {count} more API Keys', - - // Action buttons - downloadAll: 'Download All API Keys', - alreadySaved: 'I Have Saved', - directCloseTooltip: 'Close Directly (Not Recommended)', - - // File info - fileFormatInfo: - 'The downloaded file is in text format (.txt), with one API Key per line. Please save the file in a secure location to avoid leakage.', - - // Confirmation dialogs - closeReminderTitle: 'Close Reminder', - closeReminderMessage: - 'Once closed, you will not be able to view these API Keys again. Please ensure you have downloaded and saved them properly.\n\nAre you sure you want to close?', - confirmCloseButton: 'Confirm Close', - goBackDownloadButton: 'Go Back to Download', - - directCloseTitle: 'Are you sure you want to close?', - directCloseMessage: - "You haven't downloaded the API Keys yet. Once closed, you will not be able to view them again.\n\nWe strongly recommend downloading them first.", - stillCloseButton: 'Close Anyway', - - directCloseFallbackMessage: - "You haven't downloaded the API Keys yet. Once closed, you will not be able to view them again.\n\nAre you sure you want to close?", - - // Success messages - downloadSuccess: 'API Keys file downloaded successfully' - }, - - // Expiry Edit Modal - expiryEditModal: { - title: 'Edit Expiry Time', - subtitle: 'Set new expiry time for "{name}"', - currentStatus: 'Current Status', - notActivated: 'Not Activated', - activationDaysHint: '(expires after {days} days when activated)', - neverExpire: 'Never Expire', - expired: 'Expired', - daysToExpire: 'expires in {days} days', - monthsToExpire: 'expires in {months} months', - activateNow: 'Activate Now', - activateButton: 'Activate Now (expires in {days} days)', - activationInfo: - 'Click to activate this API Key immediately, it will expire in {days} days after activation', - selectNewDuration: 'Select New Duration', - neverExpireOption: 'Never Expire', - oneDay: '1 Day', - sevenDays: '7 Days', - thirtyDays: '30 Days', - ninetyDays: '90 Days', - oneHundredEightyDays: '180 Days', - threeSixtyFiveDays: '1 Year', - twoYears: '2 Years', - custom: 'Custom', - selectDateAndTime: 'Select Date and Time', - selectFutureDateTime: 'Select a future date and time as the expiry time', - newExpiryTime: 'New Expiry Time', - cancel: 'Cancel', - saving: 'Saving...', - saveChanges: 'Save Changes', - activateConfirmTitle: 'Activate API Key', - activateConfirmMessage: - 'Are you sure you want to activate this API Key now? It will automatically expire after {days} days.', - confirmActivate: 'Confirm Activation', - confirmCancel: 'Cancel' - }, - - // Usage Detail Modal - usageDetailModal: { - title: 'Usage Statistics Details', - close: 'Close', - - // Statistics cards - totalRequests: 'Total Requests', - totalTokens: 'Total Tokens', - totalCost: 'Total Cost', - averageRate: 'Average Rate', - - // Today stats - today: 'Today', - todayRequests: '{count} requests', - todayTokens: '{count}', - todayCost: '${amount}', - - // Usage labels - times: ' requests', - - // Token distribution - tokenDistribution: 'Token Usage Distribution', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - cacheCreateTokens: 'Cache Create Tokens', - cacheReadTokens: 'Cache Read Tokens', - - // Limits section - limitSettings: 'Limit Settings', - dailyCostLimit: 'Daily Cost Limit', - concurrencyLimit: 'Concurrency Limit', - timeWindowLimit: 'Time Window Limit', - windowStatus: 'Window Status', - used: 'Used', - remainingQuota: 'Remaining: ${amount}', - - // Progress indicators - usedPercentage: 'Used {percentage}%' - }, - - // Batch Edit API Key Modal - batchEditApiKeyModal: { - title: 'Batch Edit API Keys ({count} items)', - - // Info section - infoTitle: 'Batch Edit Instructions', - infoContent: - 'The following settings will be applied to the selected {count} API Keys. Only filled or modified fields will be updated, blank fields will keep their original values.', - - // Tag operations - tagLabel: 'Tags (Batch Operations)', - tagOperations: { - replace: 'Replace Tags', - add: 'Add Tags', - remove: 'Remove Tags', - none: "Don't Modify Tags" - }, - - // Tag status texts - newTagsList: 'New tag list:', - tagsToAdd: 'Tags to add:', - tagsToRemove: 'Tags to remove:', - clickToSelectTags: 'Click to select existing tags:', - createNewTag: 'Create new tag:', - inputNewTagPlaceholder: 'Enter new tag name', - - // Rate limit settings - rateLimitTitle: 'Rate Limit Settings', - rateLimitWindow: 'Time Window (minutes)', - rateLimitRequests: 'Request Limit', - rateLimitCost: 'Cost Limit (USD)', - noModifyPlaceholder: "Don't modify", - - // Daily cost limit - dailyCostLimit: 'Daily Cost Limit (USD)', - dailyCostLimitPlaceholder: "Don't modify (0 means unlimited)", - - // Weekly Opus cost limit - weeklyOpusCostLimit: 'Opus Model Weekly Cost Limit (USD)', - weeklyOpusCostLimitPlaceholder: "Don't modify (0 means unlimited)", - opusLimitDescription: - 'Set weekly cost limit for Opus model (Monday to Sunday), only for Claude official accounts', - - // Concurrency limit - concurrencyLimit: 'Concurrency Limit', - concurrencyLimitPlaceholder: "Don't modify (0 means unlimited)", - - // Active status - activeStatus: 'Active Status', - statusOptions: { - active: 'Active', - disabled: 'Disabled', - noChange: "Don't modify" - }, - - // Service permissions - servicePermissions: 'Service Permissions', - permissionOptions: { - noChange: "Don't modify", - all: 'All Services', - claude: 'Claude Only', - gemini: 'Gemini Only', - openai: 'OpenAI Only' - }, - - // Account binding - accountBinding: 'Dedicated Account Binding', - refreshAccounts: 'Refresh Accounts', - refreshing: 'Refreshing...', - - claudeAccount: 'Claude Dedicated Account', - geminiAccount: 'Gemini Dedicated Account', - openaiAccount: 'OpenAI Dedicated Account', - bedrockAccount: 'Bedrock Dedicated Account', - - accountOptions: { - noChange: "Don't modify", - sharedPool: 'Use shared account pool', - groupPrefix: 'Group - ' - }, - - // Optgroup labels - optgroupLabels: { - accountGroups: 'Account Groups', - dedicatedAccounts: 'Dedicated Accounts' - }, - - // Buttons - cancel: 'Cancel', - saving: 'Saving...', - batchSave: 'Batch Save', - - // Messages - refreshAccountsSuccess: 'Account list refreshed', - refreshAccountsFailed: 'Failed to refresh account list', - batchEditSuccess: 'Successfully batch edited {count} API Keys', - batchEditPartialFail: '{failedCount} edits failed:\n{errors}', - batchEditAllFailed: 'All API Keys edit failed', - batchEditFailed: 'Batch edit failed', - batchEditErrorLog: 'Batch edit API Keys failed:' - }, - - // Edit API Key Modal - editApiKeyModal: { - title: 'Edit API Key', - - // Basic Info - name: 'Name', - namePlaceholder: 'Please enter API Key name', - nameHint: 'Used to identify the purpose of this API Key', - - // Owner - owner: 'Owner', - adminLabel: '- Admin', - ownerHint: - 'Assign this API Key to specified user or admin, admin assignment is not limited by user API Key count', - - // Tags - tags: 'Tags', - selectedTags: 'Selected tags:', - clickToSelectTags: 'Click to select existing tags:', - createNewTag: 'Create new tag:', - newTagPlaceholder: 'Enter new tag name', - tagsHint: 'Used to mark different teams or purposes, convenient for filtering and management', - - // Rate Limit Settings - rateLimitTitle: 'Rate Limit Settings (Optional)', - rateLimitWindow: 'Time Window (minutes)', - rateLimitRequests: 'Request Limit', - rateLimitCost: 'Cost Limit (USD)', - rateLimitWindowHint: 'Time period unit', - rateLimitRequestsHint: 'Max requests in window', - rateLimitCostHint: 'Max cost in window', - noLimit: 'Unlimited', - - // Usage Examples - usageExamples: '💡 Usage Examples', - example1: 'Example 1: Time window=60, Requests=1000 → Max 1000 requests per 60 minutes', - example2: 'Example 2: Time window=1, Cost=0.1 → Max $0.1 cost per minute', - example3: - 'Example 3: Window=30, Requests=50, Cost=5 → 50 requests and max $5 cost per 30 minutes', - - // Cost Limits - dailyCostLimit: 'Daily Cost Limit (USD)', - dailyCostLimitPlaceholder: '0 means unlimited', - dailyCostHint: - 'Set daily cost limit for this API Key, requests will be rejected when exceeded, 0 or empty means unlimited', - weeklyOpusCostLimit: 'Opus Model Weekly Cost Limit (USD)', - weeklyOpusHint: - 'Set weekly cost limit for Opus model (Monday to Sunday), Claude official accounts only, 0 or empty means unlimited', - custom: 'Custom', - - // Concurrency - concurrencyLimit: 'Concurrency Limit', - concurrencyLimitPlaceholder: '0 means unlimited', - concurrencyHint: 'Set maximum concurrent requests this API Key can handle', - - // Active Status - activeStatus: 'Activate Account', - activeStatusHint: - 'Unchecking will disable this API Key, pause all requests, client returns 401 error', - - // Service Permissions - servicePermissions: 'Service Permissions', - allServices: 'All Services', - claudeOnly: 'Claude Only', - geminiOnly: 'Gemini Only', - openaiOnly: 'OpenAI Only', - permissionsHint: 'Control which services this API Key can access', - - // Account Binding - accountBinding: 'Dedicated Account Binding', - refreshAccounts: 'Refresh Accounts', - refreshing: 'Refreshing...', - claudeAccount: 'Claude Dedicated Account', - geminiAccount: 'Gemini Dedicated Account', - openaiAccount: 'OpenAI Dedicated Account', - bedrockAccount: 'Bedrock Dedicated Account', - useSharedPool: 'Use Shared Account Pool', - selectClaudeAccount: 'Please select Claude account', - selectGeminiAccount: 'Please select Gemini account', - selectOpenaiAccount: 'Please select OpenAI account', - selectBedrockAccount: 'Please select Bedrock account', - accountBindingHint: 'Modifying binding account will affect this API Key request routing', - - // Model Restrictions - enableModelRestriction: 'Enable Model Restrictions', - restrictedModels: 'Restricted Models List', - noRestrictedModels: 'No restricted models', - allCommonModelsRestricted: 'All common models are in restricted list', - addRestrictedModelPlaceholder: 'Enter model name, press Enter to add', - modelRestrictionHint: - 'Set models that this API Key cannot access, e.g.: claude-opus-4-20250514', - - // Client Restrictions - enableClientRestriction: 'Enable Client Restrictions', - allowedClients: 'Allowed Clients', - clientRestrictionHint: 'Check clients that are allowed to use this API Key', - - // Buttons - cancel: 'Cancel', - save: 'Save Changes', - saving: 'Saving...', - - // Messages - costLimitConfirmTitle: 'Cost Limit Reminder', - costLimitConfirmMessage: - 'You set a time window but cost limit is 0, which means no cost limit.\n\nContinue?', - costLimitConfirmContinue: 'Continue Save', - costLimitConfirmBack: 'Go Back to Modify', - refreshAccountsSuccess: 'Account list refreshed', - refreshAccountsFailed: 'Failed to refresh account list', - updateFailed: 'Update failed' - }, - - // Renew API Key Modal - renewApiKeyModal: { - title: 'Renew API Key', - apiKeyInfo: 'API Key Information', - currentExpiry: 'Current Expiry Time: ', - neverExpires: 'Never expires', - renewDuration: 'Renew Duration', - extend7Days: 'Extend 7 days', - extend30Days: 'Extend 30 days', - extend90Days: 'Extend 90 days', - extend180Days: 'Extend 180 days', - extend365Days: 'Extend 365 days', - customDate: 'Custom date', - setPermanent: 'Set to never expire', - newExpiry: 'New expiry time: ', - cancel: 'Cancel', - renewing: 'Renewing...', - confirmRenew: 'Confirm Renew', - renewSuccess: 'API Key renewed successfully', - renewFailed: 'Renewal failed' - }, - - // New API Key Modal - newApiKeyModal: { - title: 'API Key Created Successfully', - subtitle: 'Please save your API Key securely', - directCloseTooltip: 'Close directly (not recommended)', - - // Warning section - warningTitle: 'Important Notice', - warningMessage: - 'This is the only chance to see the complete API Key. After closing this window, the system will no longer display the complete API Key. Please copy and save it immediately.', - - // Field labels - apiKeyName: 'API Key Name', - remarks: 'Remarks', - noDescription: 'No description', - apiKey: 'API Key', - - // Visibility toggle - hideApiKey: 'Hide API Key', - showFullApiKey: 'Show Full API Key', - visibilityHint: - 'Click the eye icon to toggle display mode, use the button below to copy the complete API Key', - - // Buttons - copyApiKey: 'Copy API Key', - alreadySaved: 'I Have Saved It', - - // Confirmation dialogs - closeReminderTitle: 'Close Reminder', - closeReminderMessage: - 'After closing, you will not be able to view the complete API Key again. Please ensure it has been saved securely.\n\nAre you sure you want to close?', - confirmClose: 'Confirm Close', - cancel: 'Cancel', - - directCloseTitle: 'Are you sure you want to close?', - directCloseMessage: - 'You have not saved the API Key yet. After closing, you will not be able to view it again.\n\nWe recommend copying the API Key before closing.', - stillClose: 'Close Anyway', - goBack: 'Go Back to Copy', - - directCloseFallback: - 'You have not saved the API Key yet. After closing, you will not be able to view it again.\n\nAre you sure you want to close?', - - // Success messages - apiKeyNotFound: 'API Key not found', - copySuccess: 'API Key copied to clipboard', - copyFailed: 'Copy failed, please copy manually' - }, - - // Create API Key Modal - createApiKeyModal: { - title: 'Create New API Key', - - // Create type section - createType: 'Create Type', - singleCreate: 'Single Create', - batchCreate: 'Batch Create', - batchCount: 'Create Count', - batchCountPlaceholder: 'Enter count (2-500)', - maxSupported: 'Max support 500', - batchHint: - 'When creating in batch, each Key name will automatically add number suffix, e.g.: {name}_1, {name}_2 ...', - - // Basic form fields - name: 'Name', - nameRequired: '*', - nameError: 'Please enter API Key name', - singleNamePlaceholder: 'Give your API Key a name', - batchNamePlaceholder: 'Enter base name (will auto add numbers)', - description: 'Description (optional)', - descriptionPlaceholder: 'Describe the purpose of this API Key...', - - // Tags section - tags: 'Tags', - selectedTags: 'Selected tags:', - clickToSelectTags: 'Click to select existing tags:', - createNewTag: 'Create new tag:', - newTagPlaceholder: 'Enter new tag name', - tagHint: 'Used to mark different teams or purposes for easy filtering', - - // Rate limit section - rateLimitTitle: 'Rate Limit Settings (optional)', - rateLimitWindow: 'Time Window (minutes)', - rateLimitRequests: 'Request Limit', - rateLimitCost: 'Cost Limit (USD)', - rateLimitWindowPlaceholder: 'Unlimited', - rateLimitRequestsPlaceholder: 'Unlimited', - rateLimitCostPlaceholder: 'Unlimited', - rateLimitWindowHint: 'Time period unit', - rateLimitRequestsHint: 'Max requests in window', - rateLimitCostHint: 'Max cost in window', - - // Rate limit examples - exampleTitle: '💡 Usage Examples', - example1: 'Example 1: Window=60, Requests=1000 → Max 1000 requests per 60 minutes', - example2: 'Example 2: Window=1, Cost=0.1 → Max $0.1 cost per minute', - example3: - 'Example 3: Window=30, Requests=50, Cost=5 → 50 requests and $5 cost per 30 minutes', - - // Cost limits - dailyCostLimit: 'Daily Cost Limit (USD)', - dailyCostLimitPlaceholder: '0 means unlimited', - dailyCostHint: - 'Set daily cost limit for this API Key, requests will be rejected when exceeded, 0 or empty means unlimited', - weeklyOpusCostLimit: 'Opus Model Weekly Cost Limit (USD)', - weeklyOpusCostLimitPlaceholder: '0 means unlimited', - weeklyOpusHint: - 'Set weekly cost limit for Opus model (Monday to Sunday), Claude official accounts only, 0 or empty means unlimited', - custom: 'Custom', - - // Concurrency limit - concurrencyLimit: 'Concurrency Limit (optional)', - concurrencyLimitPlaceholder: '0 means unlimited', - concurrencyHint: - 'Set maximum concurrent requests this API Key can handle, 0 or empty means unlimited', - - // Expiration settings - expirationSettings: 'Expiration Settings', - fixedTimeExpiry: 'Fixed Time Expiry', - activationExpiry: 'Activate After First Use', - fixedModeHint: - 'Fixed time mode: Key takes effect immediately after creation, expires at set time', - activationModeHint: - 'Key activates on first use, expires after set days from activation (suitable for batch sales)', - - // Expiration duration options - neverExpire: 'Never Expire', - '1d': '1 Day', - '7d': '7 Days', - '30d': '30 Days', - '90d': '90 Days', - '180d': '180 Days', - '365d': '365 Days', - customDate: 'Custom Date', - - // Activation mode - activationDays: 'Enter days', - daysUnit: 'days', - activationHint: 'Key will activate on first use, expires after {days} days from activation', - - // Expiry status - willExpireOn: 'Will expire on {date}', - - // Service permissions - servicePermissions: 'Service Permissions', - allServices: 'All Services', - claudeOnly: 'Claude Only', - geminiOnly: 'Gemini Only', - openaiOnly: 'OpenAI Only', - permissionHint: 'Control which services this API Key can access', - - // Account binding - dedicatedAccountBinding: 'Dedicated Account Binding (optional)', - refreshAccounts: 'Refresh Accounts', - refreshing: 'Refreshing...', - claudeDedicatedAccount: 'Claude Dedicated Account', - geminiDedicatedAccount: 'Gemini Dedicated Account', - openaiDedicatedAccount: 'OpenAI Dedicated Account', - bedrockDedicatedAccount: 'Bedrock Dedicated Account', - useSharedPool: 'Use shared account pool', - accountBindingHint: - 'After selecting dedicated account, this API Key will only use that account, otherwise use shared account pool', - selectClaudeAccount: 'Please select Claude account', - selectGeminiAccount: 'Please select Gemini account', - selectOpenaiAccount: 'Please select OpenAI account', - selectBedrockAccount: 'Please select Bedrock account', - - // Model restrictions - enableModelRestriction: 'Enable Model Restriction', - restrictedModelsList: 'Restricted Models List', - noRestrictedModels: 'No restricted models', - allCommonModelsRestricted: 'All common models are in the restricted list', - addRestrictedModelPlaceholder: 'Enter model name, press Enter to add', - modelRestrictionHint: 'Set models this API Key cannot access, e.g.: claude-opus-4-20250514', - - // Client restrictions - enableClientRestriction: 'Enable Client Restriction', - allowedClients: 'Allowed Clients', - - // Buttons - cancel: 'Cancel', - create: 'Create', - creating: 'Creating...', - - // Messages - batchCountError: 'Batch create count must be between 2-500', - costLimitConfirmTitle: 'Cost Limit Reminder', - costLimitConfirmMessage: - 'You set time window but cost limit is 0, which means no cost limit.\n\nContinue?', - costLimitConfirmContinue: 'Continue Creating', - costLimitConfirmBack: 'Go Back to Modify', - costLimitFallbackMessage: - 'You set time window but cost limit is 0, which means no cost limit.\nContinue?', - createSuccess: 'API Key created successfully', - batchCreateSuccess: 'Successfully created {count} API Keys', - createFailed: 'Create failed', - batchCreateFailed: 'Batch create failed', - refreshAccountsSuccess: 'Account list refreshed', - refreshAccountsFailed: 'Failed to refresh account list' - }, - - // Window Countdown - windowCountdown: { - expired: 'Window expired', - notStarted: 'Window not started', - minutes: 'minutes', - requests: 'Requests', - tokens: 'Tokens', - cost: 'Cost', - aboutToReset: 'About to reset', - minutesUntilReset: 'minutes until reset', - untilReset: 'until reset', - windowLimit: 'Window Limit', - hours: 'hours' - } - }, - - // User-related translations - user: { - // User Dashboard - dashboard: { - title: 'Dashboard Overview', - welcomeMessage: 'Welcome to your Claude Relay dashboard', - - // Navigation tabs - overview: 'Overview', - apiKeys: 'API Keys', - usageStats: 'Usage Stats', - - // Welcome section - welcome: 'Welcome', - - // Stats cards - activeApiKeys: 'Active API Keys', - deletedApiKeys: 'Deleted API Keys', - totalRequests: 'Total Requests', - inputTokens: 'Input Tokens', - totalCost: 'Total Cost', - - // Account information section - accountInformation: 'Account Information', - username: 'Username', - displayName: 'Display Name', - email: 'Email', - role: 'Role', - memberSince: 'Member Since', - lastLogin: 'Last Login', - notAvailable: 'N/A', - - // Messages - logout: 'Logout', - logoutSuccess: 'Logged out successfully', - logoutFailed: 'Logout failed', - loadProfileFailed: 'Failed to load user profile', - loadStatsFailed: 'Failed to load API keys stats' - }, - - // User API Keys Manager - userApiKeysManager: { - title: 'My API Keys', - description: 'Manage your API keys to access Claude Relay services', - loading: 'Loading API keys...', - warnings: { - maxKeysReached: - 'You have reached the maximum number of API keys ({maxApiKeys}). Please delete an existing key to create a new one.' - }, - status: { - deleted: 'Deleted', - noDescription: 'No description', - neverUsed: 'Never used' - }, - dateLabels: { - created: 'Created', - deleted: 'Deleted', - lastUsed: 'Last used', - expires: 'Expires' - }, - usage: { - requests: 'requests' - }, - actions: { - viewApiKey: 'View API Key', - deleteApiKey: 'Delete API Key' - }, - buttons: { - createApiKey: 'Create API Key', - delete: 'Delete' - }, - emptyState: { - title: 'No API keys', - description: 'Get started by creating your first API key.' - }, - confirmDelete: { - title: 'Delete API Key', - message: "Are you sure you want to delete '{name}'? This action cannot be undone." - }, - messages: { - loadFailed: 'Failed to load API keys', - deleteSuccess: 'API key deleted successfully', - deleteFailed: 'Failed to delete API key' - } - }, - - // User Login - login: { - title: 'User Sign In', - subtitle: 'Sign in to your account to manage your API keys', - username: 'Username', - password: 'Password', - usernamePlaceholder: 'Enter your username', - passwordPlaceholder: 'Enter your password', - signIn: 'Sign In', - signingIn: 'Signing In...', - adminLogin: 'Admin Login', - - // Validation and error messages - requiredFields: 'Please enter both username and password', - loginSuccess: 'Login successful!', - loginFailed: 'Login failed', - accountDisabled: 'Your account has been disabled' - }, - - // View API Key Modal - viewApiKeyModal: { - title: 'API Key Details', - fields: { - name: 'Name', - description: 'Description', - apiKey: 'API Key', - status: 'Status', - usageStatistics: 'Usage Statistics' - }, - apiKeyDisplay: { - notAvailable: 'Not available', - keyPreview: 'cr_****', - fullKeyNotice: 'Full API key is only shown when first created or regenerated' - }, - buttons: { - hide: 'Hide', - show: 'Show', - copy: 'Copy', - close: 'Close' - }, - status: { - active: 'Active', - disabled: 'Disabled' - }, - usageStats: { - requests: 'Requests', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - totalCost: 'Total Cost' - }, - timestamps: { - created: 'Created', - lastUsed: 'Last Used', - expires: 'Expires' - }, - messages: { - copySuccess: 'Copied to clipboard!', - copyFailed: 'Failed to copy to clipboard' - } - }, - - // User Management - management: { - title: 'User Management', - description: 'Manage users, their API keys, and view usage statistics', - refresh: 'Refresh', - - // Stats cards - totalUsers: 'Total Users', - activeUsers: 'Active Users', - totalApiKeys: 'Total API Keys', - totalCost: 'Total Cost', - - // Search and filters - searchPlaceholder: 'Search users...', - allRoles: 'All Roles', - user: 'User', - admin: 'Admin', - allStatus: 'All Status', - active: 'Active', - disabled: 'Disabled', - - // User list - users: 'Users', - loadingUsers: 'Loading users...', - noUsersFound: 'No users found', - noUsersMatch: 'No users match your search criteria.', - noUsersCreated: 'No users have been created yet.', - - // User info and actions - displayName: 'Display Name', - email: 'Email', - role: 'Role', - username: 'Username', - apiKeysCount: 'API keys', - lastLogin: 'Last login', - neverLoggedIn: 'Never logged in', - requests: 'requests', - totalCostLabel: 'total cost', - - // Action buttons and tooltips - viewUsageStats: 'View Usage Stats', - disableAllApiKeys: 'Disable All API Keys', - disableUser: 'Disable User', - enableUser: 'Enable User', - changeRole: 'Change Role', - - // Confirmation dialogs - disableUserTitle: 'Disable User', - enableUserTitle: 'Enable User', - disableUserMessage: - 'Are you sure you want to disable user "{username}"? This will prevent them from logging in.', - enableUserMessage: 'Are you sure you want to enable user "{username}"?', - disable: 'Disable', - enable: 'Enable', - - disableAllKeysTitle: 'Disable All API Keys', - disableAllKeysMessage: - 'Are you sure you want to disable all {count} API keys for user "{username}"? This will prevent them from using the service.', - disableKeys: 'Disable Keys', - - // Success messages - userDisabledSuccess: 'User disabled successfully', - userEnabledSuccess: 'User enabled successfully', - keysDisabledSuccess: 'Disabled {count} API keys', - - // Error messages - loadUsersError: 'Failed to load users', - toggleStatusError: 'Failed to toggleStatus', - disableKeysError: 'Failed to disableKeys' - }, - - // User Usage Stats Modal - usageStatsModal: { - title: 'Usage Statistics', - titleWithUser: 'Usage Statistics - {displayName}', - - // Time period selection - periodSelection: { - day: 'Last 24 Hours', - week: 'Last 7 Days', - month: 'Last 30 Days', - quarter: 'Last 90 Days' - }, - - // Loading state - loadingStats: 'Loading usage statistics...', - - // Summary cards - summaryCards: { - requests: 'Requests', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - totalCost: 'Total Cost' - }, - - // API Keys table - apiKeysTable: { - title: 'API Keys Usage', - headers: { - apiKey: 'API Key', - status: 'Status', - requests: 'Requests', - tokens: 'Tokens', - cost: 'Cost', - lastUsed: 'Last Used' - }, - status: { - active: 'Active', - disabled: 'Disabled' - }, - tokensFormat: { - input: 'In', - output: 'Out' - }, - never: 'Never' - }, - - // Usage trend chart - usageTrend: { - title: 'Usage Trend', - chartTitle: 'Usage Chart', - dailyTrends: 'Daily usage trends for {period} period', - chartNote: '(Chart integration can be added with Chart.js, D3.js, or similar library)' - }, - - // No data state - noData: { - title: 'No usage data', - description: "This user hasn't made any API requests in the selected period." - }, - - // Buttons - close: 'Close' - }, - - // Change Role Modal - changeRoleModal: { - title: 'Change User Role', - - // User info display - currentRole: 'Current: {role}', - - // Role selection form - newRole: 'New Role', - roles: { - user: 'User', - userDesc: 'Regular user with basic permissions', - admin: 'Administrator', - adminDesc: 'Full access to manage users and system' - }, - - // Warning messages - roleChangeWarning: { - title: 'Role Change Warning', - grantAdmin: - 'Granting admin privileges will give this user full access to the system, including the ability to manage other users and their API keys.', - removeAdmin: - 'Removing admin privileges will restrict this user to only managing their own API keys and viewing their own usage statistics.' - }, - - // Buttons - cancel: 'Cancel', - updateRole: 'Update Role', - updating: 'Updating...', - - // Success message - roleUpdated: 'User role updated to {role}' - }, - - // User Usage Statistics - userUsageStats: { - // Page header - title: 'Usage Statistics', - subtitle: 'View your API usage statistics and costs', - - // Time period selection - periodSelection: { - day: 'Last 24 Hours', - week: 'Last 7 Days', - month: 'Last 30 Days', - quarter: 'Last 90 Days' - }, - - // Loading state - loadingStats: 'Loading usage statistics...', - - // Statistics cards - statsCards: { - totalRequests: 'Total Requests', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - totalCost: 'Total Cost' - }, - - // Daily usage trend chart - usageTrend: { - title: 'Daily Usage Trend', - chartTitle: 'Usage Chart', - dailyTrendsDescription: 'Daily usage trends would be displayed here', - chartIntegrationNote: - '(Chart integration can be added with Chart.js, D3.js, or similar library)' - }, - - // Usage by model section - modelUsage: { - title: 'Usage by Model', - requests: 'requests', - requestsCount: '{count} requests' - }, - - // Usage by API key table - apiKeyUsage: { - title: 'Usage by API Key', - headers: { - apiKey: 'API Key', - requests: 'Requests', - inputTokens: 'Input Tokens', - outputTokens: 'Output Tokens', - cost: 'Cost', - status: 'Status' - }, - status: { - active: 'Active', - disabled: 'Disabled', - deleted: 'Deleted' - } - }, - - // No data state - noData: { - title: 'No usage data', - description: - "You haven't made any API requests yet. Create an API key and start using the service to see usage statistics." - }, - - // Error messages - loadFailed: 'Failed to load usage statistics' - }, - - // Create API Key Modal - createApiKeyModal: { - title: 'Create New API Key', - - // Form labels and placeholders - form: { - nameLabel: 'Name', - nameRequired: '*', - namePlaceholder: 'Enter API key name', - descriptionLabel: 'Description', - descriptionPlaceholder: 'Optional description' - }, - - // Button text - buttons: { - cancel: 'Cancel', - creating: 'Creating...', - createApiKey: 'Create API Key', - copy: 'Copy', - done: 'Done' - }, - - // Success state - success: { - title: 'API Key Created Successfully!', - warning: { - important: 'Important:', - message: "Copy your API key now. You won't be able to see it again!" - } - }, - - // Error and validation messages - validation: { - nameRequired: 'API key name is required' - }, - - errors: { - createFailed: 'Failed to create API key' - }, - - // Toast messages - messages: { - createSuccess: 'API key created successfully!', - copySuccess: 'API key copied to clipboard!', - copyFailed: 'Failed to copy to clipboard' - } - } - }, - - // Settings - settings: { - title: 'System Settings', - description: 'Website customization and notification configuration', - loading: 'Loading settings...', - - // Navigation tabs - branding: 'Branding Settings', - webhook: 'Notification Settings', - - // Branding settings - siteName: 'Site Name', - siteNameDescription: 'Brand identity', - siteNamePlaceholder: 'Claude Relay Service', - siteNameHint: 'Will be displayed in browser title and page header', - - siteIcon: 'Site Icon', - siteIconDescription: 'Favicon', - currentIcon: 'Current icon', - uploadIcon: 'Upload Icon', - removeIcon: 'Remove', - iconFormats: 'Supports .ico, .png, .jpg, .svg formats, max 350KB', - iconPreview: 'Icon preview', - validation: { - iconTooLarge: 'Icon file size must not exceed 350KB', - iconTypeNotSupported: 'Unsupported file type, please choose .ico, .png, .jpg or .svg' - }, - - adminEntry: 'Admin Entry', - adminEntryDescription: 'Login button display', - hideLoginButton: 'Hide login button', - showLoginButton: 'Show login button', - adminEntryHint: 'When hidden, users need to visit /admin/login directly', - - // Mobile card titles - siteNameCard: 'Site Name', - siteNameCardDesc: 'Customize your site brand name', - siteIconCard: 'Site Icon', - siteIconCardDesc: 'Upload custom icon or enter icon URL', - adminEntryCard: 'Admin Entry', - adminEntryCardDesc: 'Control login button visibility on homepage', - - // Action buttons - save: 'Save Settings', - saving: 'Saving...', - reset: 'Reset to Default', - lastUpdated: 'Last updated: {time}', - lastUpdatedMobile: 'Last updated: {time}', - - // Webhook settings - enableWebhook: 'Enable Webhook Notifications', - webhookDescription: 'When enabled, system will send notifications to configured platforms', - - // Notification types - notificationTypes: 'Notification Types', - accountAnomaly: 'Account Anomaly', - quotaWarning: 'Quota Warning', - systemError: 'System Error', - securityAlert: 'Security Alert', - accountAnomalyDesc: 'Account status anomalies, authentication failures, etc.', - quotaWarningDesc: 'API call quota insufficient warnings', - systemErrorDesc: 'System runtime errors and failures', - securityAlertDesc: 'Security-related alert notifications', - - // Notification platforms - notificationPlatforms: 'Notification Platforms', - addPlatform: 'Add Platform', - noPlatforms: 'No notification platforms configured, click "Add Platform" to add one', - enableSignature: 'Signature verification enabled', - testConnection: 'Test Connection', - edit: 'Edit', - delete: 'Delete', - - // Advanced settings - advancedSettings: 'Advanced Settings', - maxRetries: 'Max Retries', - retryDelay: 'Retry Delay (ms)', - timeout: 'Timeout (ms)', - - // Test notification - sendTestNotification: 'Send Test Notification', - - // Platform modal - addPlatformModal: 'Add Notification Platform', - editPlatformModal: 'Edit Notification Platform', - configurePlatform: 'Configure new Webhook notification channel', - updatePlatform: 'Configure and update Webhook notification channel', - - platformType: 'Platform Type', - platformName: 'Name', - platformNameOptional: '(Optional)', - platformNamePlaceholder: 'e.g.: Operations team, Dev test group', - - webhookUrl: 'Webhook URL', - webhookUrlRequired: '*', - webhookUrlPlaceholder: 'https://...', - editModeWarning: 'Platform type cannot be changed in edit mode', - - // Bark specific settings - deviceKey: 'Device Key', - deviceKeyPlaceholder: 'e.g.: aBcDeFgHiJkLmNoPqRsTuVwX', - deviceKeyHint: 'Check your push key in Bark App', - serverAddress: 'Server Address', - serverAddressOptional: '(Optional)', - serverAddressPlaceholder: 'Default: https://api.day.app/push', - notificationLevel: 'Notification Level', - notificationSound: 'Notification Sound', - notificationGroup: 'Notification Group', - notificationGroupOptional: '(Optional)', - notificationGroupPlaceholder: 'Default: claude-relay', - - // Notification level options - levelAuto: 'Auto (based on notification type)', - levelPassive: 'Passive', - levelActive: 'Default', - levelTimeSensitive: 'Time Sensitive', - levelCritical: 'Critical', - - // Sound options - soundAuto: 'Auto (based on notification type)', - soundDefault: 'Default', - soundAlarm: 'Alarm', - soundBell: 'Bell', - soundBirdsong: 'Birdsong', - soundElectronic: 'Electronic', - soundGlass: 'Glass', - soundHorn: 'Horn', - soundSilence: 'Silence', - - // Bark instructions - barkInstructions: [ - '1. Install Bark App on iPhone', - '2. Open the app to get your device key', - '3. Paste the key into the input box above' - ], - - // Signature settings - enableSignatureVerify: 'Enable Signature Verification', - signatureEnabled: 'Enabled', - signatureSecret: 'Signature Secret', - signatureSecretPlaceholder: 'SEC...', - - // Platform hints - wechatWorkHint: 'Get Webhook URL from WeChat Work group bot settings', - dingtalkHint: 'Get Webhook URL from DingTalk group bot settings', - feishuHint: 'Get Webhook URL from Feishu group bot settings', - slackHint: 'Get URL from Slack app Incoming Webhooks', - discordHint: 'Create Webhook in Discord server integration settings', - barkHint: 'Check your device key in Bark App', - customHint: 'Enter complete Webhook receiving URL', - - // Modal buttons - required: 'Required fields', - cancel: 'Cancel', - testing: 'Testing...', - saveChanges: 'Save Changes', - addPlatformBtn: 'Add Platform', - - // Success/error messages - loadSettingsFailed: 'Failed to load settings', - settingsSaved: 'Settings saved', - saveSettingsFailed: 'Failed to save settings', - oemSettingsSaved: 'OEM settings saved successfully', - oemSettingsSaveFailed: 'Failed to save OEM settings', - resetToDefault: 'Reset to default settings', - resetFailed: 'Reset failed', - confirmReset: - 'Are you sure you want to reset to default settings?\n\nThis will clear all custom site name and icon settings.', - - webhookConfigSaved: 'Configuration saved', - webhookConfigSaveFailed: 'Failed to save configuration', - getWebhookConfigFailed: 'Failed to get webhook configuration', - - platformAdded: 'Platform added', - platformUpdated: 'Platform updated', - platformDeleted: 'Platform deleted', - platformDeleteFailed: 'Delete failed', - confirmDeletePlatform: 'Are you sure you want to delete this platform?', - operationFailed: 'Operation failed', - - testSuccess: 'Test successful, webhook connection normal', - testFailed: 'Test failed', - testNotificationSent: 'Test notification sent', - testNotificationFailed: 'Send failed', - - // Form validation messages - enterBarkDeviceKey: 'Please enter Bark device key', - enterWebhookUrl: 'Please enter Webhook URL', - enterValidWebhookUrl: 'Please enter valid Webhook URL', - enterWebhookUrlFirst: 'Please enter Webhook URL first', - enterBarkDeviceKeyFirst: 'Please enter Bark device key first', - - // File upload - fileReadFailed: 'File read failed', - iconLoadFailed: 'Icon failed to load', - - // Platform names - platforms: { - wechatWork: 'WeChat Work', - dingtalk: 'DingTalk', - feishu: 'Feishu', - slack: 'Slack', - discord: 'Discord', - bark: 'Bark', - custom: 'Custom' - } - }, - - // AccountForm Component - accountForm: { - // Titles and modal - editAccount: 'Edit Account', - addAccount: 'Add Account', - - // Step indicators - stepBasicInfo: 'Basic Information', - stepAuthorization: 'Authorization', - - // Platform selection - platform: 'Platform', - platformClaude: 'Claude', - platformClaudeConsole: 'Claude Console', - platformGemini: 'Gemini', - platformOpenAI: 'OpenAI', - platformAzureOpenAI: 'Azure OpenAI', - platformBedrock: 'Bedrock', - - // Add methods - addMethod: 'Add Method', - addTypeSetupToken: 'Setup Token (Recommended)', - addTypeOAuth: 'OAuth Authorization', - addTypeManual: 'Manual Access Token Input', - - // Basic information fields - accountName: 'Account Name', - accountNamePlaceholder: 'Set an easily recognizable name for the account', - description: 'Description', - descriptionOptional: 'Description (Optional)', - descriptionPlaceholder: 'Account usage description...', - - // Account type - accountType: 'Account Type', - accountTypeShared: 'Shared Account', - accountTypeDedicated: 'Dedicated Account', - accountTypeGroup: 'Group Scheduling', - accountTypeDescription: - 'Shared: Available to all API Keys; Dedicated: Only for specific API Keys; Group: Join group for group scheduling', - - // Group selection - selectGroup: 'Select Group', - selectGroupRequired: 'Select Group *', - noGroupsAvailable: 'No groups available', - memberCount: 'members', - newGroup: 'New Group', - refreshGroups: 'Refresh Groups', - - // Gemini Project ID - projectId: 'Project ID', - projectIdOptional: 'Project ID (Optional)', - projectIdPlaceholder: 'e.g.: verdant-wares-464411-k9', - projectIdDescription: 'Google Cloud/Workspace accounts need to provide Project ID', - projectIdInstructions: 'How to get Project ID:', - projectIdStep1: 'Visit Google Cloud Console', - projectIdStep2: 'Copy Project ID (Project ID), usually in string format', - projectIdStep3: '⚠️ Note: Copy Project ID, not Project Number!', - projectIdTip: - 'Tip: If your account is a regular personal account (not bound to Google Cloud), leave this field blank.', - projectIdGoogleCloudRequired: 'Google Cloud/Workspace accounts require Project ID', - projectIdGoogleCloudDescription: - 'Some Google accounts (especially those bound to Google Cloud) will be identified as Workspace accounts and require an additional Project ID.', - - // Bedrock fields - awsAccessKeyId: 'AWS Access Key ID', - awsAccessKeyIdRequired: 'AWS Access Key ID *', - awsAccessKeyIdPlaceholder: 'Please enter AWS Access Key ID', - awsSecretAccessKey: 'AWS Secret Access Key', - awsSecretAccessKeyRequired: 'AWS Secret Access Key *', - awsSecretAccessKeyPlaceholder: 'Please enter AWS Secret Access Key', - awsRegion: 'AWS Region', - awsRegionRequired: 'AWS Region *', - awsRegionPlaceholder: 'e.g.: us-east-1', - awsRegionReference: 'Common AWS regions reference:', - awsRegionUsEast1: '• us-east-1 (US East)', - awsRegionUsWest2: '• us-west-2 (US West)', - awsRegionEuWest1: '• eu-west-1 (Europe Ireland)', - awsRegionApSoutheast1: '• ap-southeast-1 (Singapore)', - awsRegionApNortheast1: '• ap-northeast-1 (Tokyo)', - awsRegionEuCentral1: '• eu-central-1 (Frankfurt)', - awsRegionTip: '💡 Please enter complete region code, like us-east-1', - sessionToken: 'Session Token', - sessionTokenOptional: 'Session Token (Optional)', - sessionTokenPlaceholder: 'If using temporary credentials, please enter session token', - sessionTokenDescription: 'Only required when using temporary AWS credentials', - defaultModel: 'Default Primary Model', - defaultModelOptional: 'Default Primary Model (Optional)', - defaultModelPlaceholder: 'e.g.: us.anthropic.claude-sonnet-4-20250514-v1:0', - defaultModelDescription: - 'Leave blank to use system default model. Supports inference profile ID or ARN', - bedrockModelConfigTitle: 'Bedrock Model Configuration:', - bedrockModelConfigInferenceProfile: '• Supports Inference Profile ID (recommended)', - bedrockModelConfigArn: '• Supports Application Inference Profile ARN', - bedrockModelConfigCommon: '• Common model: us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockModelConfigDefault: '• Leave blank to use system configured default model', - smallFastModel: 'Small Fast Model', - smallFastModelOptional: 'Small Fast Model (Optional)', - smallFastModelPlaceholder: 'e.g.: us.anthropic.claude-3-5-haiku-20241022-v1:0', - smallFastModelDescription: - 'Lightweight model for fast responses, leave blank to use system default', - - // Azure OpenAI fields - azureEndpoint: 'Azure Endpoint', - azureEndpointRequired: 'Azure Endpoint *', - azureEndpointPlaceholder: 'https://your-resource.openai.azure.com', - azureEndpointDescription: - 'Azure OpenAI resource endpoint URL, format: https://your-resource.openai.azure.com', - apiVersion: 'API Version', - apiVersionPlaceholder: '2024-02-01', - apiVersionDescription: 'Azure OpenAI API version, defaults to latest stable version 2024-02-01', - deploymentName: 'Deployment Name', - deploymentNameRequired: 'Deployment Name *', - deploymentNamePlaceholder: 'gpt-4', - deploymentNameDescription: 'Deployment name created in Azure OpenAI Studio', - apiKey: 'API Key', - apiKeyRequired: 'API Key *', - apiKeyPlaceholder: 'Please enter API Key', - apiKeyDescription: 'API key obtained from Azure portal', - supportedModels: 'Supported Models', - supportedModelsDescription: 'Select model types supported by this deployment', - - // Claude Console fields - apiUrl: 'API URL', - apiUrlRequired: 'API URL *', - apiUrlPlaceholder: 'e.g.: https://api.example.com', - apiKeyClaudeConsoleRequired: 'API Key *', - apiKeyClaudeConsolePlaceholder: 'Please enter API Key', - dailyQuota: 'Daily Quota Limit', - dailyQuotaLabel: 'Daily Quota Limit ($)', - dailyQuotaPlaceholder: '0 means no limit', - dailyQuotaDescription: 'Set daily usage quota, 0 means no limit', - quotaResetTime: 'Quota Reset Time', - quotaResetTimePlaceholder: '00:00', - quotaResetTimeDescription: 'Daily automatic quota reset time', - todayUsage: "Today's Usage", - remaining: 'Remaining', - used: 'Used', - modelMapping: 'Model Mapping Table', - modelMappingOptional: 'Model Mapping Table (Optional)', - modelMappingDescription: - 'Leave blank to support all models without modification. With mapping configured, left models will be recognized as supported, right models are actually sent.', - originalModel: 'Original Model Name', - mappedModel: 'Mapped Model Name', - addModelMapping: 'Add Model Mapping', - userAgent: 'Custom User-Agent', - userAgentOptional: 'Custom User-Agent (Optional)', - userAgentPlaceholder: 'Leave blank to pass through client User-Agent', - userAgentDescription: - 'When blank, will automatically use client User-Agent, only fill when need to fix specific UA', - rateLimitMechanism: 'Rate Limit Mechanism', - enableRateLimit: 'Enable Rate Limiting', - rateLimitDescription: - 'When enabled, will pause scheduling for a period when account returns 429 errors', - rateLimitDuration: 'Rate Limit Duration (minutes)', - rateLimitDurationDescription: - 'Time to pause scheduling after account is rate limited (minutes)', - - // Claude subscription types - subscriptionType: 'Subscription Type', - subscriptionClaudeMax: 'Claude Max', - subscriptionClaudePro: 'Claude Pro', - claudeProLimitation: 'Pro accounts do not support Claude Opus 4 model', - - // Claude special features - autoStopOnWarning: 'Auto stop scheduling when 5-hour usage approaches limit', - autoStopOnWarningDescription: - 'When system detects account approaching 5-hour usage limit, automatically pause scheduling this account. Will automatically resume when entering new time window.', - useUnifiedUserAgent: 'Use Unified Claude Code Version', - useUnifiedUserAgentDescription: - 'When enabled, will use unified User-Agent captured from real Claude Code client to improve compatibility', - currentUnifiedVersion: '💡 Current unified version: ', - clearCache: 'Clear Cache', - clearing: 'Clearing...', - waitingForCapture: '⏳ Waiting to capture User-Agent from Claude Code client', - captureHint: - '💡 Tip: If unable to capture for a long time, please confirm Claude Code client is using this account, or contact developer to check if User-Agent format has changed', - useUnifiedClientId: 'Use Unified Client Identifier', - useUnifiedClientIdDescription: - 'When enabled, will use fixed client identifier to make all requests appear from same client, reducing fingerprint', - clientId: 'Client Identifier ID', - regenerate: 'Regenerate', - clientIdDescription: - 'This ID will replace user_id client part in requests, keeping session part for sticky sessions', - - // Schedule priority - schedulePriority: 'Schedule Priority', - schedulePriorityRange: 'Schedule Priority (1-100)', - schedulePriorityPlaceholder: 'Lower number = higher priority, default 50', - schedulePriorityDescription: 'Lower number = higher priority, recommended range: 1-100', - - // Manual token input - manualTokenTitle: 'Manual Token Input', - manualTokenDescription: - 'Please enter valid Access Token. If you have Refresh Token, also recommend filling it to support auto refresh.', - manualTokenClaudeDescription: - 'Please enter valid Claude Access Token. If you have Refresh Token, also recommend filling it to support auto refresh.', - manualTokenGeminiDescription: - 'Please enter valid Gemini Access Token. If you have Refresh Token, also recommend filling it to support auto refresh.', - manualTokenOpenAIDescription: - 'Please enter valid OpenAI Access Token. If you have Refresh Token, also recommend filling it to support auto refresh.', - obtainTokenMethods: 'Methods to obtain Access Token:', - claudeTokenPath: - 'Please get credentials from ~/.claude/.credentials.json file on machine with logged-in Claude Code, do not use keys from Claude official website API Keys page.', - geminiTokenPath: - 'Please get credentials from ~/.config/gemini/credentials.json file on machine with logged-in Gemini CLI.', - openaiTokenPath: - 'Please get authentication credentials from machine with logged-in OpenAI account, or get Access Token through OAuth authorization flow.', - accessToken: 'Access Token', - accessTokenOptional: 'Access Token (Optional)', - accessTokenRequired: 'Access Token *', - accessTokenPlaceholder: 'Please enter Access Token...', - accessTokenOptionalPlaceholder: - 'Optional: If not filled, system will automatically get via Refresh Token...', - accessTokenOptionalDescription: - 'Access Token is optional. If not provided, system will automatically get via Refresh Token.', - refreshToken: 'Refresh Token', - refreshTokenOptional: 'Refresh Token (Optional)', - refreshTokenRequired: 'Refresh Token *', - refreshTokenPlaceholder: 'Please enter Refresh Token...', - refreshTokenRequiredPlaceholder: 'Please enter Refresh Token (required)...', - refreshTokenDescription: - 'System will use Refresh Token to automatically get Access Token and user info', - refreshTokenTip: '💡 If Refresh Token not filled, token needs manual update after expiry.', - - // Setup Token flow - setupTokenTitle: 'Claude Setup Token Authorization', - setupTokenDescription: - 'Please follow these steps to complete Claude account authorization via Setup Token:', - setupTokenStep1Title: 'Click button below to generate authorization link', - setupTokenStep2Title: 'Open link in browser and complete authorization', - setupTokenStep2Description: - 'Please open authorization link in new tab, login to your Claude account and authorize Claude Code.', - setupTokenStep2Warning: - 'Note: If you have proxy configured, please ensure browser also uses same proxy to access authorization page.', - setupTokenStep3Title: 'Enter Authorization Code', - setupTokenStep3Description: - 'After authorization completes, copy Authorization Code from return page and paste into input below:', - generateSetupTokenUrl: 'Generate Setup Token Authorization Link', - generating: 'Generating...', - copyLink: 'Copy Link', - regenerateLink: 'Regenerate', - authorizationCode: 'Authorization Code', - authorizationCodePlaceholder: - 'Paste Authorization Code obtained from Claude Code authorization page...', - authorizationCodeDescription: - 'Please paste Authorization Code copied from Claude Code authorization page', - verifying: 'Verifying...', - completeAuthorization: 'Complete Authorization', - - // Token update (edit mode) - updateTokenTitle: 'Update Token', - updateTokenDescription: - 'Can update Access Token and Refresh Token. For security, current Token values are not displayed.', - updateTokenTip: '💡 Leave blank to not update that field.', - newAccessToken: 'New Access Token', - newRefreshToken: 'New Refresh Token', - leaveBlankNoUpdate: 'Leave blank to not update...', - - // Usage information - currentUsage: 'Current Usage', - - // Buttons - cancel: 'Cancel', - nextStep: 'Next Step', - previousStep: 'Previous Step', - create: 'Create', - creating: 'Creating...', - update: 'Update', - updating: 'Updating...', - - // Error messages - pleaseEnterAccountName: 'Please enter account name', - pleaseSelectGroup: 'Please select a group', - pleaseEnterApiUrl: 'Please enter API URL', - pleaseEnterApiKey: 'Please enter API Key', - pleaseEnterAccessKeyId: 'Please enter AWS Access Key ID', - pleaseEnterSecretAccessKey: 'Please enter AWS Secret Access Key', - pleaseEnterRegion: 'Please select AWS region', - pleaseEnterAzureEndpoint: 'Please enter Azure Endpoint', - pleaseEnterDeploymentName: 'Please enter deployment name', - pleaseEnterAccessToken: 'Please enter Access Token', - pleaseEnterRefreshToken: 'Please enter Refresh Token', - - // Success messages - linkCopied: 'Link copied', - extractedAuthCode: 'Successfully extracted authorization code!', - cacheClearedSuccess: 'Unified User-Agent cache cleared', - newClientIdGenerated: 'New client identifier generated', - groupsRefreshed: 'Groups list refreshed', - modelMappingAdded: 'Mapping added', - modelMappingExists: 'Model mapping already exists', - - // Warnings and hints - copyFailed: 'Copy failed, please copy manually', - clearCacheFailed: 'Clear cache failed', - urlNotFound: 'Authorization code parameter not found in URL, please check if link is correct', - urlFormatError: 'Link format error, please check if it is a complete URL', - wrongUrlFormat: 'Please paste link starting with http://localhost:45462', - loadGroupsFailed: 'Failed to load groups list', - - // Confirmation dialogs - projectIdNotFilledTitle: 'Project ID Not Filled', - projectIdNotFilledMessage: - 'You have not filled Project ID.\n\nIf your Google account is bound to Google Cloud or identified as Workspace account, Project ID is required.\nIf you are using regular personal account, you can continue without filling.', - continueButton: 'Continue', - goBackToFill: 'Go Back to Fill', - continueSave: 'Continue Save', - - // Quick model mapping buttons - presetSonnet4: '+ Sonnet 4', - presetOpus41: '+ Opus 4.1', - presetHaiku35: '+ Haiku 3.5', - presetOpus41ToSonnet4: '+ Opus 4.1 → Sonnet 4', - - // Edit mode special hints - leaveBlankNoUpdateApiKey: 'Leave blank to not update API Key', - leaveBlankNoUpdateAwsKey: 'Leave blank to not update AWS Access Key ID', - leaveBlankNoUpdateAwsSecret: 'Leave blank to not update AWS Secret Access Key', - leaveBlankNoUpdateSession: 'Leave blank to not update', - - // General description text - allModelsIfEmpty: - 'Leave blank to support all models. If models specified, requests with models not in list will not be scheduled to this account', - systemDefaultIfEmpty: - 'Leave blank to use system default model. Supports inference profile ID or ARN', - noUpdateIfEmpty: 'Leave blank to not update this field', - - // Manual Token Input Section - manualTokenInput: 'Manual Token Input', - getAccessTokenMethod: 'Methods to get Access Token:', - claudeCredentialsPath: 'Please get from logged-in Claude Code machine', - geminiCredentialsPath: 'Please get from logged-in Gemini CLI machine', - openaiCredentialsPath: - 'Please get authentication credentials from logged-in OpenAI account machine, or obtain Access Token through OAuth authorization flow.', - claudeCredentialsWarning: - 'credentials from file, do not use keys from Claude official API Keys page.', - refreshTokenWarning: - '💡 If Refresh Token is not filled, Token needs manual update after expiration.', - accessTokenOptionalInfo: - 'Access Token is optional. If not provided, system will automatically obtain through Refresh Token.', - accessTokenRequiredPlaceholder: 'Please enter Access Token...', - refreshTokenRequiredInfo: - 'System will use Refresh Token to automatically obtain Access Token and user information', - refreshTokenOptionalPlaceholder: 'Please enter Refresh Token...', - - // Priority Settings - priorityPlaceholder: 'Lower number = higher priority, default 50', - priorityDescription: 'Lower number = higher priority, recommended range: 1-100', - prioritySchedulingTitle: 'Scheduling Priority (1-100)', - priorityEditPlaceholder: 'Lower number = higher priority', - - // Claude Subscription Type and Advanced Options - claudeMaxSubscription: 'Claude Max', - claudeProSubscription: 'Claude Pro', - clientIdLabel: 'Client Identifier ID', - regenerateClientId: 'Regenerate', - - // Edit Mode Fields - accountNameEdit: 'Account Name', - accountNameEditPlaceholder: 'Set an identifiable name for the account', - descriptionOptionalEdit: 'Description (Optional)', - descriptionOptionalEditPlaceholder: 'Account usage description...', - accountTypeEdit: 'Account Type', - noAvailableGroups: 'No available groups', - membersCount: ' members', - createNewGroup: 'Create New Group', - - // AWS Bedrock Configuration - bedrockCredentials: 'Credentials Configuration', - bedrockCredentialsDescription: - 'Please fill in AWS access credentials for calling Amazon Bedrock service.', - bedrockModelConfig: 'Model Configuration', - defaultModelLabel: 'Default Model', - smallFastModelLabel: 'Small Fast Model', - - // Azure OpenAI Configuration - azureOpenAIConfig: 'Azure OpenAI Configuration', - azureOpenAIDescription: - 'Please configure connection information and deployment details for Azure OpenAI service.', - azureApiKey: 'API Key *', - azureApiKeyPlaceholder: 'Please enter Azure OpenAI API Key...', - azureApiVersion: 'API Version', - azureApiVersionDescription: 'Azure OpenAI API version, usually use latest version', - azureDeploymentName: 'Deployment Name *', - azureDeploymentNamePlaceholder: 'e.g., gpt-4', - azureDeploymentDescription: 'Deployment name created in Azure OpenAI Studio', - azureSupportedModels: 'Supported Models', - azureSupportedModelsPlaceholder: 'e.g., gpt-4, gpt-3.5-turbo', - azureSupportedModelsDescription: - 'Model list supported by this account, separated by commas. Leave blank to support all models', - azureAccountSettings: 'Account Settings', - azureIsActive: 'Enable this account', - azureSchedulable: 'Allow scheduling', - - // Claude Console Model Mapping - claudeConsoleModels: 'Model Mapping', - claudeConsoleModelsDescription: - 'Configure model request mapping relationships, mapping client-requested model names to actual models called.', - modelMappingFrom: 'Request Model', - modelMappingFromPlaceholder: 'e.g., claude-3-5-sonnet-20241022', - modelMappingTo: 'Actual Model', - modelMappingToPlaceholder: 'e.g., claude-3-5-sonnet-latest', - removeMapping: 'Remove', - presetMappings: 'Preset Mappings', - modelMappingExample: 'Example: claude-3-5-sonnet-20241022 → claude-3-5-sonnet-latest', - noMappingsConfigured: 'No mappings configured, will use original model name directly', - - // Setup Token Authorization Flow Detailed Steps - setupTokenAuth: 'Setup Token Authorization', - setupTokenAuthDescription: - 'Setup Token is a secure authorization method that completes account verification through temporary authorization code.', - setupTokenStep1: 'Step 1: Generate Authorization Link', - setupTokenStep1Description: - 'System will generate a dedicated authorization link for obtaining temporary authorization code.', - setupTokenStep2: 'Step 2: Complete Authorization', - setupTokenStep3: 'Step 3: Enter Authorization Code', - setupTokenUrlGenerated: 'Authorization link generated', - setupTokenOpenInBrowser: 'Open in browser', - setupTokenCopyLink: 'Copy link', - setupTokenUrlExpiry: 'This link expires in 10 minutes, please complete authorization quickly', - setupTokenAuthCode: 'Authorization Code *', - setupTokenAuthCodePlaceholder: - 'Please paste authorization code obtained from authorization page...', - setupTokenAuthCodeDescription: - 'After completing authorization, will display authorization code in format like "auth_code_xxx"', - setupTokenSmartDetection: 'Smart Detection', - setupTokenSmartDetectionDesc: - 'Supports directly pasting complete callback URL, system will automatically extract authorization code', - - // More Error Messages and Validation Text - unsupportedPlatform: 'Unsupported platform', - accountCreationFailed: 'Account creation failed', - accountUpdateFailed: 'Account update failed', - detailsInfo: 'Details', - accountCreationFailedConsole: 'Account creation failed:', - accountUpdateFailedConsole: 'Account update failed:', - clearCacheFailedWithError: 'Clear cache failed:', - unknownError: 'Unknown error', - modelMappingExistsInfo: 'Model mapping already exists', - modelAddedMapping: 'Mapping added', - - // Rate Limiting and Quota Management - rateLimitSettings: 'Rate Limit Settings', - rateLimitDurationPlaceholder: 'e.g., 60', - quotaManagement: 'Quota Management', - quotaResetTimeLabel: 'Quota reset time', - quotaResetDescription: 'Time point when daily quota resets', - currentDailyUsage: 'Today used', - - // Advanced Settings - advancedSettings: 'Advanced Settings', - customUserAgent: 'Custom User-Agent', - customUserAgentPlaceholder: 'Leave blank to use default User-Agent...', - - // General Hints and Status - notSet: 'Not set', - unlimited: 'Unlimited', - enabled: 'Enabled', - disabled: 'Disabled', - active: 'Active', - inactive: 'Inactive', - optional: 'Optional', - required: 'Required', - recommended: 'Recommended', - - // Additional Action Buttons - testConnection: 'Test connection', - testing: 'Testing...', - refresh: 'Refresh', - refreshing: 'Refreshing...', - validate: 'Validate', - validating: 'Validating...', - save: 'Save', - saving: 'Saving...', - - // Usage Statistics - usageStats: 'Usage Statistics', - loadingUsage: 'Loading usage...', - usageLoadFailed: 'Failed to load usage', - - // Gemini Project ID Details - geminiProjectIdRequired: 'Google Cloud/Workspace accounts require Project ID', - geminiProjectIdDetail: - 'Some Google accounts (especially those bound to Google Cloud) will be identified as Workspace accounts and require additional Project ID.', - geminiHowToGetProjectId: 'How to get Project ID:', - geminiVisitConsole: 'Visit', - geminiCopyProjectId: 'Copy Project ID (Project ID), usually in string format', - geminiProjectIdWarning: - '⚠️ Note: Copy Project ID (Project ID), not Project Number (Project Number)!', - geminiPersonalAccountTip: - 'Tip: If your account is a regular personal account (not bound to Google Cloud), please leave this field empty.', - - // AWS Region Reference - awsRegionEastUS: 'us-east-1 (US East)', - awsRegionWestUS: 'us-west-2 (US West)', - awsRegionEuropeIreland: 'eu-west-1 (Europe Ireland)', - awsRegionAsiaSingapore: 'ap-southeast-1 (Singapore)', - awsRegionAsiaTokyo: 'ap-northeast-1 (Tokyo)', - awsRegionEuropeFrankfurt: 'eu-central-1 (Frankfurt)', - awsRegionInputTip: '💡 Please enter complete region code, e.g. us-east-1', - - // Bedrock Model Description - bedrockModelConfigDesc: 'Bedrock model configuration description:', - bedrockSupportsInferenceProfile: 'Supports Inference Profile ID (recommended)', - bedrockSupportsARN: 'Supports Application Inference Profile ARN', - bedrockCommonModel: 'Common model: us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockEmptyUsesDefault: 'Leave empty to use system configured default model', - - // Azure OpenAI Model Selection - azureModelSelectionDesc: 'Select model types supported by this deployment', - - // Rate Limiting - enableRateLimitMechanism: 'Enable rate limiting mechanism', - rateLimitDescription2: - 'When enabled, will pause scheduling for some time when account returns 429 error', - rateLimitDurationMinutes: 'Rate limit duration (minutes)', - rateLimitDefaultMinutes: 'Default 60 minutes', - rateLimitPauseDesc: 'Time to pause scheduling after account is rate limited (minutes)', - - // Claude Console Specific Fields - claudeConsoleFields: 'Claude Console specific fields', - modelMappingTable: 'Model mapping table', - modelMappingTableOptional: 'Model mapping table (optional)', - - // Setup Token Auth - claudeSetupTokenAuth: 'Claude Setup Token authorization', - setupTokenAuthSteps: - 'Please follow these steps to complete Claude account authorization through Setup Token:', - generateSetupTokenLink: 'Generate Setup Token auth link', - - // Buttons and actions - completeAuth: 'Complete authorization', - - // Error messages - generateSetupTokenFailed: 'Failed to generate Setup Token auth link', - setupTokenAuthFailed: - 'Setup Token authorization failed, please check if the authorization code is correct', - accountCreationError: 'Account creation failed:', - - // Page structure comments - stepIndicator: 'Step indicator', - step1BasicInfo: 'Step 1: Basic information and proxy settings', - groupSelector: 'Group selector', - multiSelectGroup: 'Multi-select group interface', - newGroupOption: 'New group option', - geminiProjectId: 'Gemini project ID field', - bedrockFields: 'Bedrock specific fields', - azureOpenAIFields: 'Azure OpenAI specific fields', - - // Validation messages - nameRequired: 'Please enter account name', - rateLimitDefault60: 'Default 60 minutes', - rateLimitPauseDescription: 'Time to pause scheduling after account is rate limited (minutes)', - dailyQuotaLimit: 'Daily quota limit ($)', - quotaZeroUnlimited: '0 means unlimited', - - // Quota Management - quotaManagementFields: 'Quota management fields', - dailyQuotaLimitDollar: 'Daily quota limit ($)', - dailyQuotaDesc: 'Set daily usage quota, 0 means unlimited', - quotaResetTimeDesc: 'Time to automatically reset quota daily', - - // Model Mapping - modelMappingDesc: - 'Leave empty to support all models without modifying requests. After configuring mapping, left model will be recognized as supported model, right is the actual model sent.', - originalModelName: 'Original model name', - mappedModelName: 'Mapped model name', - addModelMappingBtn: 'Add model mapping', - customUserAgentOptional: 'Custom User-Agent (optional)', - customUserAgentDesc: - 'When empty, will automatically use client User-Agent, only fill when need to fix specific UA', - userAgentPassthrough: 'Leave empty to pass through client User-Agent', - - // Claude Subscription Type - claudeSubscriptionType: 'Subscription type', - - // Claude Advanced Options - claudeAutoStopScheduling: 'Auto-stop scheduling when approaching 5-hour limit', - claudeAutoStopDesc: - 'When system detects account approaching 5-hour usage limit, automatically pause scheduling for this account. Will resume automatically when entering new time window.', - claudeUseUnifiedUA: 'Use unified Claude Code version', - claudeUnifiedUADesc: - 'When enabled, will use unified User-Agent captured from real Claude Code client, improving compatibility', - claudeCurrentUnifiedVersion: '💡 Current unified version:', - claudeWaitingCapture: '⏳ Waiting to capture User-Agent from Claude Code client', - claudeCaptureHint: - '💡 Hint: If unable to capture for long time, please confirm Claude Code client is using this account, or contact developer to check if User-Agent format has changed', - claudeUseUnifiedClientId: 'Use unified client identifier', - claudeUnifiedClientIdDesc: - 'When enabled, will use fixed client identifier, making all requests appear to come from same client, reducing characteristics', - claudeClientIdLabel: 'Client identifier ID', - claudeClientIdDesc: - 'This ID will replace user_id client part in requests, preserving session part for sticky sessions', - - // Setup Token Process - setupTokenAuthProcess: 'Claude Setup Token Authorization', - setupTokenProcessDesc: - 'Please follow these steps to complete Claude account authorization through Setup Token:', - setupTokenStepOneTitle: 'Step 1: Generate authorization link', - setupTokenStepOneDesc: 'Click button below to generate authorization link', - setupTokenGenerating: 'Generating...', - setupTokenGenerateBtn: 'Generate Setup Token authorization link', - setupTokenCopyTitle: 'Copy link', - - // Step Indicators - step2OAuth: 'Step 2: OAuth authorization', - step2SetupToken: 'Step 2: Setup Token authorization', - - // Group Selector - multiGroupInterface: 'Multi-group interface', - createNewGroupOption: 'Create new group option', - - // Manual Token Input Tips - credentialsFromFile: 'credentials from file.', - - // Placeholder texts - originalModelNamePlaceholder: 'Original model name', - mappedModelNamePlaceholder: 'Mapped model name', - authCodePlaceholder: 'Paste Authorization Code obtained from Claude Code auth page...', - leaveEmptyNoUpdate: 'Leave empty for no update', - leaveEmptyNoUpdateKey: 'Leave empty for no API Key update', - leaveEmptyNoUpdateToken: 'Leave empty for no update...', - - // Labels and descriptions - schedulePriorityLabel: 'Schedule Priority (1-100)', - attentionLabel: 'Attention:', - supportedModelsLabel: 'Supported Models', - newAccessTokenLabel: 'New Access Token', - newRefreshTokenLabel: 'New Refresh Token', - updateTokenLabel: 'Update Token', - - // Button texts - regenerateBtn: 'Regenerate', - previousStepBtn: 'Previous Step', - - // Descriptive texts - claude5HourLimitDesc: 'Auto-stop scheduling when approaching 5-hour usage limit', - claude5HourLimitExplanation: - 'When system detects account approaching 5-hour usage limit, automatically pause scheduling for this account. Will resume automatically when entering new time window.', - useUnifiedClaudeVersion: 'Use unified Claude Code version', - unifiedVersionDesc: - 'When enabled, will use unified User-Agent captured from real Claude Code client, improving compatibility', - waitingUserAgent: '⏳ Waiting to capture User-Agent from Claude Code client', - userAgentTip: - '💡 Tip: If unable to capture for a long time, please confirm that Claude Code client is using this account,', - contactDeveloper: 'or contact developer to check if User-Agent format has changed', - unifiedClientIdDesc: - 'When enabled, will use fixed client identifier, making all requests appear from same client, reducing fingerprinting', - clientIdReplaceDesc: - 'This ID will replace the user_id client part in requests, retaining session part for sticky sessions', - - // OAuth step texts - step1GenerateAuthLink: 'Step 1: Generate Authorization Link', - clickButtonGenerate: 'Click button below to generate authorization link', - copyLinkTitle: 'Copy Link', - step2AccessAndAuth: 'Step 2: Access Link and Authorize', - openInBrowser: 'Open link in browser and complete authorization', - browserAuthDesc: - 'Please open the authorization link in a new tab, login to your Claude account and authorize Claude Code.', - proxyNotice: - 'If you have set up a proxy, please ensure your browser also uses the same proxy to access the authorization page.', - step3InputAuthCode: 'Step 3: Input Authorization Code', - inputAuthCodeTitle: 'Input Authorization Code', - authCompleteDesc: - 'After authorization is complete, copy Authorization Code from the return page and paste it into the input box below:', - pasteAuthCodeDesc: - 'Please paste the Authorization Code copied from Claude Code authorization page', - - // AWS region reference - awsRegionRef: 'Common AWS regions reference:', - - // Error messages - copyFailedManual: 'Copy failed, please copy manually', - - // Form descriptions - modelSupportDesc: - 'Leave empty to support all models. If models are specified, requests with models not in the list will not be scheduled to this account', - modelTypeSelectionDesc: 'Select model types supported by this deployment', - userAgentDesc: - 'When empty, will automatically use client User-Agent, only fill when need to fix specific UA', - - // Basic labels - apiUrlLabel: 'API URL', - apiKeyLabel: 'API Key', - - // More missing keys - copyLinkTooltip: 'Copy Link', - - // Claude subscription type display - claudeMaxDisplay: 'Claude Max', - claudeProDisplay: 'Claude Pro', - - // Missing translation keys for AccountForm - modelSupportDescription: - 'Leave empty to support all models. If models are specified, requests with models not in the list will not be scheduled to this account', - regenerateButton: 'Regenerate', - cancelButton: 'Cancel', - credentialsFileDescription: 'credentials from file.', - authCodeInputHint: - 'Please paste the Authorization Code copied from Claude Code authorization page', - dailyQuotaLimitLabel: 'Daily Quota Limit ($)', - todayUsageLabel: "Today's Usage", - remainingQuota: 'Remaining: ${amount}', - usedPercentage: '{percentage}% Used', - modelMappingTableLabel: 'Model Mapping Table (Optional)', - modelMappingTableDescription: - 'Leave empty to support all models without modifying requests. After configuration, left models will be recognized as supported models, right models are actually sent.', - customUserAgentLabel: 'Custom User-Agent (Optional)', - rateLimitDurationLabel: 'Rate Limit Duration (Minutes)', - awsRegionReferenceTitle: 'Common AWS Regions Reference:', - azureModelTypeDescription: 'Select model types supported by this deployment', - tokenUpdateDescription: - 'You can update Access Token and Refresh Token. For security reasons, current Token values will not be displayed.', - tokenUpdateHint: '💡 Leave empty to keep current value unchanged.', - - // Setup Token Flow Translations - clickToGenerateAuthLink: 'Click to generate authorization link', - openLinkInBrowser: 'Open link in browser and complete authorization', - openAuthLinkDescription: - 'Please open the authorization link in a new tab, log in to your Claude account and authorize.', - proxyWarning: - 'Note: If you have configured a proxy, please ensure your browser also uses the same proxy to access the authorization page.', - enterAuthorizationCode: 'Enter Authorization Code', - copyAuthCodeDescription: - 'After authorization is complete, copy the Authorization Code from the return page and paste it into the input box below:' - }, - - // OAuth Flow Component - oauthFlow: { - // Platform titles - claudeAccountAuth: 'Claude Account Authorization', - geminiAccountAuth: 'Gemini Account Authorization', - openaiAccountAuth: 'OpenAI Account Authorization', - - // Flow descriptions - claudeAuthDescription: 'Please follow these steps to complete Claude account authorization:', - geminiAuthDescription: 'Please follow these steps to complete Gemini account authorization:', - openaiAuthDescription: 'Please follow these steps to complete OpenAI account authorization:', - - // Step titles - step1Title: 'Click the button below to generate authorization link', - step2Title: 'Open the link in browser and complete authorization', - step3Title: 'Input Authorization Code', - step3TitleOpenAI: 'Input authorization link or Code', - - // Step descriptions - step2Description: - 'Please open the authorization link in a new tab, log in to your Claude account and authorize.', - step2DescriptionGemini: - 'Please open the authorization link in a new tab, log in to your Gemini account and authorize.', - step2DescriptionOpenAI: - 'Please open the authorization link in a new tab, log in to your OpenAI account and authorize.', - - step3Description: 'After authorization is complete, the page will display a', - step3DescriptionMiddle: ', please copy and paste it into the input box below:', - step3DescriptionGemini: - 'After authorization is complete, the page will display an Authorization Code, please copy and paste it into the input box below:', - step3DescriptionOpenAI: 'After authorization is complete, when the page address changes to', - step3DescriptionOpenAIMiddle: ':', - - // Button text - generating: 'Generating...', - generateAuthLink: 'Generate Authorization Link', - regenerate: 'Regenerate', - previousStep: 'Previous Step', - completeAuth: 'Complete Authorization', - verifying: 'Verifying...', - - // Placeholders - authCodePlaceholder: 'Paste the Authorization Code obtained from Claude page...', - authCodePlaceholderGemini: 'Paste the Authorization Code obtained from Gemini page...', - authCodePlaceholderOpenAI: - 'Method 1: Copy the complete link (http://localhost:1455/auth/callback?code=...)\nMethod 2: Copy only the code parameter value\nThe system will automatically recognize and extract the required information', - - // Labels - authorizationCode: 'Authorization Code', - authLinkOrCode: 'Authorization Link or Code', - - // Hints - copyLinkTooltip: 'Copy Link', - authCodeHint: 'Please paste the Authorization Code copied from Claude page', - authCodeHintGemini: 'Please paste the Authorization Code copied from Gemini page', - - // Notices - proxyNotice: 'Note:', - proxyNoticeText: - 'If you have set up a proxy, please ensure the browser also uses the same proxy to access the authorization page.', - - // OpenAI specific hints - openaiImportantNote: 'Important Notice:', - openaiLoadingNote: - 'The page may take a long time to load after authorization, please wait patiently.', - openaiAddressNote: 'When the browser address bar changes to', - openaiAddressNoteMiddle: 'at the beginning, it means authorization is complete.', - - openaiTip: 'Tip:', - openaiTipText: - 'You can directly copy the entire link or just copy the code parameter value, the system will automatically recognize.', - openaiLinkExample: '• Complete link example:', - openaiCodeExample: '• Code only example:', - - // Success and error messages - successExtractCode: 'Successfully extracted authorization code!', - errorCodeNotFound: - 'Authorization code parameter not found in URL, please check if the link is correct', - errorLinkFormat: 'Link format error, please check if it is a complete URL', - errorWrongUrlFormat: - 'Please paste links starting with http://localhost:1455 or http://localhost:45462', - linkCopied: 'Link copied', - authFailed: 'Authorization failed, please check if the authorization code is correct', - generateAuthFailed: 'Failed to generate authorization link' - }, - - // Group Management Modal - groupManagement: { - title: 'Account Group Management', - createNewGroup: 'Create New Group', - createGroup: 'Create New Group', - groupNameRequired: 'Group Name *', - groupNamePlaceholder: 'Enter group name', - platformTypeRequired: 'Platform Type *', - descriptionOptional: 'Description (Optional)', - descriptionPlaceholder: 'Group description...', - creating: 'Creating...', - create: 'Create', - cancel: 'Cancel', - loading: 'Loading...', - noGroups: 'No groups', - noDescription: 'No description', - membersCount: ' members', - edit: 'Edit', - delete: 'Delete', - editGroup: 'Edit Group', - platformTypeLabel: 'Platform Type', - cannotModify: '(Cannot modify)', - updating: 'Updating...', - update: 'Update', - // Toast messages - loadGroupsFailed: 'Failed to load group list', - fillRequiredFields: 'Please fill in required fields', - groupCreated: 'Group created successfully', - createGroupFailed: 'Failed to create group', - fillGroupName: 'Please fill in group name', - groupUpdated: 'Group updated successfully', - updateGroupFailed: 'Failed to update group', - groupHasMembers: 'Group has members, cannot delete', - confirmDelete: 'Are you sure you want to delete group "{name}"?', - groupDeleted: 'Group deleted successfully', - deleteGroupFailed: 'Failed to delete group' - }, - - // Proxy Configuration - proxyConfig: { - title: 'Proxy Settings (Optional)', - enableProxy: 'Enable Proxy', - configDescription: - 'Configure proxy to access restricted network resources. Supports SOCKS5 and HTTP proxy.', - stabilityNotice: - 'Please ensure the proxy server is stable and available, otherwise it will affect normal account usage.', - proxyType: 'Proxy Type', - hostAddress: 'Host Address', - hostPlaceholder: 'e.g., 192.168.1.100', - port: 'Port', - portPlaceholder: 'e.g., 1080', - needsAuth: 'Requires Authentication', - username: 'Username', - usernamePlaceholder: 'Proxy username', - password: 'Password', - passwordPlaceholder: 'Proxy password', - tip: 'Tip:', - apiRequestNotice: - 'Proxy settings will be used for all API requests related to this account. Please ensure the proxy server supports HTTPS traffic forwarding.' - } -} diff --git a/web/admin-spa/src/i18n/locales/zh-cn.js b/web/admin-spa/src/i18n/locales/zh-cn.js deleted file mode 100644 index 999e5b7b..00000000 --- a/web/admin-spa/src/i18n/locales/zh-cn.js +++ /dev/null @@ -1,2920 +0,0 @@ -export default { - layout: { - mainLayout: { - comments: { - topNavigation: '顶部导航', - mainContentArea: '主内容区域', - tabBar: '标签栏', - contentArea: '内容区域' - }, - routing: { - routeChangeError: '路由切换失败:', - routeNotFound: '路由未找到', - defaultToTab: '默认选中仪表板' - } - }, - tabBar: { - tabs: { - dashboard: { - name: '仪表板', - shortName: '仪表板' - }, - apiKeys: { - name: 'API Keys', - shortName: 'API' - }, - accounts: { - name: '账户管理', - shortName: '账户' - }, - userManagement: { - name: '用户管理', - shortName: '用户' - }, - tutorial: { - name: '使用教程', - shortName: '教程' - }, - settings: { - name: '系统设置', - shortName: '设置' - } - } - } - }, - common: { - save: '保存', - cancel: '取消', - confirm: '确认', - loading: '加载中...', - edit: '编辑', - delete: '删除', - create: '创建', - update: '更新', - search: '搜索', - reset: '重置', - locale: 'zh-CN', - toastNotification: { - defaultTitles: { - success: '成功', - error: '错误', - warning: '警告', - info: '信息' - } - }, - errors: { - loadDashboardFailed: '加载仪表板数据失败', - loadUsageTrendFailed: '加载使用趋势失败', - loadModelStatsFailed: '加载模型统计失败', - loadApiKeysTrendFailed: '加载API Keys趋势失败', - createClaudeConsoleAccountFailed: '创建Claude Console账户失败', - createAzureOpenAIAccountFailed: '创建Azure OpenAI账户失败', - updateClaudeConsoleAccountFailed: '更新Claude Console账户失败', - updateAzureOpenAIAccountFailed: '更新Azure OpenAI账户失败', - generateSetupTokenUrlFailed: '生成Setup Token URL失败', - exchangeSetupTokenFailed: '交换Setup Token授权码失败', - allApiKeysInvalid: '所有 API Key 都无效', - loadOemSettingsFailed: '加载OEM设置失败', - getApiKeyStatsFailed: '获取API Key统计失败', - getTagsFailed: '获取标签失败', - requestFailed: '请求失败: {status}', - loadSupportedClientsFailed: '加载支持的客户端失败' - }, - system: { - status: { - normal: '正常', - abnormal: '异常' - } - }, - confirmDialog: { - confirm: '确认', - cancel: '取消' - }, - confirmModal: { - continue: '继续', - cancel: '取消' - }, - themeToggle: { - light: { - label: '浅色模式', - shortLabel: '浅色' - }, - dark: { - label: '深色模式', - shortLabel: '深色' - }, - auto: { - label: '跟随系统', - shortLabel: '自动' - }, - toggleTheme: '切换主题', - clickToSwitch: '点击切换主题' - }, - logoTitle: { - logoAlt: '标志' - }, - languageSwitch: { - zhCnName: '简体中文', - zhTwName: '繁体中文', - enName: '英语', - zhCnFlag: '简', - zhTwFlag: '繁', - enFlag: 'EN' - }, - accountSelector: { - searchPlaceholder: '搜索账号名称...', - schedulingGroups: '调度分组', - membersUnit: '个成员', - claudeOAuthAccounts: 'Claude OAuth 专属账号', - oauthAccounts: 'OAuth 专属账号', - claudeConsoleAccounts: 'Claude Console 专属账号', - noResultsFound: '没有找到匹配的账号', - selectAccount: '请选择账号', - useSharedPool: '使用共享账号池', - accountStatus: { - unknown: '未知', - unauthorized: '未授权', - tokenError: 'Token错误', - pending: '待验证', - rateLimited: '限流中', - error: '异常', - active: '正常' - }, - dateFormat: { - today: '今天创建', - yesterday: '昨天创建', - daysAgo: '天前' - } - }, - customDropdown: { - placeholder: '请选择' - }, - // 通用时间与错误 - time: { - justNow: '刚刚', - minutesAgo: '{minutes}分钟前', - hoursAgo: '{hours}小时前', - daysAgo: '{days}天前' - }, - - // API Keys 页面按钮 - model: '模型', - stats: '统计' - }, - language: { - zh: '简体中文', - 'zh-tw': '繁體中文', - en: 'English', - current: '当前语言', - switch: '切换语言' - }, - - header: { - adminPanel: '管理后台', - userMenu: '用户菜单', - logout: '退出登录', - settings: '系统设置', - - // Version related - currentVersion: '当前版本', - newVersionAvailable: '有新版本可用', - newVersion: '新版本', - hasUpdate: '有新版本', - viewUpdate: '查看更新', - checkingUpdate: '检查更新中...', - alreadyLatest: '当前已是最新版本', - checkUpdate: '检查更新', - - // User menu items - changeAccountInfo: '修改账户信息', - - // Change password modal - changePasswordModal: { - title: '修改账户信息', - currentUsername: '当前用户名', - currentUsernameHint: '当前用户名,输入新用户名以修改', - newUsername: '新用户名', - newUsernamePlaceholder: '输入新用户名(留空保持不变)', - newUsernameHint: '留空表示不修改用户名', - currentPassword: '当前密码', - currentPasswordPlaceholder: '请输入当前密码', - newPassword: '新密码', - newPasswordPlaceholder: '请输入新密码', - newPasswordHint: '密码长度至少8位', - confirmPassword: '确认新密码', - confirmPasswordPlaceholder: '请再次输入新密码', - saving: '保存中...', - save: '保存修改', - - // Messages - passwordMismatch: '两次输入的密码不一致', - passwordTooShort: '新密码长度至少8位', - accountInfoChangeSuccess: '账户信息修改成功,请重新登录', - passwordChangeSuccess: '密码修改成功,请重新登录', - changeFailed: '修改失败', - changePasswordFailed: '修改密码失败' - }, - - // Logout - logoutConfirm: '确定要退出登录吗?', - logoutSuccess: '已安全退出' - }, - apiStats: { - title: 'API Key 使用统计', - tutorialTitle: '使用教程', - userLogin: '用户登录', - adminPanel: '管理后台', - statsQuery: '统计查询', - tutorial: '使用教程', - timeRange: '统计时间范围', - today: '今日', - thisMonth: '本月', - - // API Key Input - usageStatsQuery: '使用统计查询', - apiKeyDescription: '查询您的 API Key 使用情况和统计数据', - enterApiKey: '输入您的 API Key', - enterApiKeys: '输入您的 API Keys(每行一个或用逗号分隔)', - singleMode: '单一', - aggregateMode: '聚合', - singleModeTitle: '单一模式', - aggregateModeTitle: '聚合模式', - queryButton: '查询', - - // Stats Overview - batchQuerySummary: '批量查询概要', - apiKeyInfo: 'API Key 信息', - queryKeysCount: '查询 Keys 数', - activeKeysCount: '有效 Keys 数', - totalRequests: '总请求数', - totalTokens: '总 Token 数', - totalCost: '总费用', - individual: '个', - - // Aggregated Stats Card - usageRatio: '使用占比', - requests: '次', - otherKeys: '其他', - keys: 'Keys', - - // Model Usage Stats - modelUsageStats: '模型使用统计', - loadingModelStats: '加载模型统计数据中...', - requestCount: '次请求', - inputTokens: '输入 Token', - outputTokens: '输出 Token', - cacheCreateTokens: '缓存创建', - cacheReadTokens: '缓存读取', - noModelData: '暂无{period}模型使用数据', - - // Token Distribution - tokenDistribution: 'Token 使用分布', - inputToken: '输入 Token', - outputToken: '输出 Token', - cacheCreateToken: '缓存创建 Token', - cacheReadToken: '缓存读取 Token', - - // Limit Config - limitConfig: '限制配置', - limitConfigAggregate: '限制配置(聚合查询模式)', - apiKeysOverview: 'API Keys 概况', - totalKeys: '总计 Keys', - activeKeys: '激活 Keys', - aggregatedStats: '聚合统计', - dailyLimit: '日限制', - monthlyLimit: '月限制', - usageToday: '今日使用', - usageThisMonth: '本月使用', - remaining: '剩余', - - // Stats Overview - Additional keys - name: '名称', - status: '状态', - permissions: '权限', - createdAt: '创建时间', - expiresAt: '过期时间', - active: '活跃', - inactive: '已停用', - notActivated: '未激活', - expired: '已过期', - neverExpires: '永不过期', - allModels: '全部模型', - unknown: '未知', - none: '无', - formatError: '格式错误', - usageStatsOverview: '使用统计概览', - keyContribution: '各 Key 贡献占比', - firstUseDays: '首次使用后{days}天过期', - todayRequests: '今日请求数', - todayTokens: '今日Token数', - todayCost: '今日费用', - todayInputTokens: '今日输入Token', - monthlyRequests: '本月请求数', - monthlyTokens: '本月Token数', - monthlyCost: '本月费用', - monthlyInputTokens: '本月输入Token', - - // Limit Config - Additional keys - dailyCostLimit: '每日费用限制', - concurrencyLimit: '并发限制', - modelLimit: '模型限制', - clientLimit: '客户端限制', - restrictedModelsCount: '限制 {count} 个模型', - allowAllModels: '允许所有模型', - restrictedClientsCount: '限制 {count} 个客户端', - allowAllClients: '允许所有客户端', - detailedLimitInfo: '详细限制信息', - restrictedModelsList: '受限模型列表', - restrictedModelsNote: '此 API Key 不能访问以上列出的模型', - allowedClientsList: '允许的客户端', - allowedClientsNote: '此 API Key 只能被以上列出的客户端使用', - timeWindowLimit: '时间窗口限制', - aggregateStatsNote: '每个 API Key 有独立的限制设置,聚合模式下不显示单个限制配置', - aggregateStatsSummary: '聚合统计摘要', - invalidKeysCount: '{count} 个无效的 API Key', - orRelationshipRequests: '请求次数和费用限制为“或”的关系,任一达到限制即触发限流', - orRelationshipTokens: '请求次数和Token使用量为“或”的关系,任一达到限制即触发限流', - onlyRequestsLimit: '仅限制请求次数', - - // Token Distribution - Additional keys - totalAmount: '总计', - todayTotal: '今日总计', - monthlyTotal: '本月总计', - - // Additional missing keys - usageRatioOnlyInMultiMode: '使用占比仅在多Key查询时显示', - noData: '暂无数据', - - // ApiKeyInput placeholders and texts - apiKeyPlaceholder: '请输入您的 API Key (cr_...)', - apiKeysPlaceholder: '请输入您的 API Keys,支持以下格式:\ncr_xxx\ncr_yyy\n或\ncr_xxx, cr_yyy', - clearInput: '清空输入', - securityNoticeSingle: '您的 API Key 仅用于查询自己的统计数据,不会被存储或用于其他用途', - securityNoticeMulti: - '您的 API Keys 仅用于查询统计数据,不会被存储。聚合模式下部分个体化信息将不显示。', - multiKeyTip: '提示:最多支持同时查询 30 个 API Keys。使用 Ctrl+Enter 快速查询。', - errors: { - queryStatsFailed: '查询统计数据失败,请检查您的 API Key 是否正确', - enterAtLeastOneKey: '请输入至少一个有效的 API Key', - batchQueryFailed: '批量查询失败', - batchModelStatsFailed: '加载批量模型统计失败', - loadModelStatsFailed: '加载模型统计失败', - allInvalidKeys: '所有 API Key 都无效' - } - }, - - // Login page - login: { - title: '管理后台', - username: '用户名', - usernamePlaceholder: '请输入用户名', - password: '密码', - passwordPlaceholder: '请输入密码', - loginButton: '登录', - loggingIn: '登录中...', - loginFailed: '登录失败', - loginFailedCheck: '登录失败,请检查用户名和密码' - }, - - // Dashboard page - dashboard: { - // Main stats cards - totalApiKeys: '总 API Keys', - activeApiKeys: '活跃', - serviceAccounts: '服务账户', - normalAccounts: '正常', - abnormalAccounts: '异常', - pausedAccounts: '停止调度', - rateLimitedAccounts: '限流', - todayRequests: '今日请求', - totalRequests: '总请求', - systemStatus: '系统状态', - uptime: '运行时间', - - // Platform accounts tooltip - claudeAccount: 'Claude: {total} 个 (正常: {normal})', - consoleAccount: 'Console: {total} 个 (正常: {normal})', - geminiAccount: 'Gemini: {total} 个 (正常: {normal})', - bedrockAccount: 'Bedrock: {total} 个 (正常: {normal})', - openaiAccount: 'OpenAI: {total} 个 (正常: {normal})', - azureOpenaiAccount: 'Azure OpenAI: {total} 个 (正常: {normal})', - - // Token stats cards - todayToken: '今日Token', - totalTokenConsumption: '总Token消耗', - inputTokens: '输入', - outputTokens: '输出', - cacheCreateTokens: '缓存创建', - cacheReadTokens: '缓存读取', - - // Real-time metrics - realtimeRPM: '实时RPM', - realtimeTPM: '实时TPM', - requestsPerMinute: '每分钟请求数', - tokensPerMinute: '每分钟Token数', - historicalData: '历史数据', - minutes: '分钟', - // Uptime display formats - uptimeFormat: { - daysHours: '{days}天 {hours}小时', - hoursMinutes: '{hours}小时 {minutes}分钟', - minutes: '{minutes}分钟' - }, - - // Charts section - modelDistributionAndTrend: '模型使用分布与Token使用趋势', - - // Date filter presets (would be populated from dateFilter.presetOptions) - today: '今日', - yesterday: '昨日', - dayBefore: '前天', - last7Days: '近7天', - last30Days: '近30天', - thisWeek: '本周', - lastWeek: '上周', - thisMonth: '本月', - lastMonth: '上月', - - // Granularity buttons - byDay: '按天', - byHour: '按小时', - - // Date picker - startDatePlaceholder: '开始日期', - endDatePlaceholder: '结束日期', - dateSeparator: '至', - maxHours24: '最多24小时', - - // Auto refresh controls - autoRefresh: '自动刷新', - refresh: '刷新', - refreshing: '刷新中', - refreshDataNow: '立即刷新数据', - - // Charts - tokenUsageDistribution: 'Token使用分布', - detailedStatistics: '详细统计数据', - noModelUsageData: '暂无模型使用数据', - - // Table headers - model: '模型', - requestCount: '请求数', - totalTokens: '总Token', - cost: '费用', - percentage: '占比', - - // Trend charts - tokenUsageTrend: 'Token使用趋势', - apiKeysUsageTrend: 'API Keys 使用趋势', - requestsCount: '请求次数', - tokenCount: 'Token 数量', - totalApiKeysCount: '共 {count} 个 API Key', - showingTop10: '共 {count} 个 API Key,显示使用量前 10 个', - - // Chart labels - inputTokensLabel: '输入Token', - outputTokensLabel: '输出Token', - cacheCreateTokensLabel: '缓存创建Token', - cacheReadTokensLabel: '缓存读取Token', - costLabel: '费用 (USD)', - requestsLabel: '请求数', - time: '时间', - date: '日期', - tokenQuantity: 'Token数量', - requestsQuantity: '请求次数', - - // Usage Trend component - usageTrend: { - title: '使用趋势', - granularity: { - byDay: '按天', - byHour: '按小时' - }, - periodOptions: { - last24Hours: '24小时', - last7Days: '7天', - last30Days: '30天', - recentDays: '最近{days}天' - }, - chartLabels: { - requests: '请求次数', - tokens: 'Token使用量', - requestsAxis: '请求次数', - tokensAxis: 'Token使用量' - } - }, - - // Model Distribution component - modelDistribution: { - title: '模型使用分布', - periods: { - daily: '今日', - total: '累计' - }, - noData: '暂无模型使用数据', - units: { - requests: '请求', - tokens: 'tokens' - }, - chart: { - tooltip: { - requests: '请求', - tokens: 'Tokens' - } - } - }, - errors: { - rangeTooLongHour: '小时粒度下日期范围不能超过24小时', - rangeTooLongDay: '日期范围不能超过 31 天', - rangeTooLongHourSwitched: '小时粒度下日期范围不能超过24小时,已切换到近24小时' - } - }, - - // Accounts page - accounts: { - title: '账户管理', - description: '管理您的 Claude、Gemini、OpenAI 和 Azure OpenAI 账户及代理配置', - - // Filters and sorting - sortBy: '选择排序', - selectPlatform: '选择平台', - selectGroup: '选择分组', - refresh: '刷新', - refreshTooltip: '刷新数据 (Ctrl/⌘+点击强制刷新所有缓存)', - addAccount: '添加账户', - - // Sort options - sortByName: '按名称排序', - sortByDailyTokens: '按今日Token排序', - sortByDailyRequests: '按今日请求数排序', - sortByTotalTokens: '按总Token排序', - sortByLastUsed: '按最后使用排序', - - // Platform options - allPlatforms: '所有平台', - claudePlatform: 'Claude', - claudeConsolePlatform: 'Claude Console', - geminiPlatform: 'Gemini', - openaiPlatform: 'OpenAi', - azureOpenaiPlatform: 'Azure OpenAI', - bedrockPlatform: 'Bedrock', - - // Group options - allAccounts: '所有账户', - ungroupedAccounts: '未分组账户', - - // Loading states - loadingAccounts: '正在加载账户...', - noAccounts: '暂无账户', - noAccountsHint: '点击上方按钮添加您的第一个账户', - - // Table headers - name: '名称', - platformType: '平台/类型', - status: '状态', - priority: '优先级', - proxy: '代理', - dailyUsage: '今日使用', - sessionWindow: '会话窗口', - lastUsed: '最后使用', - actions: '操作', - - // Account types - dedicated: '专属', - groupScheduling: '分组调度', - shared: '共享', - belongsToGroup: '所属分组: {name}', - - // Platform labels - unknown: '未知', - apiKey: 'API Key', - oauth: 'OAuth', - setup: 'Setup', - aws: 'AWS', - - // Account status - normal: '正常', - abnormal: '异常', - blocked: '已封锁', - tempError: '临时异常', - rateLimited: '限流中', - notSchedulable: '不可调度', - bound: '绑定: {count} 个API Key', - - // Proxy status - noProxy: '无代理', - - // Usage statistics - requests: '次', - noData: '暂无数据', - averageRpm: '平均 {rpm} RPM', - - // Session window tooltip - sessionWindowTooltip: { - title: '会话窗口进度表示5小时窗口的时间进度', - normal: '正常:请求正常处理', - warning: '警告:接近限制', - rejected: '拒绝:达到速率限制' - }, - - // Session window status - remaining: '剩余 {time}', - ended: '已结束', - - // Console quota - quotaProgress: '额度进度', - remainingQuota: '剩余 $${amount}', - reset: '重置 {time}', - - // Mobile view labels - dailyUsageLabel: '今日使用', - sessionWindowLabel: '会话窗口', - lastUsedLabel: '最后使用', - proxyLabel: '代理', - priorityLabel: '优先级', - neverUsed: '从未使用', - sessionWindowTooltipMobile: '会话窗口进度不代表使用量,仅表示距离下一个5小时窗口的剩余时间', - - // Action buttons - resetStatus: '重置状态', - resetting: '重置中...', - resetStatusTooltip: '重置所有异常状态', - scheduling: '调度', - disabled: '停用', - enableTooltip: '点击启用调度', - disableTooltip: '点击禁用调度', - edit: '编辑', - editTooltip: '编辑账户', - delete: '删除', - deleteTooltip: '删除账户', - pause: '暂停', - enable: '启用', - - // Time formatting - justNow: '刚刚', - minutesAgo: '{minutes} 分钟前', - hoursAgo: '{hours} 小时前', - daysAgo: '{days} 天前', - hoursAndMinutes: '{hours}小时{minutes}分钟', - hoursOnly: '{hours}小时', - minutesOnly: '{minutes}分钟', - daysAndHours: '{days}天{hours}小时', - daysOnly: '{days}天', - - // Rate limit time - rateLimitTime: '({time})', - - // Messages and confirmations - resetStatusConfirmTitle: '重置账户状态', - resetStatusConfirmMessage: - '确定要重置此账户的所有异常状态吗?这将清除限流状态、401错误计数等所有异常标记。', - resetStatusConfirmButton: '确定重置', - resetStatusCancelButton: '取消', - statusResetSuccess: '账户状态已重置', - statusResetFailed: '状态重置失败', - - deleteAccountTitle: '删除账户', - deleteAccountMessage: '确定要删除账户 "{name}" 吗?\n\n此操作不可恢复。', - deleteAccountButton: '删除', - deleteAccountCancel: '取消', - cannotDeleteBoundAccount: - '无法删除此账号,有 {count} 个API Key绑定到此账号,请先解绑所有API Key', - accountDeleted: '账户已删除', - deleteFailed: '删除失败', - - enabledScheduling: '已启用调度', - disabledScheduling: '已禁用调度', - schedulingToggleFailed: '切换调度状态失败', - unsupportedAccountType: '该账户类型暂不支持调度控制', - operationFailed: '操作失败', - - accountCreateSuccess: '账户创建成功', - accountUpdateSuccess: '账户更新成功', - loadAccountsFailed: '加载账户失败', - unsupportedAccountTypeReset: '不支持的账户类型', - - // Schedulable reasons - invalidApiKey: 'API Key无效或已过期(401错误)', - serviceOverload: '服务过载(529错误)', - rateLimitTriggered: '触发限流(429错误)', - authFailed: '认证失败(401错误)', - manualStop: '手动停止调度', - - // Account type display - claudeMax: 'Claude Max', - claudePro: 'Claude Pro', - claudeFree: 'Claude Free', - - // Platform display - openaiResponsesPlatform: 'OpenAI-Responses', - ccrPlatform: 'CCR' - }, - apiKeys: { - // Page title and description - title: 'API Keys 管理', - description: '管理和监控您的 API 密钥', - - // Tab navigation - activeTab: '活跃 API Keys', - deletedTab: '已删除 API Keys', - - // Toolbar and actions - refresh: '刷新', - refreshTooltip: '刷新 API Keys 列表(Ctrl+点击强制刷新)', - createNew: '创建新 Key', - bulkEdit: '编辑选中', - bulkDelete: '删除选中', - - // Table headers - name: '名称', - tags: '标签', - status: '状态', - usageStats: '使用统计', - createdAt: '创建时间', - expiresAt: '过期时间', - actions: '操作', - - // Filter options - timeRange: { - today: '今日', - week: '最近7天', - month: '本月', - all: '全部时间' - }, - - // Status - active: '活跃', - disabled: '禁用', - expired: '已过期', - expiringSoon: '即将过期', - neverExpire: '永不过期', - notActivated: '未激活', - - // Usage statistics - dailyCost: '今日费用', - totalCost: '总费用', - dailyRequests: '今日请求', - lastUsed: '最后使用', - neverUsed: '从未使用', - minutesAgo: '{minutes} 分钟前', - hoursAgo: '{hours} 小时前', - daysAgo: '{days} 天前', - justNow: '刚刚', - requests: '次', - - // Search and filter - searchPlaceholder: '搜索名称...', - searchPlaceholderWithOwner: '搜索名称或所有者...', - allTags: '所有标签', - noTags: '无标签', - - // Binding information - shared: '使用共享池', - dedicated: '专属', - consoleAccount: 'Console账户', - bindingWarning: '⚠️ 账户不存在', - - // Limits and quotas - dailyLimit: '每日费用', - weeklyOpusLimit: 'Opus周费用', - remainingQuota: '剩余: ${amount}', - reset: '重置于 {time}', - quotaProgress: '额度进度', - - // Model statistics - modelStats: '模型使用分布', - modelStatsCount: '{count} 个模型', - totalTokens: '总Token', - inputTokens: '输入', - outputTokens: '输出', - cacheCreate: '缓存创建', - cacheRead: '缓存读取', - totalRequests: '总请求', - noModelData: '暂无模型使用数据', - resetFilter: '刷新', - adjustTimeRange: '尝试调整时间范围或点击刷新重新加载数据', - - // Date filter - dateFilter: { - today: '今日', - days7: '7天', - days30: '30天' - }, - - // Actions - viewDetails: '查看详细统计', - edit: '编辑', - renew: '续期', - activate: '激活', - disable: '禁用', - copy: '复制', - copyStatsLink: '复制统计页面链接', - - // Pagination - totalRecords: '共 {count} 条记录', - pageSize: '每页显示', - records: '条', - - // Empty states - noApiKeys: '暂无 API Keys', - noApiKeysHint: '点击上方按钮创建您的第一个 API Key', - noDeletedKeys: '暂无已删除的 API Keys', - noDeletedKeysHint: '已删除的 API Keys 会出现在这里', - loading: '正在加载 API Keys...', - loadingDeleted: '正在加载已删除的 API Keys...', - loadingModelStats: '加载模型统计...', - - // Deleted keys table - creator: '创建者', - deletedBy: '删除者', - deletedAt: '删除时��', - canRestore: '恢复', - permanentDelete: '彻底删除', - clearAllDeleted: '清空所有已删除', - - // User types - admin: '管理员', - user: '用户', - unknown: '未知', - system: '系统', - - // Confirmation dialogs - confirmDisable: '确定要禁用 API Key "{name}" 吗?禁用后所有使用此 Key 的请求将返回 401 错误。', - confirmDelete: '确定要删除这个 API Key 吗?此操作不可恢复。', - confirmBatchDelete: '确定要删除选中的 {count} 个 API Key 吗?此操作不可恢复。', - confirmRestore: '确定要恢复这个 API Key 吗?恢复后可以重新使用。', - confirmPermanentDelete: - '确定要彻底删除这个 API Key 吗?此操作不可恢复,所有相关数据将被永久删除。', - confirmClearAll: - '确定要彻底删除全部 {count} 个已删除的 API Keys 吗?此操作不可恢复,所有相关数据将被永久删除。', - - // Success messages - keyActivated: 'API Key 已激活', - keyDisabled: 'API Key 已禁用', - keyDeleted: 'API Key 已删除', - keyRestored: 'API Key 已成功恢复', - keyPermanentDeleted: 'API Key 已彻底删除', - allDeletedCleared: '已清空所有已删除的 API Keys', - linkCopied: '已复制统计页面链接', - expiryUpdated: '过期时间已更新', - - // Error messages - selectKeysFirst: '请先选择要编辑的 API Keys', - loadFailed: '加载 API Keys 失败', - operationFailed: '操作失败', - copyFailed: '复制失败,请手动复制', - updateFailed: '更新失败', - deleteFailed: '删除失败', - restoreFailed: '恢复失败', - clearFailed: '清空失败', - - // Tooltips and helpers - editExpiry: '编辑过期时间', - activationDays: '未激活 ({days}天)', - boundTo: '绑定到', - belongsToGroup: '所属分组: {name}', - - // Batch operations - batchSuccess: '成功处理 {count} 个项目', - batchPartialFail: '{failed} 个处理失败', - batchAllFailed: '所有项目处理失败', - - // Batch API Key Modal - batchApiKeyModal: { - title: '批量创建成功', - successMessage: '成功创建 {count} 个 API Key', - importantReminder: '重要提醒', - warningMessage: - '这是您唯一能看到所有 API Key 的机会。关闭此窗口后,系统将不再显示完整的 API Key。请立即下载并妥善保存。', - - // Statistics cards - createdCount: '创建数量', - baseName: '基础名称', - permissionScope: '权限范围', - expiryTime: '过期时间', - - // Permission texts - permissions: { - all: '全部服务', - claude: '仅 Claude', - gemini: '仅 Gemini', - unknown: '未知' - }, - - // Expiry time texts - neverExpire: '永不过期', - daysFormat: '{days}天', - weeksFormat: '{weeks}周', - monthsFormat: '{months}个月', - yearsFormat: '{years}年', - - // Preview section - previewTitle: 'API Keys 预览', - hide: '隐藏', - show: '显示', - preview: '预览', - maxDisplayNote: '(最多显示前10个)', - moreKeysNote: '... 还有 {count} 个 API Key', - - // Action buttons - downloadAll: '下载所有 API Keys', - alreadySaved: '我已保存', - directCloseTooltip: '直接关闭(不推荐)', - - // File info - fileFormatInfo: - '下载的文件格式为文本文件(.txt),每行包含一个 API Key。请将文件保存在安全的位置,避免泄露。', - - // Confirmation dialogs - closeReminderTitle: '关闭提醒', - closeReminderMessage: - '关闭后将无法再次查看这些 API Key,请确保已经下载并妥善保存。\n\n确定要关闭吗?', - confirmCloseButton: '确定关闭', - goBackDownloadButton: '返回下载', - - directCloseTitle: '确定要关闭吗?', - directCloseMessage: '您还没有下载 API Keys,关闭后将无法再次查看。\n\n强烈建议您先下载保存。', - stillCloseButton: '仍然关闭', - - directCloseFallbackMessage: '您还没有下载 API Keys,关闭后将无法再次查看。\n\n确定要关闭吗?', - - // Success messages - downloadSuccess: 'API Keys 文件已下载' - }, - - // Window Countdown - windowCountdown: { - expired: '窗口已过期', - notStarted: '窗口未激活', - minutes: '分钟', - requests: '请求', - tokens: 'Token', - cost: '费用', - aboutToReset: '即将重置', - minutesUntilReset: '分钟后重置', - untilReset: '后重置', - windowLimit: '窗口限制', - hours: '小时' - }, - - // Expiry Edit Modal - expiryEditModal: { - title: '修改过期时间', - subtitle: '为 "{name}" 设置新的过期时间', - currentStatus: '当前状态', - notActivated: '未激活', - activationDaysHint: '(激活后 {days} 天过期)', - neverExpire: '永不过期', - expired: '已过期', - daysToExpire: '{days} 天后过期', - monthsToExpire: '{months} 个月后过期', - activateNow: '立即激活', - activateButton: '立即激活 (激活后 {days} 天过期)', - activationInfo: '点击立即激活此 API Key,激活后将在 {days} 天后过期', - selectNewDuration: '选择新的期限', - neverExpireOption: '永不过期', - oneDay: '1 天', - sevenDays: '7 天', - thirtyDays: '30 天', - ninetyDays: '90 天', - oneHundredEightyDays: '180 天', - threeSixtyFiveDays: '1 年', - twoYears: '2 年', - custom: '自定义', - selectDateAndTime: '选择日期和时间', - selectFutureDateTime: '选择一个未来的日期和时间作为过期时间', - newExpiryTime: '新的过期时间', - cancel: '取消', - saving: '保存中...', - saveChanges: '保存更改', - activateConfirmTitle: '激活 API Key', - activateConfirmMessage: '确定要立即激活此 API Key 吗?激活后将在 {days} 天后自动过期。', - confirmActivate: '确定激活', - confirmCancel: '取消' - }, - - // Edit API Key Modal - editApiKeyModal: { - title: '编辑 API Key', - - // Basic Info - name: '名称', - namePlaceholder: '请输入API Key名称', - nameHint: '用于识别此 API Key 的用途', - - // Owner - owner: '所有者', - adminLabel: '- 管理员', - ownerHint: '分配此 API Key 给指定用户或管理员,管理员分配时不受用户 API Key 数量限制', - - // Tags - tags: '标签', - selectedTags: '已选择的标签:', - clickToSelectTags: '点击选择已有标签:', - createNewTag: '创建新标签:', - newTagPlaceholder: '输入新标签名称', - tagsHint: '用于标记不同团队或用途,方便筛选管理', - - // Rate Limit Settings - rateLimitTitle: '速率限制设置 (可选)', - rateLimitWindow: '时间窗口 (分钟)', - rateLimitRequests: '请求次数限制', - rateLimitCost: '费用限制 (美元)', - rateLimitWindowHint: '时间段单位', - rateLimitRequestsHint: '窗口内最大请求', - rateLimitCostHint: '窗口内最大费用', - noLimit: '无限制', - - // Usage Examples - usageExamples: '💡 使用示例', - example1: '示例1: 时间窗口=60,请求次数=1000 → 每60分钟最多1000次请求', - example2: '示例2: 时间窗口=1,费用=0.1 → 每分钟最多$0.1费用', - example3: '示例3: 窗口=30,请求=50,费用=5 → 每30分钟50次请求且不超$5费用', - - // Cost Limits - dailyCostLimit: '每日费用限制 (美元)', - dailyCostLimitPlaceholder: '0 表示无限制', - dailyCostHint: '设置此 API Key 每日的费用限制,超过限制将拒绝请求,0 或留空表示无限制', - weeklyOpusCostLimit: 'Opus 模型周费用限制 (美元)', - weeklyOpusHint: - '设置 Opus 模型的周费用限制(周一到周日),仅限 Claude 官方账户,0 或留空表示无限制', - custom: '自定义', - - // Concurrency - concurrencyLimit: '并发限制', - concurrencyLimitPlaceholder: '0 表示无限制', - concurrencyHint: '设置此 API Key 可同时处理的最大请求数', - - // Active Status - activeStatus: '激活账号', - activeStatusHint: '取消勾选将禁用此 API Key,暂停所有请求,客户端返回 401 错误', - - // Service Permissions - servicePermissions: '服务权限', - allServices: '全部服务', - claudeOnly: '仅 Claude', - geminiOnly: '仅 Gemini', - openaiOnly: '仅 OpenAI', - permissionsHint: '控制此 API Key 可以访问哪些服务', - - // Account Binding - accountBinding: '专属账号绑定', - refreshAccounts: '刷新账号', - refreshing: '刷新中...', - claudeAccount: 'Claude 专属账号', - geminiAccount: 'Gemini 专属账号', - openaiAccount: 'OpenAI 专属账号', - bedrockAccount: 'Bedrock 专属账号', - useSharedPool: '使用共享账号池', - selectClaudeAccount: '请选择Claude账号', - selectGeminiAccount: '请选择Gemini账号', - selectOpenaiAccount: '请选择OpenAI账号', - selectBedrockAccount: '请选择Bedrock账号', - accountBindingHint: '修改绑定账号将影响此API Key的请求路由', - - // Model Restrictions - enableModelRestriction: '启用模型限制', - restrictedModels: '限制的模型列表', - noRestrictedModels: '暂无限制的模型', - allCommonModelsRestricted: '所有常用模型已在限制列表中', - addRestrictedModelPlaceholder: '输入模型名称,按回车添加', - modelRestrictionHint: '设置此API Key无法访问的模型,例如:claude-opus-4-20250514', - - // Client Restrictions - enableClientRestriction: '启用客户端限制', - allowedClients: '允许的客户端', - clientRestrictionHint: '勾选允许使用此API Key的客户端', - - // Buttons - cancel: '取消', - save: '保存修改', - saving: '保存中...', - - // Messages - costLimitConfirmTitle: '费用限制提醒', - costLimitConfirmMessage: - '您设置了时间窗口但费用限制为0,这意味着不会有费用限制。\n\n是否继续?', - costLimitConfirmContinue: '继续保存', - costLimitConfirmBack: '返回修改', - refreshAccountsSuccess: '账号列表已刷新', - refreshAccountsFailed: '刷新账号列表失败', - updateFailed: '更新失败' - }, - - // Renew API Key Modal - renewApiKeyModal: { - title: '续期 API Key', - apiKeyInfo: 'API Key 信息', - currentExpiry: '当前过期时间:', - neverExpires: '永不过期', - renewDuration: '续期时长', - extend7Days: '延长 7 天', - extend30Days: '延长 30 天', - extend90Days: '延长 90 天', - extend180Days: '延长 180 天', - extend365Days: '延长 365 天', - customDate: '自定义日期', - setPermanent: '设为永不过期', - newExpiry: '新的过期时间:', - cancel: '取消', - renewing: '续期中...', - confirmRenew: '确认续期', - renewSuccess: 'API Key 续期成功', - renewFailed: '续期失败' - }, - - // Batch Edit API Key Modal - batchEditApiKeyModal: { - title: '批量编辑 API Keys ({count} 个)', - - // Info section - infoTitle: '批量编辑说明', - infoContent: - '以下设置将应用到所选的 {count} 个 API Key。只有填写或修改的字段才会被更新,空白字段将保持原值不变。', - - // Tag operations - tagLabel: '标签 (批量操作)', - tagOperations: { - replace: '替换标签', - add: '添加标签', - remove: '移除标签', - none: '不修改标签' - }, - - // Tag status texts - newTagsList: '新标签列表:', - tagsToAdd: '要添加的标签:', - tagsToRemove: '要移除的标签:', - clickToSelectTags: '点击选择已有标签:', - createNewTag: '创建新标签:', - inputNewTagPlaceholder: '输入新标签名称', - - // Rate limit settings - rateLimitTitle: '速率限制设置', - rateLimitWindow: '时间窗口 (分钟)', - rateLimitRequests: '请求次数限制', - rateLimitCost: '费用限制 (美元)', - noModifyPlaceholder: '不修改', - - // Daily cost limit - dailyCostLimit: '每日费用限制 (美元)', - dailyCostLimitPlaceholder: '不修改 (0 表示无限制)', - - // Weekly Opus cost limit - weeklyOpusCostLimit: 'Opus 模型周费用限制 (美元)', - weeklyOpusCostLimitPlaceholder: '不修改 (0 表示无限制)', - opusLimitDescription: '设置 Opus 模型的周费用限制(周一到周日),仅限 Claude 官方账户', - - // Concurrency limit - concurrencyLimit: '并发限制', - concurrencyLimitPlaceholder: '不修改 (0 表示无限制)', - - // Active status - activeStatus: '激活状态', - statusOptions: { - active: '激活', - disabled: '禁用', - noChange: '不修改' - }, - - // Service permissions - servicePermissions: '服务权限', - permissionOptions: { - noChange: '不修改', - all: '全部服务', - claude: '仅 Claude', - gemini: '仅 Gemini', - openai: '仅 OpenAI' - }, - - // Account binding - accountBinding: '专属账号绑定', - refreshAccounts: '刷新账号', - refreshing: '刷新中...', - - claudeAccount: 'Claude 专属账号', - geminiAccount: 'Gemini 专属账号', - openaiAccount: 'OpenAI 专属账号', - bedrockAccount: 'Bedrock 专属账号', - - accountOptions: { - noChange: '不修改', - sharedPool: '使用共享账号池', - groupPrefix: '分组 - ' - }, - - // Optgroup labels - optgroupLabels: { - accountGroups: '账号分组', - dedicatedAccounts: '专属账号' - }, - - // Buttons - cancel: '取消', - saving: '保存中...', - batchSave: '批量保存', - - // Messages - refreshAccountsSuccess: '账号列表已刷新', - refreshAccountsFailed: '刷新账号列表失败', - batchEditSuccess: '成功批量编辑 {count} 个 API Keys', - batchEditPartialFail: '{failedCount} 个编辑失败:\n{errors}', - batchEditAllFailed: '所有 API Keys 编辑失败', - batchEditFailed: '批量编辑失败', - batchEditErrorLog: '批量编辑 API Keys 失败:' - }, - - // Usage Detail Modal - usageDetailModal: { - title: '使用统计详情', - close: '关闭', - - // Statistics cards - totalRequests: '总请求数', - totalTokens: '总Token数', - totalCost: '总费用', - averageRate: '平均速率', - - // Today stats - today: '今日', - todayRequests: '{count} 次', - todayTokens: '{count}', - todayCost: '${amount}', - - // Usage labels - times: '次', - - // Token distribution - tokenDistribution: 'Token 使用分布', - inputTokens: '输入 Token', - outputTokens: '输出 Token', - cacheCreateTokens: '缓存创建 Token', - cacheReadTokens: '缓存读取 Token', - - // Limits section - limitSettings: '限制设置', - dailyCostLimit: '每日费用限制', - concurrencyLimit: '并发限制', - timeWindowLimit: '时间窗口限制', - windowStatus: '窗口状态', - used: '已使用', - remainingQuota: '剩余: ${amount}', - - // Progress indicators - usedPercentage: '已使用 {percentage}%' - }, - - // Create API Key Modal - newApiKeyModal: { - title: 'API Key 创建成功', - subtitle: '请妥善保存您的 API Key', - directCloseTooltip: '直接关闭(不推荐)', - - // 警告提示 - warningTitle: '重要提醒', - warningMessage: - '这是您唯一能看到完整 API Key 的机会。关闭此窗口后,系统将不再显示完整的 API Key。请立即复制并妥善保存。', - - // 字段标签 - apiKeyName: 'API Key 名称', - remarks: '备注', - noDescription: '无描述', - apiKey: 'API Key', - - // 可见性切换 - hideApiKey: '隐藏API Key', - showFullApiKey: '显示完整API Key', - visibilityHint: '点击眼睛图标切换显示模式,使用下方按钮复制完整 API Key', - - // 按钮 - copyApiKey: '复制 API Key', - alreadySaved: '我已保存', - - // 确认对话框 - closeReminderTitle: '关闭提醒', - closeReminderMessage: - '关闭后将无法再次查看完整的API Key,请确保已经妥善保存。\n\n确定要关闭吗?', - confirmClose: '确定关闭', - cancel: '取消', - - directCloseTitle: '确定要关闭吗?', - directCloseMessage: - '您还没有保存API Key,关闭后将无法再次查看。\n\n建议您先复制API Key再关闭。', - stillClose: '仍然关闭', - goBack: '返回复制', - - directCloseFallback: '您还没有保存API Key,关闭后将无法再次查看。\n\n确定要关闭吗?', - - // 成功消息 - apiKeyNotFound: 'API Key 不存在', - copySuccess: 'API Key 已复制到剪贴板', - copyFailed: '复制失败,请手动复制' - }, - - createApiKeyModal: { - title: '创建新的 API Key', - - // Create type section - createType: '创建类型', - singleCreate: '单个创建', - batchCreate: '批量创建', - batchCount: '创建数量', - batchCountPlaceholder: '输入数量 (2-500)', - maxSupported: '最大支持 500 个', - batchHint: '批量创建时,每个 Key 的名称会自动添加序号后缀,例如:{name}_1, {name}_2 ...', - - // Basic form fields - name: '名称', - nameRequired: '*', - nameError: '请输入API Key名称', - singleNamePlaceholder: '为您的 API Key 取一个名称', - batchNamePlaceholder: '输入基础名称(将自动添加序号)', - description: '备注 (可选)', - descriptionPlaceholder: '描述此 API Key 的用途...', - - // Tags section - tags: '标签', - selectedTags: '已选择的标签:', - clickToSelectTags: '点击选择已有标签:', - createNewTag: '创建新标签:', - newTagPlaceholder: '输入新标签名称', - tagHint: '用于标记不同团队或用途,方便筛选管理', - - // Rate limit section - rateLimitTitle: '速率限制设置 (可选)', - rateLimitWindow: '时间窗口 (分钟)', - rateLimitRequests: '请求次数限制', - rateLimitCost: '费用限制 (美元)', - rateLimitWindowPlaceholder: '无限制', - rateLimitRequestsPlaceholder: '无限制', - rateLimitCostPlaceholder: '无限制', - rateLimitWindowHint: '时间段单位', - rateLimitRequestsHint: '窗口内最大请求', - rateLimitCostHint: '窗口内最大费用', - - // Rate limit examples - exampleTitle: '💡 使用示例', - example1: '示例1: 时间窗口=60,请求次数=1000 → 每60分钟最多1000次请求', - example2: '示例2: 时间窗口=1,费用=0.1 → 每分钟最多$0.1费用', - example3: '示例3: 窗口=30,请求=50,费用=5 → 每30分钟50次请求且不超$5费用', - - // Cost limits - dailyCostLimit: '每日费用限制 (美元)', - dailyCostLimitPlaceholder: '0 表示无限制', - dailyCostHint: '设置此 API Key 每日的费用限制,超过限制将拒绝请求,0 或留空表示无限制', - weeklyOpusCostLimit: 'Opus 模型周费用限制 (美元)', - weeklyOpusCostLimitPlaceholder: '0 表示无限制', - weeklyOpusHint: - '设置 Opus 模型的周费用限制(周一到周日),仅限 Claude 官方账户,0 或留空表示无限制', - custom: '自定义', - - // Concurrency limit - concurrencyLimit: '并发限制 (可选)', - concurrencyLimitPlaceholder: '0 表示无限制', - concurrencyHint: '设置此 API Key 可同时处理的最大请求数,0 或留空表示无限制', - - // Expiration settings - expirationSettings: '过期设置', - fixedTimeExpiry: '固定时间过期', - activationExpiry: '首次使用后激活', - fixedModeHint: '固定时间模式:Key 创建后立即生效,按设定时间过期', - activationModeHint: 'Key 首次使用时激活,激活后按设定天数过期(适合批量销售)', - - // Expiration duration options - neverExpire: '永不过期', - '1d': '1 天', - '7d': '7 天', - '30d': '30 天', - '90d': '90 天', - '180d': '180 天', - '365d': '365 天', - customDate: '自定义日期', - - // Activation mode - activationDays: '输入天数', - daysUnit: '天', - activationHint: 'Key 将在首次使用后激活,激活后 {days} 天过期', - - // Expiry status - willExpireOn: '将于 {date} 过期', - - // Service permissions - servicePermissions: '服务权限', - allServices: '全部服务', - claudeOnly: '仅 Claude', - geminiOnly: '仅 Gemini', - openaiOnly: '仅 OpenAI', - permissionHint: '控制此 API Key 可以访问哪些服务', - - // Account binding - dedicatedAccountBinding: '专属账号绑定 (可选)', - refreshAccounts: '刷新账号', - refreshing: '刷新中...', - claudeDedicatedAccount: 'Claude 专属账号', - geminiDedicatedAccount: 'Gemini 专属账号', - openaiDedicatedAccount: 'OpenAI 专属账号', - bedrockDedicatedAccount: 'Bedrock 专属账号', - useSharedPool: '使用共享账号池', - accountBindingHint: '选择专属账号后,此API Key将只使用该账号,不选择则使用共享账号池', - selectClaudeAccount: '请选择Claude账号', - selectGeminiAccount: '请选择Gemini账号', - selectOpenaiAccount: '请选择OpenAI账号', - selectBedrockAccount: '请选择Bedrock账号', - - // Model restrictions - enableModelRestriction: '启用模型限制', - restrictedModelsList: '限制的模型列表', - noRestrictedModels: '暂无限制的模型', - allCommonModelsRestricted: '所有常用模型已在限制列表中', - addRestrictedModelPlaceholder: '输入模型名称,按回车添加', - modelRestrictionHint: '设置此API Key无法访问的模型,例如:claude-opus-4-20250514', - - // Client restrictions - enableClientRestriction: '启用客户端限制', - allowedClients: '允许的客户端', - - // Buttons - cancel: '取消', - create: '创建', - creating: '创建中...', - - // Messages - batchCountError: '批量创建数量必须在 2-500 之间', - costLimitConfirmTitle: '费用限制提醒', - costLimitConfirmMessage: - '您设置了时间窗口但费用限制为0,这意味着不会有费用限制。\n\n是否继续?', - costLimitConfirmContinue: '继续创建', - costLimitConfirmBack: '返回修改', - costLimitFallbackMessage: - '您设置了时间窗口但费用限制为0,这意味着不会有费用限制。\n是否继续?', - createSuccess: 'API Key 创建成功', - batchCreateSuccess: '成功创建 {count} 个 API Key', - createFailed: '创建失败', - batchCreateFailed: '批量创建失败', - refreshAccountsSuccess: '账号列表已刷新', - refreshAccountsFailed: '刷新账号列表失败' - } - }, - - // User-related translations - user: { - // User Dashboard - dashboard: { - title: 'Dashboard Overview', - welcomeMessage: 'Welcome to your Claude Relay dashboard', - - // Navigation tabs - overview: 'Overview', - apiKeys: 'API Keys', - usageStats: 'Usage Stats', - - // Welcome section - welcome: 'Welcome', - - // Stats cards - activeApiKeys: 'Active API Keys', - deletedApiKeys: 'Deleted API Keys', - totalRequests: 'Total Requests', - inputTokens: 'Input Tokens', - totalCost: 'Total Cost', - - // Account information section - accountInformation: 'Account Information', - username: 'Username', - displayName: 'Display Name', - email: 'Email', - role: 'Role', - memberSince: 'Member Since', - lastLogin: 'Last Login', - notAvailable: 'N/A', - - // Messages - logout: 'Logout', - logoutSuccess: 'Logged out successfully', - logoutFailed: 'Logout failed', - loadProfileFailed: 'Failed to load user profile', - loadStatsFailed: 'Failed to load API keys stats' - }, - - // User API Keys Manager - userApiKeysManager: { - title: '我的 API Keys', - description: '管理您的 API Keys 以访问 Claude Relay 服务', - loading: '正在加载 API Keys...', - warnings: { - maxKeysReached: - '您已达到 API Keys 的最大数量限制({maxApiKeys} 个)。请删除现有的 Key 以创建新的。' - }, - status: { - deleted: '已删除', - noDescription: '无描述', - neverUsed: '从未使用' - }, - dateLabels: { - created: '创建时间', - deleted: '删除时间', - lastUsed: '最后使用', - expires: '到期时间' - }, - usage: { - requests: '次请求' - }, - actions: { - viewApiKey: '查看 API Key', - deleteApiKey: '删除 API Key' - }, - buttons: { - createApiKey: '创建 API Key', - delete: '删除' - }, - emptyState: { - title: '无 API Keys', - description: '创建您的第一个 API Key 开始使用。' - }, - confirmDelete: { - title: '删除 API Key', - message: "确定要删除 '{name}' 吗?此操作无法撤销。" - }, - messages: { - loadFailed: '加载 API Keys 失败', - deleteSuccess: 'API Key 删除成功', - deleteFailed: '删除 API Key 失败' - } - }, - - // User Login - login: { - title: 'User Sign In', - subtitle: 'Sign in to your account to manage your API keys', - username: 'Username', - password: 'Password', - usernamePlaceholder: 'Enter your username', - passwordPlaceholder: 'Enter your password', - signIn: 'Sign In', - signingIn: 'Signing In...', - adminLogin: 'Admin Login', - - // Validation and error messages - requiredFields: 'Please enter both username and password', - loginSuccess: 'Login successful!', - loginFailed: 'Login failed', - accountDisabled: '您的账号已被禁用' - }, - - // View API Key Modal - viewApiKeyModal: { - title: 'API Key 详情', - fields: { - name: '名称', - description: '描述', - apiKey: 'API Key', - status: '状态', - usageStatistics: '使用统计' - }, - apiKeyDisplay: { - notAvailable: '不可用', - keyPreview: 'cr_****', - fullKeyNotice: '完整 API Key 仅在首次创建或重新生成时显示' - }, - buttons: { - hide: '隐藏', - show: '显示', - copy: '复制', - close: '关闭' - }, - status: { - active: '启用', - disabled: '禁用' - }, - usageStats: { - requests: '请求次数', - inputTokens: '输入令牌', - outputTokens: '输出令牌', - totalCost: '总费用' - }, - timestamps: { - created: '创建时间', - lastUsed: '最后使用', - expires: '过期时间' - }, - messages: { - copySuccess: '已复制到剪贴板!', - copyFailed: '复制到剪贴板失败' - } - }, - - // User Management - management: { - title: 'User Management', - description: 'Manage users, their API keys, and view usage statistics', - refresh: 'Refresh', - - // Stats cards - totalUsers: 'Total Users', - activeUsers: 'Active Users', - totalApiKeys: 'Total API Keys', - totalCost: 'Total Cost', - - // Search and filters - searchPlaceholder: 'Search users...', - allRoles: 'All Roles', - user: 'User', - admin: 'Admin', - allStatus: 'All Status', - active: 'Active', - disabled: 'Disabled', - - // User list - users: 'Users', - loadingUsers: 'Loading users...', - noUsersFound: 'No users found', - noUsersMatch: 'No users match your search criteria.', - noUsersCreated: 'No users have been created yet.', - - // User info and actions - displayName: 'Display Name', - email: 'Email', - role: 'Role', - username: 'Username', - apiKeysCount: 'API keys', - lastLogin: 'Last login', - neverLoggedIn: 'Never logged in', - requests: 'requests', - totalCostLabel: 'total cost', - - // Action buttons and tooltips - viewUsageStats: 'View Usage Stats', - disableAllApiKeys: 'Disable All API Keys', - disableUser: 'Disable User', - enableUser: 'Enable User', - changeRole: 'Change Role', - - // Confirmation dialogs - disableUserTitle: 'Disable User', - enableUserTitle: 'Enable User', - disableUserMessage: - 'Are you sure you want to disable user "{username}"? This will prevent them from logging in.', - enableUserMessage: 'Are you sure you want to enable user "{username}"?', - disable: 'Disable', - enable: 'Enable', - - disableAllKeysTitle: 'Disable All API Keys', - disableAllKeysMessage: - 'Are you sure you want to disable all {count} API keys for user "{username}"? This will prevent them from using the service.', - disableKeys: 'Disable Keys', - - // Success messages - userDisabledSuccess: 'User disabled successfully', - userEnabledSuccess: 'User enabled successfully', - keysDisabledSuccess: 'Disabled {count} API keys', - - // Error messages - loadUsersError: 'Failed to load users', - toggleStatusError: 'Failed to toggleStatus', - disableKeysError: 'Failed to disableKeys' - }, - - // User Usage Stats Modal - usageStatsModal: { - title: '使用统计', - titleWithUser: '使用统计 - {displayName}', - - // Time period selection - periodSelection: { - day: '最近24小时', - week: '最近7天', - month: '最近30天', - quarter: '最近90天' - }, - - // Loading state - loadingStats: '正在加载使用统计...', - - // Summary cards - summaryCards: { - requests: '请求数', - inputTokens: '输入Token', - outputTokens: '输出Token', - totalCost: '总费用' - }, - - // API Keys table - apiKeysTable: { - title: 'API Keys 使用情况', - headers: { - apiKey: 'API Key', - status: '状态', - requests: '请求数', - tokens: 'Token数', - cost: '费用', - lastUsed: '最后使用' - }, - status: { - active: '活跃', - disabled: '已禁用' - }, - tokensFormat: { - input: '输入', - output: '输出' - }, - never: '从未使用' - }, - - // Usage trend chart - usageTrend: { - title: '使用趋势', - chartTitle: '使用图表', - dailyTrends: '最近 {period} 的日使用趋势', - chartNote: '(可集成 Chart.js、D3.js 或类似图表库)' - }, - - // No data state - noData: { - title: '暂无使用数据', - description: '该用户在所选时间段内尚未发起任何API请求。' - }, - - // Buttons - close: '关闭' - }, - - // Change Role Modal - changeRoleModal: { - title: '修改用户角色', - - // User info display - currentRole: '当前角色:{role}', - - // Role selection form - newRole: '新角色', - roles: { - user: '用户', - userDesc: '具有基本权限的普通用户', - admin: '管理员', - adminDesc: '拥有管理用户和系统的完整权限' - }, - - // Warning messages - roleChangeWarning: { - title: '角色变更警告', - grantAdmin: - '授予管理员权限将使该用户拥有系统的完整访问权限,包括管理其他用户及其API密钥的能力。', - removeAdmin: '移除管理员权限将限制该用户只能管理自己的API密钥和查看自己的使用统计。' - }, - - // Buttons - cancel: '取消', - updateRole: '更新角色', - updating: '更新中...', - - // Success message - roleUpdated: '用户角色已更新为 {role}' - }, - - // User Usage Statistics - userUsageStats: { - // Page header - title: '使用统计', - subtitle: '查看您的 API 使用统计和费用', - - // Time period selection - periodSelection: { - day: '最近24小时', - week: '最近7天', - month: '最近30天', - quarter: '最近90天' - }, - - // Loading state - loadingStats: '正在加载使用统计...', - - // Statistics cards - statsCards: { - totalRequests: '总请求数', - inputTokens: '输入Token', - outputTokens: '输出Token', - totalCost: '总费用' - }, - - // Daily usage trend chart - usageTrend: { - title: '日使用趋势', - chartTitle: '使用图表', - dailyTrendsDescription: '这里将显示日使用趋势', - chartIntegrationNote: '(可集成 Chart.js、D3.js 或类似图表库)' - }, - - // Usage by model section - modelUsage: { - title: '按模型使用情况', - requests: '请求', - requestsCount: '{count} 请求' - }, - - // Usage by API key table - apiKeyUsage: { - title: '按 API Key 使用情况', - headers: { - apiKey: 'API Key', - requests: '请求数', - inputTokens: '输入Token', - outputTokens: '输出Token', - cost: '费用', - status: '状态' - }, - status: { - active: '活跃', - disabled: '已禁用', - deleted: '已删除' - } - }, - - // No data state - noData: { - title: '暂无使用数据', - description: - '您还没有发起任何 API 请求。创建一个 API Key 并开始使用服务后,就能看到使用统计了。' - }, - - // Error messages - loadFailed: '加载使用统计失败' - }, - - // Create API Key Modal - createApiKeyModal: { - title: '创建新的 API Key', - - // 表单标签和占位符 - form: { - nameLabel: '名称', - nameRequired: '*', - namePlaceholder: '为您的 API Key 取一个名称', - descriptionLabel: '备注', - descriptionPlaceholder: '可选的备注信息' - }, - - // 按钮文本 - buttons: { - cancel: '取消', - creating: '创建中...', - createApiKey: '创建 API Key', - copy: '复制', - done: '完成' - }, - - // 成功状态 - success: { - title: 'API Key 创建成功!', - warning: { - important: '重要提示:', - message: '请立即复制您的 API Key,您将无法再次查看!' - } - }, - - // 错误和验证消息 - validation: { - nameRequired: 'API Key 名称是必填项' - }, - - errors: { - createFailed: '创建 API Key 失败' - }, - - // Toast 消息 - messages: { - createSuccess: 'API Key 创建成功!', - copySuccess: 'API Key 已复制到剪贴板!', - copyFailed: '复制到剪贴板失败' - } - } - }, - - // Settings 设置页面 - settings: { - title: '系统设置', - description: '网站定制和通知配置', - loading: '正在加载设置...', - - // 导航标签 - branding: '品牌设置', - webhook: '通知设置', - - // 品牌设置 - siteName: '网站名称', - siteNameDescription: '品牌标识', - siteNamePlaceholder: 'Claude Relay Service', - siteNameHint: '将显示在浏览器标题和页面头部', - - siteIcon: '网站图标', - siteIconDescription: 'Favicon', - currentIcon: '当前图标', - uploadIcon: '上传图标', - removeIcon: '删除', - iconFormats: '支持 .ico, .png, .jpg, .svg 格式,最大 350KB', - iconPreview: '图标预览', - validation: { - iconTooLarge: '图标文件大小不能超过 350KB', - iconTypeNotSupported: '不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件' - }, - - adminEntry: '管理入口', - adminEntryDescription: '登录按钮显示', - hideLoginButton: '隐藏登录按钮', - showLoginButton: '显示登录按钮', - adminEntryHint: '隐藏后,用户需要直接访问 /admin/login 页面登录', - - // 移动端卡片标题 - siteNameCard: '站点名称', - siteNameCardDesc: '自定义您的站点品牌名称', - siteIconCard: '站点图标', - siteIconCardDesc: '上传自定义图标或输入图标URL', - adminEntryCard: '管理入口', - adminEntryCardDesc: '控制登录按钮在首页的显示', - - // 操作按钮 - save: '保存设置', - saving: '保存中...', - reset: '重置为默认', - lastUpdated: '最后更新:{time}', - lastUpdatedMobile: '上次更新: {time}', - - // Webhook 设置 - enableWebhook: '启用 Webhook 通知', - webhookDescription: '开启后,系统将按配置发送通知到指定平台', - - // 通知类型 - notificationTypes: '通知类型', - accountAnomaly: '账号异常', - quotaWarning: '配额警告', - systemError: '系统错误', - securityAlert: '安全警报', - accountAnomalyDesc: '账号状态异常、认证失败等', - quotaWarningDesc: 'API调用配额不足警告', - systemErrorDesc: '系统运行错误和故障', - securityAlertDesc: '安全相关的警报通知', - - // 通知平台 - notificationPlatforms: '通知平台', - addPlatform: '添加平台', - noPlatforms: '暂无配置的通知平台,请点击"添加平台"按钮添加', - enableSignature: '已启用签名验证', - testConnection: '测试连接', - edit: '编辑', - delete: '删除', - - // 高级设置 - advancedSettings: '高级设置', - maxRetries: '最大重试次数', - retryDelay: '重试延迟 (毫秒)', - timeout: '超时时间 (毫秒)', - - // 测试通知 - sendTestNotification: '发送测试通知', - - // 平台模态框 - addPlatformModal: '添加通知平台', - editPlatformModal: '编辑通知平台', - configurePlatform: '配置新的Webhook通知渠道', - updatePlatform: '配置并更新Webhook通知渠道', - - platformType: '平台类型', - platformName: '名称', - platformNameOptional: '(可选)', - platformNamePlaceholder: '例如:运维群通知、开发测试群', - - webhookUrl: 'Webhook URL', - webhookUrlRequired: '*', - webhookUrlPlaceholder: 'https://...', - editModeWarning: '编辑模式下不能更改平台类型', - cannotChangePlatformType: '编辑模式下不能更改平台类型', - serverUrl: '服务器地址', - requiredField: '必填项', - - // Bark 特有设置 - deviceKey: '设备密钥 (Device Key)', - deviceKeyPlaceholder: '例如:aBcDeFgHiJkLmNoPqRsTuVwX', - deviceKeyHint: '在Bark App中查看您的推送密钥', - serverAddress: '服务器地址', - serverAddressOptional: '(可选)', - serverAddressPlaceholder: '默认: https://api.day.app/push', - notificationLevel: '通知级别', - notificationSound: '通知声音', - notificationGroup: '通知分组', - notificationGroupOptional: '(可选)', - notificationGroupPlaceholder: '默认: claude-relay', - - // 通知级别选项 - levelAuto: '自动(根据通知类型)', - levelPassive: '被动', - levelActive: '默认', - levelTimeSensitive: '时效性', - levelCritical: '紧急', - - // 声音选项 - soundAuto: '自动(根据通知类型)', - soundDefault: '默认', - soundAlarm: '警报', - soundBell: '铃声', - soundBirdsong: '鸟鸣', - soundElectronic: '电子音', - soundGlass: '玻璃', - soundHorn: '喇叭', - soundSilence: '静音', - - // Bark 提示信息 - barkInstructions: [ - '1. 在iPhone上安装Bark App', - '2. 打开App获取您的设备密钥', - '3. 将密钥粘贴到上方输入框' - ], - - // 签名设置 - enableSignatureVerify: '启用签名验证', - signatureEnabled: '已启用', - signatureSecret: '签名密钥', - signatureSecretPlaceholder: 'SEC...', - - // 平台提示信息 - wechatWorkHint: '请在企业微信群机器人设置中获取Webhook地址', - dingtalkHint: '请在钉钉群机器人设置中获取Webhook地址', - feishuHint: '请在飞书群机器人设置中获取Webhook地址', - slackHint: '请在Slack应用的Incoming Webhooks中获取地址', - discordHint: '请在Discord服务器的集成设置中创建Webhook', - barkHint: '请在Bark App中查看您的设备密钥', - customHint: '请输入完整的Webhook接收地址', - - // 模态框按钮 - required: '必填项', - cancel: '取消', - testing: '测试中...', - saveChanges: '保存修改', - addPlatformBtn: '添加平台', - - // 成功/错误消息 - loadSettingsFailed: '加载设置失败', - settingsSaved: '设置已保存', - saveSettingsFailed: '保存设置失败', - oemSettingsSaved: 'OEM设置保存成功', - oemSettingsSaveFailed: '保存OEM设置失败', - resetToDefault: '已重置为默认设置', - resetFailed: '重置失败', - confirmReset: '确定要重置为默认设置吗?\n\n这将清除所有自定义的网站名称和图标设置。', - - webhookConfigSaved: '配置已保存', - webhookConfigSaveFailed: '保存配置失败', - getWebhookConfigFailed: '获取webhook配置失败', - - platformAdded: '平台已添加', - platformUpdated: '平台已更新', - platformDeleted: '平台已删除', - platformDeleteFailed: '删除失败', - confirmDeletePlatform: '确定要删除这个平台吗?', - operationFailed: '操作失败', - - testSuccess: '测试成功,webhook连接正常', - testFailed: '测试失败', - testNotificationSent: '测试通知已发送', - testNotificationFailed: '发送失败', - - // 表单验证消息 - enterBarkDeviceKey: '请输入Bark设备密钥', - enterWebhookUrl: '请输入Webhook URL', - enterValidWebhookUrl: '请输入有效的Webhook URL', - enterWebhookUrlFirst: '请先输入Webhook URL', - enterBarkDeviceKeyFirst: '请先输入Bark设备密钥', - - // 文件上传 - fileReadFailed: '文件读取失败', - iconLoadFailed: 'Icon failed to load', - - // 平台名称 - platforms: { - wechatWork: '企业微信', - dingtalk: '钉钉', - feishu: '飞书', - slack: 'Slack', - discord: 'Discord', - bark: 'Bark', - custom: '自定义' - } - }, - - // AccountForm 组件 - accountForm: { - // 标题和模态框 - editAccount: '编辑账户', - addAccount: '添加账户', - - // 步骤指示器 - stepBasicInfo: '基本信息', - stepAuthorization: '授权认证', - - // 平台选择 - platform: '平台', - platformClaude: 'Claude', - platformClaudeConsole: 'Claude Console', - platformGemini: 'Gemini', - platformOpenAI: 'OpenAI', - platformAzureOpenAI: 'Azure OpenAI', - platformBedrock: 'Bedrock', - - // 添加方式 - addMethod: '添加方式', - addTypeSetupToken: 'Setup Token (推荐)', - addTypeOAuth: 'OAuth 授权', - addTypeManual: '手动输入 Access Token', - - // 基本信息字段 - accountName: '账户名称', - accountNamePlaceholder: '为账户设置一个易识别的名称', - description: '描述', - descriptionOptional: '描述 (可选)', - descriptionPlaceholder: '账户用途说明...', - - // 账户类型 - accountType: '账户类型', - accountTypeShared: '共享账户', - accountTypeDedicated: '专属账户', - accountTypeGroup: '分组调度', - accountTypeDescription: - '共享账户:供所有API Key使用;专属账户:仅供特定API Key使用;分组调度:加入分组供分组内调度', - - // 分组选择 - selectGroup: '选择分组', - selectGroupRequired: '选择分组 *', - noGroupsAvailable: '暂无可用分组', - memberCount: '个成员', - newGroup: '新建分组', - refreshGroups: '刷新分组', - - // Gemini 项目 ID - projectId: '项目 ID', - projectIdOptional: '项目 ID (可选)', - projectIdPlaceholder: '例如:verdant-wares-464411-k9', - projectIdDescription: 'Google Cloud/Workspace 账号需要提供项目 ID', - projectIdInstructions: '如何获取项目 ID:', - projectIdStep1: '访问 Google Cloud Console', - projectIdStep2: '复制项目 ID(Project ID),通常是字符串格式', - projectIdStep3: '⚠️ 注意:要复制项目 ID(Project ID),不要复制项目编号(Project Number)!', - projectIdTip: '提示:如果您的账号是普通个人账号(未绑定 Google Cloud),请留空此字段。', - projectIdGoogleCloudRequired: 'Google Cloud/Workspace 账号需要提供项目 ID', - projectIdGoogleCloudDescription: - '某些 Google 账号(特别是绑定了 Google Cloud 的账号)会被识别为 Workspace 账号,需要提供额外的项目 ID。', - - // Bedrock 字段 - awsAccessKeyId: 'AWS 访问密钥 ID', - awsAccessKeyIdRequired: 'AWS 访问密钥 ID *', - awsAccessKeyIdPlaceholder: '请输入 AWS Access Key ID', - awsSecretAccessKey: 'AWS 秘密访问密钥', - awsSecretAccessKeyRequired: 'AWS 秘密访问密钥 *', - awsSecretAccessKeyPlaceholder: '请输入 AWS Secret Access Key', - awsRegion: 'AWS 区域', - awsRegionRequired: 'AWS 区域 *', - awsRegionPlaceholder: '例如:us-east-1', - awsRegionReference: '常用 AWS 区域参考:', - awsRegionUsEast1: '• us-east-1 (美国东部)', - awsRegionUsWest2: '• us-west-2 (美国西部)', - awsRegionEuWest1: '• eu-west-1 (欧洲爱尔兰)', - awsRegionApSoutheast1: '• ap-southeast-1 (新加坡)', - awsRegionApNortheast1: '• ap-northeast-1 (东京)', - awsRegionEuCentral1: '• eu-central-1 (法兰克福)', - awsRegionTip: '💡 请输入完整的区域代码,如 us-east-1', - sessionToken: '会话令牌', - sessionTokenOptional: '会话令牌 (可选)', - sessionTokenPlaceholder: '如果使用临时凭证,请输入会话令牌', - sessionTokenDescription: '仅在使用临时 AWS 凭证时需要填写', - defaultModel: '默认主模型', - defaultModelOptional: '默认主模型 (可选)', - defaultModelPlaceholder: '例如:us.anthropic.claude-sonnet-4-20250514-v1:0', - defaultModelDescription: '留空将使用系统默认模型。支持 inference profile ID 或 ARN', - bedrockModelConfigTitle: 'Bedrock 模型配置说明:', - bedrockModelConfigInferenceProfile: '• 支持 Inference Profile ID(推荐)', - bedrockModelConfigArn: '• 支持 Application Inference Profile ARN', - bedrockModelConfigCommon: '• 常用模型:us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockModelConfigDefault: '• 留空将使用系统配置的默认模型', - smallFastModel: '小快速模型', - smallFastModelOptional: '小快速模型 (可选)', - smallFastModelPlaceholder: '例如:us.anthropic.claude-3-5-haiku-20241022-v1:0', - smallFastModelDescription: '用于快速响应的轻量级模型,留空将使用系统默认', - - // Azure OpenAI 字段 - azureEndpoint: 'Azure Endpoint', - azureEndpointRequired: 'Azure Endpoint *', - azureEndpointPlaceholder: 'https://your-resource.openai.azure.com', - azureEndpointDescription: - 'Azure OpenAI 资源的终结点 URL,格式:https://your-resource.openai.azure.com', - apiVersion: 'API 版本', - apiVersionPlaceholder: '2024-02-01', - apiVersionDescription: 'Azure OpenAI API 版本,默认使用最新稳定版本 2024-02-01', - deploymentName: '部署名称', - deploymentNameRequired: '部署名称 *', - deploymentNamePlaceholder: 'gpt-4', - deploymentNameDescription: '在 Azure OpenAI Studio 中创建的部署名称', - apiKey: 'API Key', - apiKeyRequired: 'API Key *', - apiKeyPlaceholder: '请输入 API Key', - apiKeyDescription: '从 Azure 门户获取的 API 密钥', - supportedModels: '支持的模型', - supportedModelsDescription: '选择此部署支持的模型类型', - - // Claude Console 字段 - apiUrl: 'API URL', - apiUrlRequired: 'API URL *', - apiUrlPlaceholder: '例如:https://api.example.com', - apiKeyClaudeConsoleRequired: 'API Key *', - apiKeyClaudeConsolePlaceholder: '请输入API Key', - dailyQuota: '每日额度限制', - dailyQuotaLabel: '每日额度限制 ($)', - dailyQuotaPlaceholder: '0 表示不限制', - dailyQuotaDescription: '设置每日使用额度,0 表示不限制', - quotaResetTime: '额度重置时间', - quotaResetTimePlaceholder: '00:00', - quotaResetTimeDescription: '每日自动重置额度的时间', - todayUsage: '今日使用情况', - remaining: '剩余', - used: '已使用', - modelMapping: '模型映射表', - modelMappingOptional: '模型映射表 (可选)', - modelMappingDescription: - '留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。', - originalModel: '原始模型名称', - mappedModel: '映射后的模型名称', - addModelMapping: '添加模型映射', - userAgent: '自定义 User-Agent', - userAgentOptional: '自定义 User-Agent (可选)', - userAgentPlaceholder: '留空则透传客户端 User-Agent', - userAgentDescription: '留空时将自动使用客户端的 User-Agent,仅在需要固定特定 UA 时填写', - rateLimitMechanism: '限流机制', - enableRateLimit: '启用限流机制', - rateLimitDescription: '启用后,当账号返回429错误时将暂停调度一段时间', - rateLimitDuration: '限流时间 (分钟)', - rateLimitDurationDescription: '账号被限流后暂停调度的时间(分钟)', - - // Claude 订阅类型 - subscriptionType: '订阅类型', - subscriptionClaudeMax: 'Claude Max', - subscriptionClaudePro: 'Claude Pro', - claudeProLimitation: 'Pro 账号不支持 Claude Opus 4 模型', - - // Claude 特殊功能 - autoStopOnWarning: '5小时使用量接近限制时自动停止调度', - autoStopOnWarningDescription: - '当系统检测到账户接近5小时使用限制时,自动暂停调度该账户。进入新的时间窗口后会自动恢复调度。', - useUnifiedUserAgent: '使用统一 Claude Code 版本', - useUnifiedUserAgentDescription: - '开启后将使用从真实 Claude Code 客户端捕获的统一 User-Agent,提高兼容性', - currentUnifiedVersion: '💡 当前统一版本:', - clearCache: '清除缓存', - clearing: '清除中...', - waitingForCapture: '⏳ 等待从 Claude Code 客户端捕获 User-Agent', - captureHint: - '💡 提示:如果长时间未能捕获,请确认有 Claude Code 客户端正在使用此账户,或联系开发者检查 User-Agent 格式是否发生变化', - useUnifiedClientId: '使用统一的客户端标识', - useUnifiedClientIdDescription: - '开启后将使用固定的客户端标识,使所有请求看起来来自同一个客户端,减少特征', - clientId: '客户端标识 ID', - regenerate: '重新生成', - clientIdDescription: '此ID将替换请求中的user_id客户端部分,保留session部分用于粘性会话', - - // 调度优先级 - schedulePriority: '调度优先级', - schedulePriorityRange: '调度优先级 (1-100)', - schedulePriorityPlaceholder: '数字越小优先级越高,默认50', - schedulePriorityDescription: '数字越小优先级越高,建议范围:1-100', - - // 手动输入 Token - manualTokenTitle: '手动输入 Token', - manualTokenDescription: - '请输入有效的 Access Token。如果您有 Refresh Token,建议也一并填写以支持自动刷新。', - manualTokenClaudeDescription: - '请输入有效的 Claude Access Token。如果您有 Refresh Token,建议也一并填写以支持自动刷新。', - manualTokenGeminiDescription: - '请输入有效的 Gemini Access Token。如果您有 Refresh Token,建议也一并填写以支持自动刷新。', - manualTokenOpenAIDescription: - '请输入有效的 OpenAI Access Token。如果您有 Refresh Token,建议也一并填写以支持自动刷新。', - obtainTokenMethods: '获取 Access Token 的方法:', - claudeTokenPath: - '请从已登录 Claude Code 的机器上获取 ~/.claude/.credentials.json 文件中的凭证, 请勿使用 Claude 官网 API Keys 页面的密钥。', - geminiTokenPath: - '请从已登录 Gemini CLI 的机器上获取 ~/.config/gemini/credentials.json 文件中的凭证。', - openaiTokenPath: - '请从已登录 OpenAI 账户的机器上获取认证凭证, 或通过 OAuth 授权流程获取 Access Token。', - accessToken: 'Access Token', - accessTokenOptional: 'Access Token (可选)', - accessTokenRequired: 'Access Token *', - accessTokenPlaceholder: '请输入 Access Token...', - accessTokenOptionalPlaceholder: '可选:如果不填写,系统会自动通过 Refresh Token 获取...', - accessTokenOptionalDescription: - 'Access Token 可选填。如果不提供,系统会通过 Refresh Token 自动获取。', - refreshToken: 'Refresh Token', - refreshTokenOptional: 'Refresh Token (可选)', - refreshTokenRequired: 'Refresh Token *', - refreshTokenPlaceholder: '请输入 Refresh Token...', - refreshTokenRequiredPlaceholder: '请输入 Refresh Token(必填)...', - refreshTokenDescription: '系统将使用 Refresh Token 自动获取 Access Token 和用户信息', - refreshTokenTip: '💡 如果未填写 Refresh Token,Token 过期后需要手动更新。', - - // Setup Token 流程 - setupTokenTitle: 'Claude Setup Token 授权', - setupTokenDescription: '请按照以下步骤通过 Setup Token 完成 Claude 账户的授权:', - setupTokenStep1Title: '点击下方按钮生成授权链接', - setupTokenStep2Title: '在浏览器中打开链接并完成授权', - setupTokenStep2Description: - '请在新标签页中打开授权链接,登录您的 Claude 账户并授权 Claude Code。', - setupTokenStep2Warning: '注意:如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。', - setupTokenStep3Title: '输入 Authorization Code', - setupTokenStep3Description: - '授权完成后,从返回页面复制 Authorization Code,并粘贴到下方输入框:', - generateSetupTokenUrl: '生成 Setup Token 授权链接', - generating: '生成中...', - copyLink: '复制链接', - regenerateLink: '重新生成', - authorizationCode: 'Authorization Code', - authorizationCodePlaceholder: '粘贴从Claude Code授权页面获取的Authorization Code...', - authorizationCodeDescription: '请粘贴从Claude Code授权页面复制的Authorization Code', - verifying: '验证中...', - completeAuthorization: '完成授权', - - // Token 更新(编辑模式) - updateTokenTitle: '更新 Token', - updateTokenDescription: - '可以更新 Access Token 和 Refresh Token。为了安全起见,不会显示当前的 Token 值。', - updateTokenTip: '💡 留空表示不更新该字段。', - newAccessToken: '新的 Access Token', - newRefreshToken: '新的 Refresh Token', - leaveBlankNoUpdate: '留空表示不更新...', - - // 使用情况 - currentUsage: '当前使用情况', - - // 按钮 - cancel: '取消', - nextStep: '下一步', - previousStep: '上一步', - create: '创建', - creating: '创建中...', - update: '更新', - updating: '更新中...', - - // 错误消息 - pleaseEnterAccountName: '请填写账户名称', - pleaseSelectGroup: '请选择一个分组', - pleaseEnterApiUrl: '请填写 API URL', - pleaseEnterApiKey: '请填写 API Key', - pleaseEnterAccessKeyId: '请填写 AWS 访问密钥 ID', - pleaseEnterSecretAccessKey: '请填写 AWS 秘密访问密钥', - pleaseEnterRegion: '请选择 AWS 区域', - pleaseEnterAzureEndpoint: '请填写 Azure Endpoint', - pleaseEnterDeploymentName: '请填写部署名称', - pleaseEnterAccessToken: '请填写 Access Token', - pleaseEnterRefreshToken: '请填写 Refresh Token', - - // 成功消息 - linkCopied: '链接已复制', - extractedAuthCode: '成功提取授权码!', - cacheClearedSuccess: '统一User-Agent缓存已清除', - newClientIdGenerated: '已生成新的客户端标识', - groupsRefreshed: '分组列表已刷新', - modelMappingAdded: '已添加映射', - modelMappingExists: '模型映射已存在', - - // 警告和提示 - copyFailed: '复制失败,请手动复制', - clearCacheFailed: '清除缓存失败', - urlNotFound: 'URL 中未找到授权码参数,请检查链接是否正确', - urlFormatError: '链接格式错误,请检查是否为完整的 URL', - wrongUrlFormat: '请粘贴以 http://localhost:45462 开头的链接', - loadGroupsFailed: '加载分组列表失败', - - // 确认对话框 - projectIdNotFilledTitle: '项目 ID 未填写', - projectIdNotFilledMessage: - '您尚未填写项目 ID。\n\n如果您的Google账号绑定了Google Cloud或被识别为Workspace账号,需要提供项目 ID。\n如果您使用的是普通个人账号,可以继续不填写。', - continueButton: '继续', - goBackToFill: '返回填写', - continueSave: '继续保存', - - // 快捷模型映射按钮 - presetSonnet4: '+ Sonnet 4', - presetOpus41: '+ Opus 4.1', - presetHaiku35: '+ Haiku 3.5', - presetOpus41ToSonnet4: '+ Opus 4.1 → Sonnet 4', - - // 编辑模式特殊提示 - leaveBlankNoUpdateApiKey: '留空表示不更新 API Key', - leaveBlankNoUpdateAwsKey: '留空表示不更新 AWS Access Key ID', - leaveBlankNoUpdateAwsSecret: '留空表示不更新 AWS Secret Access Key', - leaveBlankNoUpdateSession: '留空表示不更新', - - // 通用描述文本 - allModelsIfEmpty: - '留空表示支持所有模型。如果指定模型,请求中的模型不在列表内将不会调度到此账号', - systemDefaultIfEmpty: '留空将使用系统默认模型。支持 inference profile ID 或 ARN', - noUpdateIfEmpty: '留空表示不更新该字段', - - // 手动 Token 输入部分 - manualTokenInput: '手动输入 Token', - getAccessTokenMethod: '获取 Access Token 的方法:', - claudeCredentialsPath: '请从已登录 Claude Code 的机器上获取', - geminiCredentialsPath: '请从已登录 Gemini CLI 的机器上获取', - openaiCredentialsPath: - '请从已登录 OpenAI 账户的机器上获取认证凭证,或通过 OAuth 授权流程获取 Access Token。', - claudeCredentialsWarning: '文件中的凭证,请勿使用 Claude 官网 API Keys 页面的密钥。', - refreshTokenWarning: '💡 如果未填写 Refresh Token,Token 过期后需要手动更新。', - accessTokenOptionalInfo: 'Access Token 可选填。如果不提供,系统会通过 Refresh Token 自动获取。', - accessTokenRequiredPlaceholder: '请输入 Access Token...', - refreshTokenRequiredInfo: '系统将使用 Refresh Token 自动获取 Access Token 和用户信息', - refreshTokenOptionalPlaceholder: '请输入 Refresh Token...', - - // 优先级设置 - priorityPlaceholder: '数字越小优先级越高,默认50', - priorityDescription: '数字越小优先级越高,建议范围:1-100', - prioritySchedulingTitle: '调度优先级 (1-100)', - priorityEditPlaceholder: '数字越小优先级越高', - - // Claude 订阅类型和高级选项 - claudeMaxSubscription: 'Claude Max', - claudeProSubscription: 'Claude Pro', - clientIdLabel: '客户端标识 ID', - regenerateClientId: '重新生成', - - // 编辑模式字段 - accountNameEdit: '账户名称', - accountNameEditPlaceholder: '为账户设置一个易识别的名称', - descriptionOptionalEdit: '描述 (可选)', - descriptionOptionalEditPlaceholder: '账户用途说明...', - accountTypeEdit: '账户类型', - noAvailableGroups: '暂无可用分组', - membersCount: ' 个成员', - createNewGroup: '新建分组', - - // AWS Bedrock 配置 - bedrockCredentials: '凭证配置', - bedrockCredentialsDescription: '请填写 AWS 访问凭证,用于调用 Amazon Bedrock 服务。', - sessionTokenOptionalPlaceholder: '临时凭证的会话令牌...', - bedrockModelConfig: '模型配置', - defaultModelLabel: '默认模型', - smallFastModelLabel: '小型快速模型', - - // Azure OpenAI 配置 - azureOpenAIConfig: 'Azure OpenAI 配置', - azureOpenAIDescription: '请配置 Azure OpenAI 服务的连接信息和部署详情。', - azureApiKey: 'API Key *', - azureApiKeyPlaceholder: '请输入 Azure OpenAI API Key...', - azureApiVersion: 'API 版本', - azureApiVersionDescription: 'Azure OpenAI API 版本,通常使用最新版本', - azureDeploymentName: '部署名称 *', - azureDeploymentNamePlaceholder: '例如:gpt-4', - azureDeploymentDescription: '在 Azure OpenAI Studio 中创建的部署名称', - azureSupportedModels: '支持的模型', - azureSupportedModelsPlaceholder: '例如:gpt-4, gpt-3.5-turbo', - azureSupportedModelsDescription: '此账户支持的模型列表,用逗号分隔。留空表示支持所有模型', - azureAccountSettings: '账户设置', - azureIsActive: '启用此账户', - azureSchedulable: '允许调度', - - // Claude Console 模型映射 - claudeConsoleModels: '模型映射', - claudeConsoleModelsDescription: - '配置模型请求的映射关系,将客户端请求的模型名映射为实际调用的模型。', - modelMappingFrom: '请求模型', - modelMappingFromPlaceholder: '例如:claude-3-5-sonnet-20241022', - modelMappingTo: '实际模型', - modelMappingToPlaceholder: '例如:claude-3-5-sonnet-latest', - removeMapping: '移除', - presetMappings: '预设映射', - modelMappingExample: '示例:claude-3-5-sonnet-20241022 → claude-3-5-sonnet-latest', - noMappingsConfigured: '暂未配置映射,将直接使用原模型名', - - // Setup Token 授权流程详细步骤 - setupTokenAuth: 'Setup Token 授权', - setupTokenAuthDescription: 'Setup Token 是安全的授权方式,通过临时授权码完成账户验证。', - setupTokenStep1: '步骤 1:生成授权链接', - setupTokenStep1Description: '系统将生成一个专用的授权链接,用于获取临时授权码。', - setupTokenStep2: '步骤 2:完成授权', - setupTokenStep3: '步骤 3:输入授权码', - setupTokenUrlGenerated: '授权链接已生成', - setupTokenOpenInBrowser: '在浏览器中打开', - setupTokenCopyLink: '复制链接', - setupTokenUrlExpiry: '此链接10分钟后过期,请尽快完成授权', - setupTokenAuthCode: 'Authorization Code *', - setupTokenAuthCodePlaceholder: '请粘贴从授权页面获取的授权码...', - setupTokenAuthCodeDescription: '完成授权后,将显示类似 "auth_code_xxx" 格式的授权码', - setupTokenSmartDetection: '智能检测', - setupTokenSmartDetectionDesc: '支持直接粘贴完整的回调URL,系统会自动提取授权码', - - // 更多错误消息和验证文本 - unsupportedPlatform: '不支持的平台', - accountCreationFailed: '账户创建失败', - accountUpdateFailed: '账户更新失败', - detailsInfo: '详细信息', - accountCreationFailedConsole: '账户创建失败:', - accountUpdateFailedConsole: '账户更新失败:', - clearCacheFailedWithError: '清除缓存失败:', - unknownError: '未知错误', - modelMappingExistsInfo: '模型映射已存在', - modelAddedMapping: '已添加映射', - - // 限流和配额管理 - rateLimitSettings: '限流设置', - rateLimitDurationPlaceholder: '例如:60', - quotaManagement: '配额管理', - quotaResetTimeLabel: '配额重置时间', - quotaResetDescription: '每天配额重置的时间点', - currentDailyUsage: '今日已用', - - // 高级设置 - advancedSettings: '高级设置', - customUserAgent: '自定义 User-Agent', - customUserAgentPlaceholder: '留空使用默认 User-Agent...', - - // 通用提示和状态 - notSet: '未设置', - unlimited: '无限制', - enabled: '已启用', - disabled: '已禁用', - active: '活跃', - inactive: '非活跃', - optional: '可选', - required: '必填', - recommended: '推荐', - - // 额外的操作按钮 - testConnection: '测试连接', - testing: '测试中...', - refresh: '刷新', - refreshing: '刷新中...', - validate: '验证', - validating: '验证中...', - save: '保存', - saving: '保存中...', - - // 使用情况和统计 - usageStats: '使用统计', - loadingUsage: '加载使用情况...', - usageLoadFailed: '使用情况加载失败', - - // Gemini 项目 ID 详细说明 - geminiProjectIdRequired: 'Google Cloud/Workspace 账号需要提供项目 ID', - geminiProjectIdDetail: - '某些 Google 账号(特别是绑定了 Google Cloud 的账号)会被识别为 Workspace 账号,需要提供额外的项目 ID。', - geminiHowToGetProjectId: '如何获取项目 ID:', - geminiVisitConsole: '访问', - geminiCopyProjectId: '复制\u9879目 ID(Project ID)\uff0c通常是字符串格式', - geminiProjectIdWarning: - '⚠️ 注意:要复制项目 ID(Project ID),不要复制项目编号(Project Number)!', - geminiPersonalAccountTip: - '\u63d0示\uff1a如果您的账号是普通个人账号(未绑定 Google Cloud),请留空此字段。', - - // AWS 区域参考 - awsRegionEastUS: 'us-east-1 (美国东部)', - awsRegionWestUS: 'us-west-2 (美国西部)', - awsRegionEuropeIreland: 'eu-west-1 (欧洲爱尔兰)', - awsRegionAsiaSingapore: 'ap-southeast-1 (新加坡)', - awsRegionAsiaTokyo: 'ap-northeast-1 (东京)', - awsRegionEuropeFrankfurt: 'eu-central-1 (法兰克福)', - awsRegionInputTip: '💡 请输入完整的区域代码,如 us-east-1', - - // Bedrock 模型说明 - bedrockModelConfigDesc: 'Bedrock 模型配置说明:', - bedrockSupportsInferenceProfile: '支持 Inference Profile ID(推荐)', - bedrockSupportsARN: '支持 Application Inference Profile ARN', - bedrockCommonModel: '常用模型:us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockEmptyUsesDefault: '留空将使用系统配置的默认模型', - - // Azure OpenAI 模型选择 - azureModelSelectionDesc: '选择此部署支持的模型类型', - - // 限流机制 - enableRateLimitMechanism: '启用限流机制', - rateLimitDescription2: '启用后,当账号返回429错误时将暂停调度一段时间', - - // Claude Console 特定字段 - claudeConsoleFields: 'Claude Console 特定字段', - modelMappingTable: '模型映射表', - modelMappingTableOptional: '模型映射表 (可选)', - - // Setup Token 授权 - claudeSetupTokenAuth: 'Claude Setup Token 授权', - setupTokenAuthSteps: '请按照以下步骤通过 Setup Token 完成 Claude 账户的授权:', - generateSetupTokenLink: '生成 Setup Token 授权链接', - - // 按钮和操作 - completeAuth: '完成授权', - - // 错误消息 - generateSetupTokenFailed: '生成Setup Token授权链接失败', - setupTokenAuthFailed: 'Setup Token授权失败,请检查授权码是否正确', - accountCreationError: '账户创建失败:', - - // 页面结构注释 - stepIndicator: '步骤指示器', - step1BasicInfo: '步骤1: 基本信息和代理设置', - groupSelector: '分组选择器', - multiSelectGroup: '多选分组界面', - newGroupOption: '新建分组选项', - geminiProjectId: 'Gemini 项目 ID 字段', - bedrockFields: 'Bedrock 特定字段', - azureOpenAIFields: 'Azure OpenAI 特定字段', - - // 验证消息 - nameRequired: '请填写账户名称', - rateLimitDefault60: '默认60分钟', - rateLimitPauseDescription: '账号被限流后暂停调度的时间(分钟)', - dailyQuotaLimit: '每日额度限制 ($)', - quotaZeroUnlimited: '0 表示不限制', - rateLimitDurationMinutes: '限流时间 (分钟)', - rateLimitDefaultMinutes: '默认60分钟', - rateLimitPauseDesc: '账号被限流后暂停调度的时间(分钟)', - - // 额度管理 - quotaManagementFields: '额度管理字段', - dailyQuotaLimitDollar: '每日额度限制 ($)', - dailyQuotaDesc: '设置每日使用额度,0 表示不限制', - quotaResetTimeDesc: '每日自动重置额度的时间', - - // 模型映射 - modelMappingDesc: - '留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。', - originalModelName: '原始模型名称', - mappedModelName: '映射后的模型名称', - addModelMappingBtn: '添加模型映射', - customUserAgentOptional: '自定义 User-Agent (可选)', - customUserAgentDesc: '留空时将自动使用客户端的 User-Agent,仅在需要固定特定 UA 时填写', - userAgentPassthrough: '留空则透传客户端 User-Agent', - - // Claude 订阅类型 - claudeSubscriptionType: '订阅类型', - - // Claude 高级选项 - claudeAutoStopScheduling: '5小时使用量接近限制时自动停止调度', - claudeAutoStopDesc: - '当系统检测到账户接近5小时使用限制时,自动暂停调度该账户。进入新的时间窗口后会自动恢复调度。', - claudeUseUnifiedUA: '使用统一 Claude Code 版本', - claudeUnifiedUADesc: '开启后将使用从真实 Claude Code 客户端捕获的统一 User-Agent,提高兼容性', - claudeCurrentUnifiedVersion: '💡 当前统一版本:', - claudeWaitingCapture: '⏳ 等待从 Claude Code 客户端捕获 User-Agent', - claudeCaptureHint: - '💡 提示:如果长时间未能捕获,请确认有 Claude Code 客户端正在使用此账户, 或联系开发者检查 User-Agent 格式是否发生变化', - claudeUseUnifiedClientId: '使用统一的客户端标识', - claudeUnifiedClientIdDesc: - '开启后将使用固定的客户端标识,使所有请求看起来来自同一个客户端,减少特征', - claudeClientIdLabel: '客户端标识 ID', - claudeClientIdDesc: '此ID将替换请求中的user_id客户端部分,保留session部分用于粘性会话', - - // Setup Token 流程 - setupTokenAuthProcess: 'Claude Setup Token 授权', - setupTokenProcessDesc: '请按照以下步骤通过 Setup Token 完成 Claude 账户的授权:', - setupTokenStepOneTitle: '步骤 1:生成授权链接', - setupTokenStepOneDesc: '点击下方按钮生成授权链接', - setupTokenGenerating: '生成中...', - setupTokenGenerateBtn: '生成 Setup Token 授权链接', - setupTokenCopyTitle: '复制链接', - - // 步骤指示器 - step2OAuth: '步骤2: OAuth授权', - step2SetupToken: '步骤2: Setup Token授权', - - // 分组选择器 - multiGroupInterface: '多选分组界面', - createNewGroupOption: '新建分组选项', - - // 手动输入Token提示 - credentialsFromFile: '文件中的凭证。', - - // Placeholder 文本 - originalModelNamePlaceholder: '原始模型名称', - mappedModelNamePlaceholder: '映射后的模型名称', - authCodePlaceholder: '粘贴从Claude Code授权页面获取的Authorization Code...', - leaveEmptyNoUpdate: '留空表示不更新', - leaveEmptyNoUpdateKey: '留空表示不更新 API Key', - leaveEmptyNoUpdateToken: '留空表示不更新...', - - // 标签和描述 - schedulePriorityLabel: '调度优先级 (1-100)', - attentionLabel: '注意:', - supportedModelsLabel: '支持的模型', - newAccessTokenLabel: '新的 Access Token', - newRefreshTokenLabel: '新的 Refresh Token', - updateTokenLabel: '更新 Token', - - // 按钮文本 - regenerateBtn: '重新生成', - previousStepBtn: '上一步', - - // 描述性文本 - claude5HourLimitDesc: '5小时使用量接近限制时自动停止调度', - claude5HourLimitExplanation: - '当系统检测到账户接近5小时使用限制时,自动暂停调度该账户。进入新的时间窗口后会自动恢复调度。', - useUnifiedClaudeVersion: '使用统一 Claude Code 版本', - unifiedVersionDesc: '开启后将使用从真实 Claude Code 客户端捕获的统一 User-Agent,提高兼容性', - waitingUserAgent: '⏳ 等待从 Claude Code 客户端捕获 User-Agent', - userAgentTip: '💡 提示:如果长时间未能捕获,请确认有 Claude Code 客户端正在使用此账户,', - contactDeveloper: '或联系开发者检查 User-Agent 格式是否发生变化', - unifiedClientIdDesc: '开启后将使用固定的客户端标识,使所有请求看起来来自同一个客户端,减少特征', - clientIdReplaceDesc: '此ID将替换请求中的user_id客户端部分,保留session部分用于粘性会话', - - // OAuth 步骤文本 - step1GenerateAuthLink: '步骤1: 生成授权链接', - clickButtonGenerate: '点击下方按钮生成授权链接', - copyLinkTitle: '复制链接', - step2AccessAndAuth: '步骤2: 访问链接并授权', - openInBrowser: '在浏览器中打开链接并完成授权', - browserAuthDesc: '请在新标签页中打开授权链接,登录您的 Claude 账户并授权 Claude Code。', - proxyNotice: '如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。', - step3InputAuthCode: '步骤3: 输入授权码', - inputAuthCodeTitle: '输入 Authorization Code', - authCompleteDesc: '授权完成后,从返回页面复制 Authorization Code,并粘贴到下方输入框:', - pasteAuthCodeDesc: '请粘贴从 Claude Code 授权页面复制的 Authorization Code', - - // AWS 区域参考 - awsRegionRef: '常用 AWS 区域参考:', - - // Error messages - apiKeyRequiredError: '请填写 API Key', - copyFailedManual: '复制失败,请手动复制', - - // 表单描述 - modelSupportDesc: - '留空表示支持所有模型。如果指定模型,请求中的模型不在列表内将不会调度到此账号', - modelTypeSelectionDesc: '选择此部署支持的模型类型', - userAgentDesc: '留空时将自动使用客户端的 User-Agent,仅在需要固定特定 UA 时填写', - - // 基础标签 - apiUrlLabel: 'API URL', - apiKeyLabel: 'API Key', - - // 更多缺失的翻译键 - copyLinkTooltip: '复制链接', - - // Claude 订阅类型显示 - claudeMaxDisplay: 'Claude Max', - claudeProDisplay: 'Claude Pro', - - // 新增缺失的翻译键 - modelSupportDescription: - '留空表示支持所有模型。如果指定模型,请求中的模型不在列表内将不会调度到此账号', - regenerateButton: '重新生成', - cancelButton: '取消', - credentialsFileDescription: '文件中的凭证。', - authCodeInputHint: '请粘贴从Claude Code授权页面复制的Authorization Code', - dailyQuotaLimitLabel: '每日额度限制 ($)', - todayUsageLabel: '今日使用情况', - remainingQuota: '剩余: ${amount}', - usedPercentage: '{percentage}% 已使用', - modelMappingTableLabel: '模型映射表 (可选)', - modelMappingTableDescription: - '留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。', - customUserAgentLabel: '自定义 User-Agent (可选)', - rateLimitDurationLabel: '限流时间 (分钟)', - awsRegionReferenceTitle: '常用 AWS 区域参考:', - azureModelTypeDescription: '选择此部署支持的模型类型', - tokenUpdateDescription: - '可以更新 Access Token 和 Refresh Token。为了安全起见,不会显示当前的 Token 值。', - tokenUpdateHint: '💡 留空表示不更新该字段。', - - // Setup Token 流程翻译 - clickToGenerateAuthLink: '点击生成授权链接', - openLinkInBrowser: '在浏览器中打开链接并完成授权', - openAuthLinkDescription: '请在新标签页中打开授权链接,登录您的 Claude 账户并授权。', - proxyWarning: '注意:如果您配置了代理,请确保浏览器也使用相同的代理访问授权页面。', - enterAuthorizationCode: '输入授权码', - copyAuthCodeDescription: '授权完成后,从返回页面复制 Authorization Code,并粘贴到下方输入框:' - }, - - // OAuth Flow Component - oauthFlow: { - // 平台标题 - claudeAccountAuth: 'Claude 账户授权', - geminiAccountAuth: 'Gemini 账户授权', - openaiAccountAuth: 'OpenAI 账户授权', - - // 流程说明 - claudeAuthDescription: '请按照以下步骤完成 Claude 账户的授权:', - geminiAuthDescription: '请按照以下步骤完成 Gemini 账户的授权:', - openaiAuthDescription: '请按照以下步骤完成 OpenAI 账户的授权:', - - // 步骤标题 - step1Title: '点击下方按钮生成授权链接', - step2Title: '在浏览器中打开链接并完成授权', - step3Title: '输入 Authorization Code', - step3TitleOpenAI: '输入授权链接或 Code', - - // 步骤描述 - step2Description: '请在新标签页中打开授权链接,登录您的 Claude 账户并授权。', - step2DescriptionGemini: '请在新标签页中打开授权链接,登录您的 Gemini 账户并授权。', - step2DescriptionOpenAI: '请在新标签页中打开授权链接,登录您的 OpenAI 账户并授权。', - - step3Description: '授权完成后,页面会显示一个', - step3DescriptionMiddle: ',请将其复制并粘贴到下方输入框:', - step3DescriptionGemini: - '授权完成后,页面会显示一个 Authorization Code,请将其复制并粘贴到下方输入框:', - step3DescriptionOpenAI: '授权完成后,当页面地址变为', - step3DescriptionOpenAIMiddle: '时:', - - // 按钮文本 - generating: '生成中...', - generateAuthLink: '生成授权链接', - regenerate: '重新生成', - previousStep: '上一步', - completeAuth: '完成授权', - verifying: '验证中...', - - // 占位符 - authCodePlaceholder: '粘贴从Claude页面获取的Authorization Code...', - authCodePlaceholderGemini: '粘贴从Gemini页面获取的Authorization Code...', - authCodePlaceholderOpenAI: - '方式1:复制完整的链接(http://localhost:1455/auth/callback?code=...)\n方式2:仅复制 code 参数的值\n系统会自动识别并提取所需信息', - - // 标签 - authorizationCode: 'Authorization Code', - authLinkOrCode: '授权链接或 Code', - - // 提示信息 - copyLinkTooltip: '复制链接', - authCodeHint: '请粘贴从Claude页面复制的Authorization Code', - authCodeHintGemini: '请粘贴从Gemini页面复制的Authorization Code', - - // 注意事项 - proxyNotice: '注意:', - proxyNoticeText: '如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。', - - // OpenAI 特有提示 - openaiImportantNote: '重要提示:', - openaiLoadingNote: '授权后页面可能会加载较长时间,请耐心等待。', - openaiAddressNote: '当浏览器地址栏变为', - openaiAddressNoteMiddle: '开头时,表示授权已完成。', - - openaiTip: '提示:', - openaiTipText: '您可以直接复制整个链接或仅复制 code 参数值,系统会自动识别。', - openaiLinkExample: '• 完整链接示例:', - openaiCodeExample: '• 仅 Code 示例:', - - // 成功和错误消息 - successExtractCode: '成功提取授权码!', - errorCodeNotFound: 'URL 中未找到授权码参数,请检查链接是否正确', - errorLinkFormat: '链接格式错误,请检查是否为完整的 URL', - errorWrongUrlFormat: '请粘贴以 http://localhost:1455 或 http://localhost:45462 开头的链接', - linkCopied: '链接已复制', - authFailed: '授权失败,请检查授权码是否正确', - generateAuthFailed: '生成授权链接失败' - }, - - // Group Management Modal - groupManagement: { - title: '账户分组管理', - createNewGroup: '创建新分组', - createGroup: '创建新分组', - groupNameRequired: '分组名称 *', - groupNamePlaceholder: '输入分组名称', - platformTypeRequired: '平台类型 *', - descriptionOptional: '描述 (可选)', - descriptionPlaceholder: '分组描述...', - creating: '创建中...', - create: '创建', - cancel: '取消', - loading: '加载中...', - noGroups: '暂无分组', - noDescription: '暂无描述', - membersCount: ' 个成员', - edit: '编辑', - delete: '删除', - editGroup: '编辑分组', - platformTypeLabel: '平台类型', - cannotModify: '(不可修改)', - updating: '更新中...', - update: '更新', - // Toast messages - loadGroupsFailed: '加载分组列表失败', - fillRequiredFields: '请填写必填项', - groupCreated: '分组创建成功', - createGroupFailed: '创建分组失败', - fillGroupName: '请填写分组名称', - groupUpdated: '分组更新成功', - updateGroupFailed: '更新分组失败', - groupHasMembers: '分组内还有成员,无法删除', - confirmDelete: '确定要删除分组 "{name}" 吗?', - groupDeleted: '分组删除成功', - deleteGroupFailed: '删除分组失败' - }, - - // Proxy Configuration - proxyConfig: { - title: '代理设置 (可选)', - enableProxy: '启用代理', - configDescription: '配置代理以访问受限的网络资源。支持 SOCKS5 和 HTTP 代理。', - stabilityNotice: '请确保代理服务器稳定可用,否则会影响账户的正常使用。', - proxyType: '代理类型', - hostAddress: '主机地址', - hostPlaceholder: '例如: 192.168.1.100', - port: '端口', - portPlaceholder: '例如: 1080', - needsAuth: '需要身份验证', - username: '用户名', - usernamePlaceholder: '代理用户名', - password: '密码', - passwordPlaceholder: '代理密码', - tip: '提示:', - apiRequestNotice: '代理设置将用于所有与此账户相关的API请求。请确保代理服务器支持HTTPS流量转发。' - } -} diff --git a/web/admin-spa/src/i18n/locales/zh-tw.js b/web/admin-spa/src/i18n/locales/zh-tw.js deleted file mode 100644 index 93657273..00000000 --- a/web/admin-spa/src/i18n/locales/zh-tw.js +++ /dev/null @@ -1,2922 +0,0 @@ -export default { - layout: { - mainLayout: { - comments: { - topNavigation: '頂部導航', - mainContentArea: '主內容區域', - tabBar: '標籤欄', - contentArea: '內容區域' - }, - routing: { - routeChangeError: '路由切換失敗:', - routeNotFound: '路由未找到', - defaultToTab: '預設選中儀表板' - } - }, - tabBar: { - tabs: { - dashboard: { - name: '儀表板', - shortName: '儀表板' - }, - apiKeys: { - name: 'API Keys', - shortName: 'API' - }, - accounts: { - name: '帳戶管理', - shortName: '帳戶' - }, - userManagement: { - name: '用戶管理', - shortName: '用戶' - }, - tutorial: { - name: '使用教程', - shortName: '教程' - }, - settings: { - name: '系統設置', - shortName: '設置' - } - } - } - }, - common: { - save: '保存', - cancel: '取消', - confirm: '確認', - loading: '載入中...', - edit: '編輯', - delete: '刪除', - create: '建立', - update: '更新', - search: '搜尋', - reset: '重置', - locale: 'zh-TW', - toastNotification: { - defaultTitles: { - success: '成功', - error: '錯誤', - warning: '警告', - info: '資訊' - } - }, - errors: { - loadDashboardFailed: '載入儀表板數據失敗', - loadUsageTrendFailed: '載入使用趨勢失敗', - loadModelStatsFailed: '載入模型統計失敗', - loadApiKeysTrendFailed: '載入API Keys趨勢失敗', - createClaudeConsoleAccountFailed: '建立Claude Console帳戶失敗', - createAzureOpenAIAccountFailed: '建立Azure OpenAI帳戶失敗', - updateClaudeConsoleAccountFailed: '更新Claude Console帳戶失敗', - updateAzureOpenAIAccountFailed: '更新Azure OpenAI帳戶失敗', - generateSetupTokenUrlFailed: '產生Setup Token URL失敗', - exchangeSetupTokenFailed: '交換Setup Token授權碼失敗', - allApiKeysInvalid: '所有 API Key 都無效', - loadOemSettingsFailed: '載入OEM設定失敗', - getApiKeyStatsFailed: '獲取API Key統計失敗', - getTagsFailed: '獲取標籤失敗', - requestFailed: '請求失敗: {status}', - loadSupportedClientsFailed: '載入支援的客戶端失敗' - }, - system: { - status: { - normal: '正常', - abnormal: '異常' - } - }, - confirmDialog: { - confirm: '確認', - cancel: '取消' - }, - confirmModal: { - continue: '繼續', - cancel: '取消' - }, - themeToggle: { - light: { - label: '淺色模式', - shortLabel: '淺色' - }, - dark: { - label: '深色模式', - shortLabel: '深色' - }, - auto: { - label: '跟隨系統', - shortLabel: '自動' - }, - toggleTheme: '切換主題', - clickToSwitch: '點擊切換主題' - }, - logoTitle: { - logoAlt: '標誌' - }, - languageSwitch: { - zhCnName: '簡體中文', - zhTwName: '繁體中文', - enName: '英語', - zhCnFlag: '簡', - zhTwFlag: '繁', - enFlag: 'EN' - }, - accountSelector: { - searchPlaceholder: '搜尋帳號名稱...', - schedulingGroups: '調度分組', - membersUnit: '個成員', - claudeOAuthAccounts: 'Claude OAuth 專屬帳號', - oauthAccounts: 'OAuth 專屬帳號', - claudeConsoleAccounts: 'Claude Console 專屬帳號', - noResultsFound: '沒有找到匹配的帳號', - selectAccount: '請選擇帳號', - useSharedPool: '使用共享帳號池', - accountStatus: { - unknown: '未知', - unauthorized: '未授權', - tokenError: 'Token錯誤', - pending: '待驗證', - rateLimited: '限流中', - error: '異常', - active: '正常' - }, - dateFormat: { - today: '今天建立', - yesterday: '昨天建立', - daysAgo: '天前' - } - }, - customDropdown: { - placeholder: '請選擇' - }, - // 通用時間與錯誤 - time: { - justNow: '剛剛', - minutesAgo: '{minutes}分鐘前', - hoursAgo: '{hours}小時前', - daysAgo: '{days}天前' - }, - - // API Keys 頁面按鈕 - model: '模型', - stats: '統計' - }, - language: { - zh: '簡體中文', - 'zh-tw': '繁體中文', - en: 'English', - current: '當前語言', - switch: '切換語言' - }, - header: { - adminPanel: '管理後台', - userMenu: '用戶選單', - logout: '退出登錄', - settings: '系統設置', - - // Version related - currentVersion: '當前版本', - newVersionAvailable: '有新版本可用', - newVersion: '新版本', - hasUpdate: '有新版本', - viewUpdate: '查看更新', - checkingUpdate: '檢查更新中...', - alreadyLatest: '當前已是最新版本', - checkUpdate: '檢查更新', - - // User menu items - changeAccountInfo: '修改帳戶資訊', - - // Change password modal - changePasswordModal: { - title: '修改帳戶資訊', - currentUsername: '當前用戶名', - currentUsernameHint: '當前用戶名,輸入新用戶名以修改', - newUsername: '新用戶名', - newUsernamePlaceholder: '輸入新用戶名(留空保持不變)', - newUsernameHint: '留空表示不修改用戶名', - currentPassword: '當前密碼', - currentPasswordPlaceholder: '請輸入當前密碼', - newPassword: '新密碼', - newPasswordPlaceholder: '請輸入新密碼', - newPasswordHint: '密碼長度至少8位', - confirmPassword: '確認新密碼', - confirmPasswordPlaceholder: '請再次輸入新密碼', - saving: '保存中...', - save: '保存修改', - - // Messages - passwordMismatch: '兩次輸入的密碼不一致', - passwordTooShort: '新密碼長度至少8位', - accountInfoChangeSuccess: '帳戶資訊修改成功,請重新登錄', - passwordChangeSuccess: '密碼修改成功,請重新登錄', - changeFailed: '修改失敗', - changePasswordFailed: '修改密碼失敗' - }, - - // Logout - logoutConfirm: '確定要退出登錄嗎?', - logoutSuccess: '已安全退出' - }, - apiStats: { - title: 'API Key 使用統計', - tutorialTitle: '使用教學', - userLogin: '用戶登錄', - adminPanel: '管理後台', - statsQuery: '統計查詢', - tutorial: '使用教學', - timeRange: '統計時間範圍', - today: '今日', - thisMonth: '本月', - - // API Key Input - usageStatsQuery: '使用統計查詢', - apiKeyDescription: '查詢您的 API Key 使用情況和統計資料', - enterApiKey: '輸入您的 API Key', - enterApiKeys: '輸入您的 API Keys(每行一個或用逗號分隔)', - singleMode: '單一', - aggregateMode: '彙整', - singleModeTitle: '單一模式', - aggregateModeTitle: '彙整模式', - queryButton: '查詢', - - // Stats Overview - batchQuerySummary: '批次查詢概要', - apiKeyInfo: 'API Key 資訊', - queryKeysCount: '查詢 Keys 數', - activeKeysCount: '有效 Keys 數', - totalRequests: '總請求數', - totalTokens: '總 Token 數', - totalCost: '總費用', - individual: '個', - - // Aggregated Stats Card - usageRatio: '使用占比', - requests: '次', - otherKeys: '其他', - keys: 'Keys', - - // Model Usage Stats - modelUsageStats: '模型使用統計', - loadingModelStats: '載入模型統計資料中...', - requestCount: '次請求', - inputTokens: '輸入 Token', - outputTokens: '輸出 Token', - cacheCreateTokens: '快取建立', - cacheReadTokens: '快取讀取', - noModelData: '暫無{period}模型使用資料', - - // Token Distribution - tokenDistribution: 'Token 使用分佈', - inputToken: '輸入 Token', - outputToken: '輸出 Token', - cacheCreateToken: '快取建立 Token', - cacheReadToken: '快取讀取 Token', - - // Limit Config - limitConfig: '限制設定', - limitConfigAggregate: '限制設定(彙整查詢模式)', - apiKeysOverview: 'API Keys 概況', - totalKeys: '總計 Keys', - activeKeys: '啟用 Keys', - aggregatedStats: '彙整統計', - dailyLimit: '日限制', - monthlyLimit: '月限制', - usageToday: '今日使用', - usageThisMonth: '本月使用', - remaining: '剩餘', - - // Stats Overview - Additional keys - name: '名稱', - status: '狀態', - permissions: '權限', - createdAt: '建立時間', - expiresAt: '過期時間', - active: '活躍', - inactive: '已停用', - notActivated: '未啓動', - expired: '已過期', - neverExpires: '永不過期', - allModels: '全部模型', - unknown: '未知', - none: '無', - formatError: '格式錯誤', - usageStatsOverview: '使用統計概覽', - keyContribution: '各 Key 貢獻占比', - firstUseDays: '首次使用後{days}天過期', - todayRequests: '今日請求數', - todayTokens: '今日Token數', - todayCost: '今日費用', - todayInputTokens: '今日輸入Token', - monthlyRequests: '本月請求數', - monthlyTokens: '本月Token數', - monthlyCost: '本月費用', - monthlyInputTokens: '本月輸入Token', - - // Limit Config - Additional keys - dailyCostLimit: '每日費用限制', - concurrencyLimit: '並發限制', - modelLimit: '模型限制', - clientLimit: '用戶端限制', - restrictedModelsCount: '限制 {count} 個模型', - allowAllModels: '允許所有模型', - restrictedClientsCount: '限制 {count} 個用戶端', - allowAllClients: '允許所有用戶端', - detailedLimitInfo: '詳細限制資訊', - restrictedModelsList: '受限模型清單', - restrictedModelsNote: '此 API Key 不能存取以上列出的模型', - allowedClientsList: '允許的用戶端', - allowedClientsNote: '此 API Key 只能被以上列出的用戶端使用', - timeWindowLimit: '時間視窗限制', - aggregateStatsNote: '每個 API Key 有獨立的限制設定,彙整模式下不顯示單個限制配置', - aggregateStatsSummary: '彙整統計摘要', - invalidKeysCount: '{count} 個無效的 API Key', - orRelationshipRequests: '請求次數和費用限制為「或」的關係,任一達到限制即觸發限流', - orRelationshipTokens: '請求次數和Token使用量為「或」的關係,任一達到限制即觸發限流', - onlyRequestsLimit: '僅限制請求次數', - - // Token Distribution - Additional keys - totalAmount: '總計', - todayTotal: '今日總計', - monthlyTotal: '本月總計', - - // Additional missing keys - usageRatioOnlyInMultiMode: '使用占比僅在多Key查詢時顯示', - noData: '暫無資料', - - // ApiKeyInput placeholders and texts - apiKeyPlaceholder: '請輸入您的 API Key (cr_...)', - apiKeysPlaceholder: '請輸入您的 API Keys,支援以下格式:\ncr_xxx\ncr_yyy\n或\ncr_xxx, cr_yyy', - clearInput: '清空輸入', - securityNoticeSingle: '您的 API Key 僅用於查詢自己的統計資料,不會被儲存或用於其他用途', - securityNoticeMulti: - '您的 API Keys 僅用於查詢統計資料,不會被儲存。彙整模式下部分個體化資訊將不顯示。', - multiKeyTip: '提示:最多支援同時查詢 30 個 API Keys。使用 Ctrl+Enter 快速查詢。', - errors: { - queryStatsFailed: '查詢統計資料失敗,請檢查您的 API Key 是否正確', - enterAtLeastOneKey: '請輸入至少一個有效的 API Key', - batchQueryFailed: '批次查詢失敗', - batchModelStatsFailed: '載入批次模型統計失敗', - loadModelStatsFailed: '載入模型統計失敗', - allInvalidKeys: '所有 API Key 都無效' - } - }, - - // Login page - login: { - title: '管理後台', - username: '用戶名', - usernamePlaceholder: '請輸入用戶名', - password: '密碼', - passwordPlaceholder: '請輸入密碼', - loginButton: '登錄', - loggingIn: '登錄中...', - loginFailed: '登入失敗', - loginFailedCheck: '登入失敗,請檢查使用者名稱與密碼' - }, - - // Dashboard page - dashboard: { - // Main stats cards - totalApiKeys: '總 API Keys', - activeApiKeys: '活躍', - serviceAccounts: '服務帳戶', - normalAccounts: '正常', - abnormalAccounts: '異常', - pausedAccounts: '停止調度', - rateLimitedAccounts: '限流', - todayRequests: '今日請求', - totalRequests: '總請求', - systemStatus: '系統狀態', - uptime: '運行時間', - - // Platform accounts tooltip - claudeAccount: 'Claude: {total} 個 (正常: {normal})', - consoleAccount: 'Console: {total} 個 (正常: {normal})', - geminiAccount: 'Gemini: {total} 個 (正常: {normal})', - bedrockAccount: 'Bedrock: {total} 個 (正常: {normal})', - openaiAccount: 'OpenAI: {total} 個 (正常: {normal})', - azureOpenaiAccount: 'Azure OpenAI: {total} 個 (正常: {normal})', - - // Token stats cards - todayToken: '今日Token', - totalTokenConsumption: '總Token消耗', - inputTokens: '輸入', - outputTokens: '輸出', - cacheCreateTokens: '快取建立', - cacheReadTokens: '快取讀取', - - // Real-time metrics - realtimeRPM: '即時RPM', - realtimeTPM: '即時TPM', - requestsPerMinute: '每分钟請求數', - tokensPerMinute: '每分钟Token數', - historicalData: '歷史資料', - minutes: '分鐘', - // Uptime display formats - uptimeFormat: { - daysHours: '{days}天 {hours}小時', - hoursMinutes: '{hours}小時 {minutes}分鐘', - minutes: '{minutes}分鐘' - }, - - // Charts section - modelDistributionAndTrend: '模型使用分佈與Token使用趋勢', - - // Date filter presets - today: '今日', - yesterday: '昨日', - dayBefore: '前天', - last7Days: '近7天', - last30Days: '近30天', - thisWeek: '本週', - lastWeek: '上週', - thisMonth: '本月', - lastMonth: '上月', - - // Granularity buttons - byDay: '按日', - byHour: '按小時', - - // Date picker - startDatePlaceholder: '開始日期', - endDatePlaceholder: '結束日期', - dateSeparator: '至', - maxHours24: '最多24小時', - - // Errors - errors: { - rangeTooLongHour: '小時粒度下日期範圍不能超過24小時', - rangeTooLongDay: '日期範圍不能超過 31 天', - rangeTooLongHourSwitched: '小時粒度超過24小時,已切換為近24小時' - }, - - // Auto refresh controls - autoRefresh: '自動刷新', - refresh: '刷新', - refreshing: '刷新中', - refreshDataNow: '立即刷新資料', - - // Charts - tokenUsageDistribution: 'Token使用分佈', - detailedStatistics: '詳細統計資料', - noModelUsageData: '暫無模型使用資料', - - // Table headers - model: '模型', - requestCount: '請求數', - totalTokens: '總Token', - cost: '費用', - percentage: '占比', - - // Trend charts - tokenUsageTrend: 'Token使用趋勢', - apiKeysUsageTrend: 'API Keys 使用趋勢', - requestsCount: '請求次數', - tokenCount: 'Token 數量', - totalApiKeysCount: '共 {count} 個 API Key', - showingTop10: '共 {count} 個 API Key,顯示使用量前 10 個', - - // Chart labels - inputTokensLabel: '輸入Token', - outputTokensLabel: '輸出Token', - cacheCreateTokensLabel: '快取建立Token', - cacheReadTokensLabel: '快取讀取Token', - costLabel: '費用 (USD)', - requestsLabel: '請求數', - time: '時間', - date: '日期', - tokenQuantity: 'Token數量', - requestsQuantity: '請求次數', - - // Usage Trend component - usageTrend: { - title: '使用趨勢', - granularity: { - byDay: '按天', - byHour: '按小時' - }, - periodOptions: { - last24Hours: '24小時', - last7Days: '7天', - last30Days: '30天', - recentDays: '最近{days}天' - }, - chartLabels: { - requests: '請求次數', - tokens: 'Token使用量', - requestsAxis: '請求次數', - tokensAxis: 'Token使用量' - } - }, - - // Model Distribution component - modelDistribution: { - title: '模型使用分佈', - periods: { - daily: '今日', - total: '累計' - }, - noData: '暫無模型使用資料', - units: { - requests: '請求', - tokens: 'tokens' - }, - chart: { - tooltip: { - requests: '請求', - tokens: 'Tokens' - } - } - } - }, - - // Accounts page - accounts: { - title: '帳戶管理', - description: '管理您的 Claude、Gemini、OpenAI 和 Azure OpenAI 帳戶及代理配置', - - // Filters and sorting - sortBy: '選擇排序', - selectPlatform: '選擇平台', - selectGroup: '選擇分組', - refresh: '刷新', - refreshTooltip: '刷新資料 (Ctrl/⌘+點擊強制刷新所有快取)', - addAccount: '添加帳戶', - - // Sort options - sortByName: '按名稱排序', - sortByDailyTokens: '按今日Token排序', - sortByDailyRequests: '按今日請求數排序', - sortByTotalTokens: '按總Token排序', - sortByLastUsed: '按最後使用排序', - - // Platform options - allPlatforms: '所有平台', - claudePlatform: 'Claude', - claudeConsolePlatform: 'Claude Console', - geminiPlatform: 'Gemini', - openaiPlatform: 'OpenAi', - azureOpenaiPlatform: 'Azure OpenAI', - bedrockPlatform: 'Bedrock', - - // Group options - allAccounts: '所有帳戶', - ungroupedAccounts: '未分組帳戶', - - // Loading states - loadingAccounts: '正在載入帳戶...', - noAccounts: '暫無帳戶', - noAccountsHint: '點擊上方按鈕添加您的第一個帳戶', - - // Table headers - name: '名稱', - platformType: '平台/類型', - status: '狀態', - priority: '優先級', - proxy: '代理', - dailyUsage: '今日使用', - sessionWindow: '會話窗口', - lastUsed: '最後使用', - actions: '操作', - - // Account types - dedicated: '專屬', - groupScheduling: '分組調度', - shared: '共享', - belongsToGroup: '所屬分組: {name}', - - // Platform labels - unknown: '未知', - apiKey: 'API Key', - oauth: 'OAuth', - setup: 'Setup', - aws: 'AWS', - - // Account status - normal: '正常', - abnormal: '異常', - blocked: '已封鎖', - tempError: '臨時異常', - rateLimited: '限流中', - notSchedulable: '不可調度', - bound: '綁定: {count} 個API Key', - - // Proxy status - noProxy: '無代理', - - // Usage statistics - requests: '次', - noData: '暫無資料', - averageRpm: '平均 {rpm} RPM', - - // Session window tooltip - sessionWindowTooltip: { - title: '會話窗口進度表示5小時窗口的時間進度', - normal: '正常:請求正常處理', - warning: '警告:接近限制', - rejected: '拒絕:達到速率限制' - }, - - // Session window status - remaining: '剩餘 {time}', - ended: '已結束', - - // Console quota - quotaProgress: '額度進度', - remainingQuota: '剩餘 $${amount}', - reset: '重置 {time}', - - // Mobile view labels - dailyUsageLabel: '今日使用', - sessionWindowLabel: '會話窗口', - lastUsedLabel: '最後使用', - proxyLabel: '代理', - priorityLabel: '優先級', - neverUsed: '從未使用', - sessionWindowTooltipMobile: '會話窗口進度不代表使用量,僅表示距離下一個5小時窗口的剩餘時間', - - // Action buttons - resetStatus: '重置狀態', - resetting: '重置中...', - resetStatusTooltip: '重置所有異常狀態', - scheduling: '調度', - disabled: '停用', - enableTooltip: '點擊啟用調度', - disableTooltip: '點擊禁用調度', - edit: '編輯', - editTooltip: '編輯帳戶', - delete: '刪除', - deleteTooltip: '刪除帳戶', - pause: '暫停', - enable: '啟用', - - // Time formatting - justNow: '剛剛', - minutesAgo: '{minutes} 分鐘前', - hoursAgo: '{hours} 小時前', - daysAgo: '{days} 天前', - hoursAndMinutes: '{hours}小時{minutes}分鐘', - hoursOnly: '{hours}小時', - minutesOnly: '{minutes}分鐘', - daysAndHours: '{days}天{hours}小時', - daysOnly: '{days}天', - - // Rate limit time - rateLimitTime: '({time})', - - // Messages and confirmations - resetStatusConfirmTitle: '重置帳戶狀態', - resetStatusConfirmMessage: - '確定要重置此帳戶的所有異常狀態嗎?這將清除限流狀態、401錯誤計數等所有異常標記。', - resetStatusConfirmButton: '確定重置', - resetStatusCancelButton: '取消', - statusResetSuccess: '帳戶狀態已重置', - statusResetFailed: '狀態重置失敗', - - deleteAccountTitle: '刪除帳戶', - deleteAccountMessage: '確定要刪除帳戶 "{name}" 嗎?\n\n此操作不可恢復。', - deleteAccountButton: '刪除', - deleteAccountCancel: '取消', - cannotDeleteBoundAccount: - '無法刪除此帳號,有 {count} 個API Key綁定到此帳號,請先解綁所有API Key', - accountDeleted: '帳戶已刪除', - deleteFailed: '刪除失敗', - - enabledScheduling: '已啟用調度', - disabledScheduling: '已禁用調度', - schedulingToggleFailed: '切換調度狀態失敗', - unsupportedAccountType: '該帳戶類型暫不支持調度控制', - operationFailed: '操作失敗', - - accountCreateSuccess: '帳戶創建成功', - accountUpdateSuccess: '帳戶更新成功', - loadAccountsFailed: '載入帳戶失敗', - unsupportedAccountTypeReset: '不支持的帳戶類型', - - // Schedulable reasons - invalidApiKey: 'API Key無效或已過期(401錯誤)', - serviceOverload: '服務過載(529錯誤)', - rateLimitTriggered: '觸發限流(429錯誤)', - authFailed: '認證失敗(401錯誤)', - manualStop: '手動停止調度', - - // Account type display - claudeMax: 'Claude Max', - claudePro: 'Claude Pro', - claudeFree: 'Claude Free', - - // Platform display - openaiResponsesPlatform: 'OpenAI-Responses', - ccrPlatform: 'CCR' - }, - apiKeys: { - // Page title and description - title: 'API Keys 管理', - description: '管理和監控您的 API 密鑰', - - // Tab navigation - activeTab: '活躍 API Keys', - deletedTab: '已刪除 API Keys', - - // Toolbar and actions - refresh: '重新整理', - refreshTooltip: '重新整理 API Keys 清單(Ctrl+點擊強制重新整理)', - createNew: '建立新 Key', - bulkEdit: '編輯已選取', - bulkDelete: '刪除已選取', - - // Table headers - name: '名稱', - tags: '標籤', - status: '狀態', - usageStats: '使用統計', - createdAt: '建立時間', - expiresAt: '過期時間', - actions: '操作', - - // Filter options - timeRange: { - today: '今日', - week: '最近7天', - month: '本月', - all: '全部時間' - }, - - // Status - active: '活躍', - disabled: '停用', - expired: '已過期', - expiringSoon: '即將過期', - neverExpire: '永不過期', - notActivated: '未啟用', - - // Usage statistics - dailyCost: '今日費用', - totalCost: '總費用', - dailyRequests: '今日請求', - lastUsed: '最後使用', - neverUsed: '從未使用', - minutesAgo: '{minutes} 分鐘前', - hoursAgo: '{hours} 小時前', - daysAgo: '{days} 天前', - justNow: '剛剛', - requests: '次', - - // Search and filter - searchPlaceholder: '搜尋名稱...', - searchPlaceholderWithOwner: '搜尋名稱或擁有者...', - allTags: '所有標籤', - noTags: '無標籤', - - // Binding information - shared: '使用共享池', - dedicated: '專屬', - consoleAccount: 'Console賬戶', - bindingWarning: '⚠️ 賬戶不存在', - - // Limits and quotas - dailyLimit: '每日費用', - weeklyOpusLimit: 'Opus週費用', - remainingQuota: '剩餘: ${amount}', - reset: '重設於 {time}', - quotaProgress: '配額進度', - - // Model statistics - modelStats: '模型使用分佈', - modelStatsCount: '{count} 個模型', - totalTokens: '總Token', - inputTokens: '輸入', - outputTokens: '輸出', - cacheCreate: '快取建立', - cacheRead: '快取讀取', - totalRequests: '總請求', - noModelData: '暫無模型使用資料', - resetFilter: '重新整理', - adjustTimeRange: '嘗試調整時間範圍或點擊重新整理重新載入資料', - - // Date filter - dateFilter: { - today: '今日', - days7: '7天', - days30: '30天' - }, - - // Actions - viewDetails: '查看詳細統計', - edit: '編輯', - renew: '續期', - activate: '啟用', - disable: '停用', - copy: '複製', - copyStatsLink: '複製統計頁面連結', - - // Pagination - totalRecords: '共 {count} 條記錄', - pageSize: '每頁顯示', - records: '條', - - // Empty states - noApiKeys: '暫無 API Keys', - noApiKeysHint: '點擊上方按鈕建立您的第一個 API Key', - noDeletedKeys: '暫無已刪除的 API Keys', - noDeletedKeysHint: '已刪除的 API Keys 會出現在這裡', - loading: '正在載入 API Keys...', - loadingDeleted: '正在載入已刪除的 API Keys...', - loadingModelStats: '載入模型統計...', - - // Deleted keys table - creator: '建立者', - deletedBy: '刪除者', - deletedAt: '刪除時間', - canRestore: '恢復', - permanentDelete: '徹底刪除', - clearAllDeleted: '清空所有已刪除', - - // User types - admin: '管理員', - user: '用戶', - unknown: '未知', - system: '系統', - - // Confirmation dialogs - confirmDisable: '確定要停用 API Key "{name}" 嗎?停用後所有使用此 Key 的請求將返回 401 錯誤。', - confirmDelete: '確定要刪除這個 API Key 嗎?此操作不可恢復。', - confirmBatchDelete: '確定要刪除已選取的 {count} 個 API Key 嗎?此操作不可恢復。', - confirmRestore: '確定要恢復這個 API Key 嗎?恢復後可以重新使用。', - confirmPermanentDelete: - '確定要徹底刪除這個 API Key 嗎?此操作不可恢復,所有相關資料將被永久刪除。', - confirmClearAll: - '確定要徹底刪除全部 {count} 個已刪除的 API Keys 嗎?此操作不可恢復,所有相關資料將被永久刪除。', - - // Success messages - keyActivated: 'API Key 已啟用', - keyDisabled: 'API Key 已停用', - keyDeleted: 'API Key 已刪除', - keyRestored: 'API Key 已成功恢復', - keyPermanentDeleted: 'API Key 已徹底刪除', - allDeletedCleared: '已清空所有已刪除的 API Keys', - linkCopied: '已複製統計頁面連結', - expiryUpdated: '過期時間已更新', - - // Error messages - selectKeysFirst: '請先選擇要編輯的 API Keys', - loadFailed: '載入 API Keys 失敗', - operationFailed: '操作失敗', - copyFailed: '複製失敗,請手動複製', - updateFailed: '更新失敗', - deleteFailed: '刪除失敗', - restoreFailed: '恢復失敗', - clearFailed: '清空失敗', - - // Tooltips and helpers - editExpiry: '編輯過期時間', - activationDays: '未啟用 ({days}天)', - boundTo: '綁定到', - belongsToGroup: '所屬分組: {name}', - - // Batch operations - batchSuccess: '成功處理 {count} 個項目', - batchPartialFail: '{failed} 個處理失敗', - batchAllFailed: '所有項目處理失敗', - - // Batch API Key Modal - batchApiKeyModal: { - title: '批量建立成功', - successMessage: '成功建立 {count} 個 API Key', - importantReminder: '重要提醒', - warningMessage: - '這是您唯一能看到所有 API Key 的機會。關閉此視窗後,系統將不再顯示完整的 API Key。請立即下載並妥善保存。', - - // Statistics cards - createdCount: '建立數量', - baseName: '基礎名稱', - permissionScope: '權限範圍', - expiryTime: '過期時間', - - // Permission texts - permissions: { - all: '全部服務', - claude: '僅 Claude', - gemini: '僅 Gemini', - unknown: '未知' - }, - - // Expiry time texts - neverExpire: '永不過期', - daysFormat: '{days}天', - weeksFormat: '{weeks}週', - monthsFormat: '{months}個月', - yearsFormat: '{years}年', - - // Preview section - previewTitle: 'API Keys 預覽', - hide: '隱藏', - show: '顯示', - preview: '預覽', - maxDisplayNote: '(最多顯示前10個)', - moreKeysNote: '... 還有 {count} 個 API Key', - - // Action buttons - downloadAll: '下載所有 API Keys', - alreadySaved: '我已保存', - directCloseTooltip: '直接關閉(不推薦)', - - // File info - fileFormatInfo: - '下載的檔案格式為文字檔案(.txt),每行包含一個 API Key。請將檔案保存在安全的位置,避免洩露。', - - // Confirmation dialogs - closeReminderTitle: '關閉提醒', - closeReminderMessage: - '關閉後將無法再次查看這些 API Key,請確保已經下載並妥善保存。\n\n確定要關閉嗎?', - confirmCloseButton: '確定關閉', - goBackDownloadButton: '返回下載', - - directCloseTitle: '確定要關閉嗎?', - directCloseMessage: '您還沒有下載 API Keys,關閉後將無法再次查看。\n\n強烈建議您先下載保存。', - stillCloseButton: '仍然關閉', - - directCloseFallbackMessage: '您還沒有下載 API Keys,關閉後將無法再次查看。\n\n確定要關閉嗎?', - - // Success messages - downloadSuccess: 'API Keys 檔案已下載' - }, - - // Expiry Edit Modal - expiryEditModal: { - title: '修改過期時間', - subtitle: '為 "{name}" 設定新的過期時間', - currentStatus: '目前狀態', - notActivated: '未啟動', - activationDaysHint: '(啟動後 {days} 天過期)', - neverExpire: '永不過期', - expired: '已過期', - daysToExpire: '{days} 天後過期', - monthsToExpire: '{months} 個月後過期', - activateNow: '立即啟動', - activateButton: '立即啟動 (啟動後 {days} 天過期)', - activationInfo: '點選立即啟動此 API Key,啟動後將在 {days} 天後過期', - selectNewDuration: '選擇新的期限', - neverExpireOption: '永不過期', - oneDay: '1 天', - sevenDays: '7 天', - thirtyDays: '30 天', - ninetyDays: '90 天', - oneHundredEightyDays: '180 天', - threeSixtyFiveDays: '1 年', - twoYears: '2 年', - custom: '自訂', - selectDateAndTime: '選擇日期和時間', - selectFutureDateTime: '選擇一個未來的日期和時間作為過期時間', - newExpiryTime: '新的過期時間', - cancel: '取消', - saving: '儲存中...', - saveChanges: '儲存變更', - activateConfirmTitle: '啟動 API Key', - activateConfirmMessage: '確定要立即啟動此 API Key 嗎?啟動後將在 {days} 天後自動過期。', - confirmActivate: '確定啟動', - confirmCancel: '取消' - }, - - // Edit API Key Modal - editApiKeyModal: { - title: '編輯 API Key', - - // Basic Info - name: '名稱', - namePlaceholder: '請輸入API Key名稱', - nameHint: '用於識別此 API Key 的用途', - - // Owner - owner: '所有者', - adminLabel: '- 管理員', - ownerHint: '分配此 API Key 給指定使用者或管理員,管理員分配時不受使用者 API Key 數量限制', - - // Tags - tags: '標籤', - selectedTags: '已選擇的標籤:', - clickToSelectTags: '點擊選擇已有標籤:', - createNewTag: '建立新標籤:', - newTagPlaceholder: '輸入新標籤名稱', - tagsHint: '用於標記不同團隊或用途,方便篩選管理', - - // Rate Limit Settings - rateLimitTitle: '速率限制設定 (可選)', - rateLimitWindow: '時間視窗 (分鐘)', - rateLimitRequests: '請求次數限制', - rateLimitCost: '費用限制 (美元)', - rateLimitWindowHint: '時間段單位', - rateLimitRequestsHint: '視窗內最大請求', - rateLimitCostHint: '視窗內最大費用', - noLimit: '無限制', - - // Usage Examples - usageExamples: '💡 使用範例', - example1: '範例1: 時間視窗=60,請求次數=1000 → 每60分鐘最多1000次請求', - example2: '範例2: 時間視窗=1,費用=0.1 → 每分鐘最多$0.1費用', - example3: '範例3: 視窗=30,請求=50,費用=5 → 每30分鐘50次請求且不超$5費用', - - // Cost Limits - dailyCostLimit: '每日費用限制 (美元)', - dailyCostLimitPlaceholder: '0 表示無限制', - dailyCostHint: '設定此 API Key 每日的費用限制,超過限制將拒絕請求,0 或留空表示無限制', - weeklyOpusCostLimit: 'Opus 模型週費用限制 (美元)', - weeklyOpusHint: - '設定 Opus 模型的週費用限制(週一到週日),僅限 Claude 官方帳戶,0 或留空表示無限制', - custom: '自訂', - - // Concurrency - concurrencyLimit: '並發限制', - concurrencyLimitPlaceholder: '0 表示無限制', - concurrencyHint: '設定此 API Key 可同時處理的最大請求數', - - // Active Status - activeStatus: '啟動帳號', - activeStatusHint: '取消勾選將停用此 API Key,暫停所有請求,客戶端回傳 401 錯誤', - - // Service Permissions - servicePermissions: '服務權限', - allServices: '全部服務', - claudeOnly: '僅 Claude', - geminiOnly: '僅 Gemini', - openaiOnly: '僅 OpenAI', - permissionsHint: '控制此 API Key 可以存取哪些服務', - - // Account Binding - accountBinding: '專屬帳號綁定', - refreshAccounts: '重新整理帳號', - refreshing: '重新整理中...', - claudeAccount: 'Claude 專屬帳號', - geminiAccount: 'Gemini 專屬帳號', - openaiAccount: 'OpenAI 專屬帳號', - bedrockAccount: 'Bedrock 專屬帳號', - useSharedPool: '使用共享帳號池', - selectClaudeAccount: '請選擇Claude帳號', - selectGeminiAccount: '請選擇Gemini帳號', - selectOpenaiAccount: '請選擇OpenAI帳號', - selectBedrockAccount: '請選擇Bedrock帳號', - accountBindingHint: '修改綁定帳號將影響此API Key的請求路由', - - // Model Restrictions - enableModelRestriction: '啟用模型限制', - restrictedModels: '限制的模型清單', - noRestrictedModels: '暫無限制的模型', - allCommonModelsRestricted: '所有常用模型已在限制清單中', - addRestrictedModelPlaceholder: '輸入模型名稱,按回車新增', - modelRestrictionHint: '設定此API Key無法存取的模型,例如:claude-opus-4-20250514', - - // Client Restrictions - enableClientRestriction: '啟用客戶端限制', - allowedClients: '允許的客戶端', - clientRestrictionHint: '勾選允許使用此API Key的客戶端', - - // Buttons - cancel: '取消', - save: '儲存修改', - saving: '儲存中...', - - // Messages - costLimitConfirmTitle: '費用限制提醒', - costLimitConfirmMessage: - '您設定了時間視窗但費用限制為0,這意味著不會有費用限制。\n\n是否繼續?', - costLimitConfirmContinue: '繼續儲存', - costLimitConfirmBack: '返回修改', - refreshAccountsSuccess: '帳號清單已重新整理', - refreshAccountsFailed: '重新整理帳號清單失敗', - updateFailed: '更新失敗' - }, - - // Batch Edit API Key Modal - batchEditApiKeyModal: { - title: '批次編輯 API Keys ({count} 個)', - - // Info section - infoTitle: '批次編輯說明', - infoContent: - '以下設定將套用到所選的 {count} 個 API Key。僅填寫或修改的欄位會被更新,空白欄位將保持原值不變。', - - // Tag operations - tagLabel: '標籤 (批次操作)', - tagOperations: { - replace: '替換標籤', - add: '新增標籤', - remove: '移除標籤', - none: '不修改標籤' - }, - - // Tag status texts - newTagsList: '新標籤清單:', - tagsToAdd: '要新增的標籤:', - tagsToRemove: '要移除的標籤:', - clickToSelectTags: '點擊選擇已有標籤:', - createNewTag: '建立新標籤:', - inputNewTagPlaceholder: '輸入新標籤名稱', - - // Rate limit settings - rateLimitTitle: '速率限制設定', - rateLimitWindow: '時間視窗 (分鐘)', - rateLimitRequests: '請求次數限制', - rateLimitCost: '費用限制 (美元)', - noModifyPlaceholder: '不修改', - - // Daily cost limit - dailyCostLimit: '每日費用限制 (美元)', - dailyCostLimitPlaceholder: '不修改 (0 表示無限制)', - - // Weekly Opus cost limit - weeklyOpusCostLimit: 'Opus 模型週費用限制 (美元)', - weeklyOpusCostLimitPlaceholder: '不修改 (0 表示無限制)', - opusLimitDescription: '設定 Opus 模型的週費用限制(週一到週日),僅限 Claude 官方帳戶', - - // Concurrency limit - concurrencyLimit: '並發限制', - concurrencyLimitPlaceholder: '不修改 (0 表示無限制)', - - // Active status - activeStatus: '啟用狀態', - statusOptions: { - active: '啟用', - disabled: '停用', - noChange: '不修改' - }, - - // Service permissions - servicePermissions: '服務權限', - permissionOptions: { - noChange: '不修改', - all: '全部服務', - claude: '僅 Claude', - gemini: '僅 Gemini', - openai: '僅 OpenAI' - }, - - // Account binding - accountBinding: '專屬帳號綁定', - refreshAccounts: '重新整理帳號', - refreshing: '重新整理中...', - - claudeAccount: 'Claude 專屬帳號', - geminiAccount: 'Gemini 專屬帳號', - openaiAccount: 'OpenAI 專屬帳號', - bedrockAccount: 'Bedrock 專屬帳號', - - accountOptions: { - noChange: '不修改', - sharedPool: '使用共享帳號池', - groupPrefix: '分組 - ' - }, - - // Optgroup labels - optgroupLabels: { - accountGroups: '帳號分組', - dedicatedAccounts: '專屬帳號' - }, - - // Buttons - cancel: '取消', - saving: '儲存中...', - batchSave: '批次儲存', - - // Messages - refreshAccountsSuccess: '帳號清單已重新整理', - refreshAccountsFailed: '重新整理帳號清單失敗', - batchEditSuccess: '成功批次編輯 {count} 個 API Keys', - batchEditPartialFail: '{failedCount} 個編輯失敗:\n{errors}', - batchEditAllFailed: '所有 API Keys 編輯失敗', - batchEditFailed: '批次編輯失敗', - batchEditErrorLog: '批次編輯 API Keys 失敗:' - }, - - // Renew API Key Modal - renewApiKeyModal: { - title: '續期 API Key', - apiKeyInfo: 'API Key 資訊', - currentExpiry: '當前到期時間:', - neverExpires: '永不到期', - renewDuration: '續期時長', - extend7Days: '延長 7 天', - extend30Days: '延長 30 天', - extend90Days: '延長 90 天', - extend180Days: '延長 180 天', - extend365Days: '延長 365 天', - customDate: '自訂日期', - setPermanent: '設為永不到期', - newExpiry: '新的到期時間:', - cancel: '取消', - renewing: '續期中...', - confirmRenew: '確認續期', - renewSuccess: 'API Key 續期成功', - renewFailed: '續期失敗' - }, - - // New API Key Modal - newApiKeyModal: { - title: 'API Key 建立成功', - subtitle: '請妥善保存您的 API Key', - directCloseTooltip: '直接關閉(不推薦)', - - // 警告提示 - warningTitle: '重要提醒', - warningMessage: - '這是您唯一能看到完整 API Key 的機會。關閉此視窗後,系統將不再顯示完整的 API Key。請立即複製並妥善保存。', - - // 欄位標籤 - apiKeyName: 'API Key 名稱', - remarks: '備註', - noDescription: '無描述', - apiKey: 'API Key', - - // 可見性切換 - hideApiKey: '隱藏 API Key', - showFullApiKey: '顯示完整 API Key', - visibilityHint: '點擊眼睛圖示切換顯示模式,使用下方按鈕複製完整 API Key', - - // 按鈕 - copyApiKey: '複製 API Key', - alreadySaved: '我已保存', - - // 確認對話框 - closeReminderTitle: '關閉提醒', - closeReminderMessage: - '關閉後將無法再次查看完整的 API Key,請確保已經妥善保存。\n\n確定要關閉嗎?', - confirmClose: '確定關閉', - cancel: '取消', - - directCloseTitle: '確定要關閉嗎?', - directCloseMessage: - '您還沒有保存 API Key,關閉後將無法再次查看。\n\n建議您先複製 API Key 再關閉。', - stillClose: '仍然關閉', - goBack: '返回複製', - - directCloseFallback: '您還沒有保存 API Key,關閉後將無法再次查看。\n\n確定要關閉嗎?', - - // 成功訊息 - apiKeyNotFound: 'API Key 不存在', - copySuccess: 'API Key 已複製到剪貼簿', - copyFailed: '複製失敗,請手動複製' - }, - - // Create API Key Modal - createApiKeyModal: { - title: '建立新的 API Key', - - // Create type section - createType: '建立類型', - singleCreate: '單一建立', - batchCreate: '批次建立', - batchCount: '建立數量', - batchCountPlaceholder: '輸入數量 (2-500)', - maxSupported: '最大支援 500 個', - batchHint: '批次建立時,每個 Key 的名稱會自動添加序號後綴,例如:{name}_1, {name}_2 ...', - - // Basic form fields - name: '名稱', - nameRequired: '*', - nameError: '請輸入API Key名稱', - singleNamePlaceholder: '為您的 API Key 取一個名稱', - batchNamePlaceholder: '輸入基礎名稱(將自動添加序號)', - description: '備註 (選填)', - descriptionPlaceholder: '描述此 API Key 的用途...', - - // Tags section - tags: '標籤', - selectedTags: '已選擇的標籤:', - clickToSelectTags: '點擊選擇已有標籤:', - createNewTag: '建立新標籤:', - newTagPlaceholder: '輸入新標籤名稱', - tagHint: '用於標記不同團隊或用途,方便篩選管理', - - // Rate limit section - rateLimitTitle: '速率限制設定 (選填)', - rateLimitWindow: '時間視窗 (分鐘)', - rateLimitRequests: '請求次數限制', - rateLimitCost: '費用限制 (美元)', - rateLimitWindowPlaceholder: '無限制', - rateLimitRequestsPlaceholder: '無限制', - rateLimitCostPlaceholder: '無限制', - rateLimitWindowHint: '時間段單位', - rateLimitRequestsHint: '視窗內最大請求', - rateLimitCostHint: '視窗內最大費用', - - // Rate limit examples - exampleTitle: '💡 使用範例', - example1: '範例1: 時間視窗=60,請求次數=1000 → 每60分鐘最多1000次請求', - example2: '範例2: 時間視窗=1,費用=0.1 → 每分鐘最多$0.1費用', - example3: '範例3: 視窗=30,請求=50,費用=5 → 每30分鐘50次請求且不超$5費用', - - // Cost limits - dailyCostLimit: '每日費用限制 (美元)', - dailyCostLimitPlaceholder: '0 表示無限制', - dailyCostHint: '設定此 API Key 每日的費用限制,超過限制將拒絶請求,0 或留空表示無限制', - weeklyOpusCostLimit: 'Opus 模型週費用限制 (美元)', - weeklyOpusCostLimitPlaceholder: '0 表示無限制', - weeklyOpusHint: - '設定 Opus 模型的週費用限制(週一到週日),僅限 Claude 官方帳戶,0 或留空表示無限制', - custom: '自訂', - - // Concurrency limit - concurrencyLimit: '並發限制 (選填)', - concurrencyLimitPlaceholder: '0 表示無限制', - concurrencyHint: '設定此 API Key 可同時處理的最大請求數,0 或留空表示無限制', - - // Expiration settings - expirationSettings: '過期設定', - fixedTimeExpiry: '固定時間過期', - activationExpiry: '首次使用後啟用', - fixedModeHint: '固定時間模式:Key 建立後立即生效,按設定時間過期', - activationModeHint: 'Key 首次使用時啟用,啟用後按設定天數過期(適合批次銷售)', - - // Expiration duration options - neverExpire: '永不過期', - '1d': '1 天', - '7d': '7 天', - '30d': '30 天', - '90d': '90 天', - '180d': '180 天', - '365d': '365 天', - customDate: '自訂日期', - - // Activation mode - activationDays: '輸入天數', - daysUnit: '天', - activationHint: 'Key 將在首次使用後啟用,啟用後 {days} 天過期', - - // Expiry status - willExpireOn: '將於 {date} 過期', - - // Service permissions - servicePermissions: '服務權限', - allServices: '全部服務', - claudeOnly: '僅 Claude', - geminiOnly: '僅 Gemini', - openaiOnly: '僅 OpenAI', - permissionHint: '控制此 API Key 可以存取哪些服務', - - // Account binding - dedicatedAccountBinding: '專屬帳號綁定 (選填)', - refreshAccounts: '重新整理帳號', - refreshing: '重新整理中...', - claudeDedicatedAccount: 'Claude 專屬帳號', - geminiDedicatedAccount: 'Gemini 專屬帳號', - openaiDedicatedAccount: 'OpenAI 專屬帳號', - bedrockDedicatedAccount: 'Bedrock 專屬帳號', - useSharedPool: '使用共用帳號池', - accountBindingHint: '選擇專屬帳號後,此API Key將僅使用該帳號,不選擇則使用共用帳號池', - selectClaudeAccount: '請選擇Claude帳號', - selectGeminiAccount: '請選擇Gemini帳號', - selectOpenaiAccount: '請選擇OpenAI帳號', - selectBedrockAccount: '請選擇Bedrock帳號', - - // Model restrictions - enableModelRestriction: '啟用模型限制', - restrictedModelsList: '限制的模型清單', - noRestrictedModels: '暫無限制的模型', - allCommonModelsRestricted: '所有常用模型已在限制清單中', - addRestrictedModelPlaceholder: '輸入模型名稱,按 Enter 添加', - modelRestrictionHint: '設定此API Key無法存取的模型,例如:claude-opus-4-20250514', - - // Client restrictions - enableClientRestriction: '啟用用戶端限制', - allowedClients: '允許的用戶端', - - // Buttons - cancel: '取消', - create: '建立', - creating: '建立中...', - - // Messages - batchCountError: '批次建立數量必須在 2-500 之間', - costLimitConfirmTitle: '費用限制提醒', - costLimitConfirmMessage: - '您設定了時間視窗但費用限制為0,這意味著不會有費用限制。\n\n是否繼續?', - costLimitConfirmContinue: '繼續建立', - costLimitConfirmBack: '返回修改', - costLimitFallbackMessage: - '您設定了時間視窗但費用限制為0,這意味著不會有費用限制。\n是否繼續?', - createSuccess: 'API Key 建立成功', - batchCreateSuccess: '成功建立 {count} 個 API Key', - createFailed: '建立失敗', - batchCreateFailed: '批次建立失敗', - refreshAccountsSuccess: '帳號清單已重新整理', - refreshAccountsFailed: '重新整理帳號清單失敗' - }, - - // Window Countdown - windowCountdown: { - expired: '視窗已過期', - notStarted: '視窗未啟動', - minutes: '分鐘', - requests: '請求', - tokens: 'Token', - cost: '費用', - aboutToReset: '即將重置', - minutesUntilReset: '分鐘後重置', - untilReset: '後重置', - windowLimit: '視窗限制', - hours: '小時' - } - }, - - // User-related translations - user: { - // User Dashboard - dashboard: { - title: 'Dashboard Overview', - welcomeMessage: 'Welcome to your Claude Relay dashboard', - - // Navigation tabs - overview: 'Overview', - apiKeys: 'API Keys', - usageStats: 'Usage Stats', - - // Welcome section - welcome: 'Welcome', - - // Stats cards - activeApiKeys: 'Active API Keys', - deletedApiKeys: 'Deleted API Keys', - totalRequests: 'Total Requests', - inputTokens: 'Input Tokens', - totalCost: 'Total Cost', - - // Account information section - accountInformation: 'Account Information', - username: 'Username', - displayName: 'Display Name', - email: 'Email', - role: 'Role', - memberSince: 'Member Since', - lastLogin: 'Last Login', - notAvailable: 'N/A', - - // Messages - logout: 'Logout', - logoutSuccess: 'Logged out successfully', - logoutFailed: 'Logout failed', - loadProfileFailed: 'Failed to load user profile', - loadStatsFailed: 'Failed to load API keys stats' - }, - - // User API Keys Manager - userApiKeysManager: { - title: '我的 API Keys', - description: '管理您的 API Keys 以存取 Claude Relay 服務', - loading: '正在載入 API Keys...', - warnings: { - maxKeysReached: - '您已達到 API Keys 的最大數量限制({maxApiKeys} 個)。請刪除現有的 Key 以建立新的。' - }, - status: { - deleted: '已刪除', - noDescription: '無描述', - neverUsed: '從未使用' - }, - dateLabels: { - created: '建立時間', - deleted: '刪除時間', - lastUsed: '最後使用', - expires: '到期時間' - }, - usage: { - requests: '次請求' - }, - actions: { - viewApiKey: '檢視 API Key', - deleteApiKey: '刪除 API Key' - }, - buttons: { - createApiKey: '建立 API Key', - delete: '刪除' - }, - emptyState: { - title: '無 API Keys', - description: '建立您的第一個 API Key 開始使用。' - }, - confirmDelete: { - title: '刪除 API Key', - message: "確定要刪除 '{name}' 嗎?此操作無法撤銷。" - }, - messages: { - loadFailed: '載入 API Keys 失败', - deleteSuccess: 'API Key 刪除成功', - deleteFailed: '刪除 API Key 失败' - } - }, - - // User Login - login: { - title: 'User Sign In', - subtitle: 'Sign in to your account to manage your API keys', - username: 'Username', - password: 'Password', - usernamePlaceholder: 'Enter your username', - passwordPlaceholder: 'Enter your password', - signIn: 'Sign In', - signingIn: 'Signing In...', - adminLogin: 'Admin Login', - - // Validation and error messages - requiredFields: 'Please enter both username and password', - loginSuccess: 'Login successful!', - loginFailed: 'Login failed', - accountDisabled: '您的帳號已被停用' - }, - - // View API Key Modal - viewApiKeyModal: { - title: 'API Key 詳情', - fields: { - name: '名稱', - description: '描述', - apiKey: 'API Key', - status: '狀態', - usageStatistics: '使用統計' - }, - apiKeyDisplay: { - notAvailable: '不可用', - keyPreview: 'cr_****', - fullKeyNotice: '完整 API Key 僅在首次建立或重新產生時顯示' - }, - buttons: { - hide: '隱藏', - show: '顯示', - copy: '複製', - close: '關閉' - }, - status: { - active: '啟用', - disabled: '停用' - }, - usageStats: { - requests: '請求次數', - inputTokens: '輸入權杖', - outputTokens: '輸出權杖', - totalCost: '總費用' - }, - timestamps: { - created: '建立時間', - lastUsed: '最後使用', - expires: '過期時間' - }, - messages: { - copySuccess: '已複製到剪貼簿!', - copyFailed: '複製到剪貼簿失敗' - } - }, - - // User Management - management: { - title: 'User Management', - description: 'Manage users, their API keys, and view usage statistics', - refresh: 'Refresh', - - // Stats cards - totalUsers: 'Total Users', - activeUsers: 'Active Users', - totalApiKeys: 'Total API Keys', - totalCost: 'Total Cost', - - // Search and filters - searchPlaceholder: 'Search users...', - allRoles: 'All Roles', - user: 'User', - admin: 'Admin', - allStatus: 'All Status', - active: 'Active', - disabled: 'Disabled', - - // User list - users: 'Users', - loadingUsers: 'Loading users...', - noUsersFound: 'No users found', - noUsersMatch: 'No users match your search criteria.', - noUsersCreated: 'No users have been created yet.', - - // User info and actions - displayName: 'Display Name', - email: 'Email', - role: 'Role', - username: 'Username', - apiKeysCount: 'API keys', - lastLogin: 'Last login', - neverLoggedIn: 'Never logged in', - requests: 'requests', - totalCostLabel: 'total cost', - - // Action buttons and tooltips - viewUsageStats: 'View Usage Stats', - disableAllApiKeys: 'Disable All API Keys', - disableUser: 'Disable User', - enableUser: 'Enable User', - changeRole: 'Change Role', - - // Confirmation dialogs - disableUserTitle: 'Disable User', - enableUserTitle: 'Enable User', - disableUserMessage: - 'Are you sure you want to disable user "{username}"? This will prevent them from logging in.', - enableUserMessage: 'Are you sure you want to enable user "{username}"?', - disable: 'Disable', - enable: 'Enable', - - disableAllKeysTitle: 'Disable All API Keys', - disableAllKeysMessage: - 'Are you sure you want to disable all {count} API keys for user "{username}"? This will prevent them from using the service.', - disableKeys: 'Disable Keys', - - // Success messages - userDisabledSuccess: 'User disabled successfully', - userEnabledSuccess: 'User enabled successfully', - keysDisabledSuccess: 'Disabled {count} API keys', - - // Error messages - loadUsersError: 'Failed to load users', - toggleStatusError: 'Failed to toggleStatus', - disableKeysError: 'Failed to disableKeys' - }, - - // User Usage Stats Modal - usageStatsModal: { - title: '使用統計', - titleWithUser: '使用統計 - {displayName}', - - // Time period selection - periodSelection: { - day: '最近24小時', - week: '最近7天', - month: '最近30天', - quarter: '最近90天' - }, - - // Loading state - loadingStats: '正在載入使用統計...', - - // Summary cards - summaryCards: { - requests: '請求數', - inputTokens: '輸入Token', - outputTokens: '輸出Token', - totalCost: '總費用' - }, - - // API Keys table - apiKeysTable: { - title: 'API Keys 使用情況', - headers: { - apiKey: 'API Key', - status: '狀態', - requests: '請求數', - tokens: 'Token數', - cost: '費用', - lastUsed: '最後使用' - }, - status: { - active: '活躍', - disabled: '已停用' - }, - tokensFormat: { - input: '輸入', - output: '輸出' - }, - never: '從未使用' - }, - - // Usage trend chart - usageTrend: { - title: '使用趨勢', - chartTitle: '使用圖表', - dailyTrends: '最近 {period} 的日使用趨勢', - chartNote: '(可整合 Chart.js、D3.js 或類似圖表庫)' - }, - - // No data state - noData: { - title: '暫無使用資料', - description: '該使用者在所選時間段內尚未發起任何API請求。' - }, - - // Buttons - close: '關閉' - }, - - // Change Role Modal - changeRoleModal: { - title: '修改使用者角色', - - // User info display - currentRole: '當前角色:{role}', - - // Role selection form - newRole: '新角色', - roles: { - user: '使用者', - userDesc: '具有基本權限的普通使用者', - admin: '管理員', - adminDesc: '擁有管理使用者和系統的完整權限' - }, - - // Warning messages - roleChangeWarning: { - title: '角色變更警告', - grantAdmin: - '授予管理員權限將使該使用者擁有系統的完整存取權限,包括管理其他使用者及其API密鑰的能力。', - removeAdmin: '移除管理員權限將限制該使用者只能管理自己的API密鑰和檢視自己的使用統計。' - }, - - // Buttons - cancel: '取消', - updateRole: '更新角色', - updating: '更新中...', - - // Success message - roleUpdated: '使用者角色已更新為 {role}' - }, - - // User Usage Statistics - userUsageStats: { - // Page header - title: '使用統計', - subtitle: '檢視您的 API 使用統計和費用', - - // Time period selection - periodSelection: { - day: '最近24小時', - week: '最近7天', - month: '最近30天', - quarter: '最近90天' - }, - - // Loading state - loadingStats: '正在載入使用統計...', - - // Statistics cards - statsCards: { - totalRequests: '總請求數', - inputTokens: '輸入Token', - outputTokens: '輸出Token', - totalCost: '總費用' - }, - - // Daily usage trend chart - usageTrend: { - title: '日使用趨勢', - chartTitle: '使用圖表', - dailyTrendsDescription: '這裡將顯示日使用趨勢', - chartIntegrationNote: '(可整合 Chart.js、D3.js 或類似圖表庫)' - }, - - // Usage by model section - modelUsage: { - title: '按模型使用情況', - requests: '請求', - requestsCount: '{count} 請求' - }, - - // Usage by API key table - apiKeyUsage: { - title: '按 API Key 使用情況', - headers: { - apiKey: 'API Key', - requests: '請求數', - inputTokens: '輸入Token', - outputTokens: '輸出Token', - cost: '費用', - status: '狀態' - }, - status: { - active: '活躍', - disabled: '已停用', - deleted: '已刪除' - } - }, - - // No data state - noData: { - title: '暫無使用資料', - description: - '您還沒有發起任何 API 請求。建立一個 API Key 並開始使用服務後,就能看到使用統計了。' - }, - - // Error messages - loadFailed: '載入使用統計失敗' - }, - - // Usage Detail Modal - usageDetailModal: { - title: '使用統計詳情', - close: '關閉', - - // Statistics cards - totalRequests: '總請求數', - totalTokens: '總Token數', - totalCost: '總費用', - averageRate: '平均速率', - - // Today stats - today: '今日', - todayRequests: '{count} 次', - todayTokens: '{count}', - todayCost: '${amount}', - - // Usage labels - times: '次', - - // Token distribution - tokenDistribution: 'Token 使用分佈', - inputTokens: '輸入 Token', - outputTokens: '輸出 Token', - cacheCreateTokens: '快取建立 Token', - cacheReadTokens: '快取讀取 Token', - - // Limits section - limitSettings: '限制設置', - dailyCostLimit: '每日費用限制', - concurrencyLimit: '並行限制', - timeWindowLimit: '時間窗口限制', - windowStatus: '窗口狀態', - used: '已使用', - remainingQuota: '剩餘: ${amount}', - - // Progress indicators - usedPercentage: '已使用 {percentage}%' - }, - - // Create API Key Modal - createApiKeyModal: { - title: '建立新的 API Key', - - // 表單標籤和占位符 - form: { - nameLabel: '名稱', - nameRequired: '*', - namePlaceholder: '為您的 API Key 取一個名稱', - descriptionLabel: '備註', - descriptionPlaceholder: '可選的備註資訊' - }, - - // 按鈕文本 - buttons: { - cancel: '取消', - creating: '建立中...', - createApiKey: '建立 API Key', - copy: '複製', - done: '完成' - }, - - // 成功狀態 - success: { - title: 'API Key 建立成功!', - warning: { - important: '重要提示:', - message: '請立即複製您的 API Key,您將無法再次查看!' - } - }, - - // 錯誤和驗證訊息 - validation: { - nameRequired: 'API Key 名稱是必填項' - }, - - errors: { - createFailed: '建立 API Key 失敗' - }, - - // Toast 訊息 - messages: { - createSuccess: 'API Key 建立成功!', - copySuccess: 'API Key 已複製到剪貼簿!', - copyFailed: '複製到剪貼簿失敗' - } - } - }, - - // Settings 設置頁面 - settings: { - title: '系統設置', - description: '網站定制和通知配置', - loading: '正在載入設置...', - - // 導航標籤 - branding: '品牌設置', - webhook: '通知設置', - - // 品牌設置 - siteName: '網站名稱', - siteNameDescription: '品牌標識', - siteNamePlaceholder: 'Claude Relay Service', - siteNameHint: '將顯示在瀏覽器標題和頁面頭部', - - siteIcon: '網站圖標', - siteIconDescription: 'Favicon', - currentIcon: '當前圖標', - uploadIcon: '上傳圖標', - removeIcon: '刪除', - iconFormats: '支援 .ico, .png, .jpg, .svg 格式,最大 350KB', - iconPreview: '圖標預覽', - validation: { - iconTooLarge: '圖標文件大小不能超過 350KB', - iconTypeNotSupported: '不支援的文件類型,請選擇 .ico, .png, .jpg 或 .svg 文件' - }, - - adminEntry: '管理入口', - adminEntryDescription: '登入按鈕顯示', - hideLoginButton: '隱藏登入按鈕', - showLoginButton: '顯示登入按鈕', - adminEntryHint: '隱藏後,用戶需要直接訪問 /admin/login 頁面登入', - - // 移動端卡片標題 - siteNameCard: '站點名稱', - siteNameCardDesc: '自定義您的站點品牌名稱', - siteIconCard: '站點圖標', - siteIconCardDesc: '上傳自定義圖標或輸入圖標URL', - adminEntryCard: '管理入口', - adminEntryCardDesc: '控制登入按鈕在首頁的顯示', - - // 操作按鈕 - save: '保存設置', - saving: '保存中...', - reset: '重置為預設', - lastUpdated: '最後更新:{time}', - lastUpdatedMobile: '上次更新: {time}', - - // Webhook 設置 - enableWebhook: '啟用 Webhook 通知', - webhookDescription: '開啟後,系統將按配置發送通知到指定平台', - - // 通知類型 - notificationTypes: '通知類型', - accountAnomaly: '帳號異常', - quotaWarning: '配額警告', - systemError: '系統錯誤', - securityAlert: '安全警報', - accountAnomalyDesc: '帳號狀態異常、認證失敗等', - quotaWarningDesc: 'API調用配額不足警告', - systemErrorDesc: '系統運行錯誤和故障', - securityAlertDesc: '安全相關的警報通知', - - // 通知平台 - notificationPlatforms: '通知平台', - addPlatform: '新增平台', - noPlatforms: '暫無配置的通知平台,請點擊「新增平台」按鈕新增', - enableSignature: '已啟用簽名驗證', - testConnection: '測試連線', - edit: '編輯', - delete: '刪除', - - // 高級設置 - advancedSettings: '進階設置', - maxRetries: '最大重試次數', - retryDelay: '重試延遲 (毫秒)', - timeout: '逾時時間 (毫秒)', - - // 測試通知 - sendTestNotification: '發送測試通知', - - // 平台模態框 - addPlatformModal: '新增通知平台', - editPlatformModal: '編輯通知平台', - configurePlatform: '配置新的Webhook通知渠道', - updatePlatform: '配置並更新Webhook通知渠道', - - platformType: '平台類型', - platformName: '名稱', - platformNameOptional: '(可選)', - platformNamePlaceholder: '例如:運維群通知、開發測試群', - - webhookUrl: 'Webhook URL', - webhookUrlRequired: '*', - webhookUrlPlaceholder: 'https://...', - editModeWarning: '編輯模式下不能更改平台類型', - - // Bark 特有設置 - deviceKey: '設備密鑰 (Device Key)', - deviceKeyPlaceholder: '例如:aBcDeFgHiJkLmNoPqRsTuVwX', - deviceKeyHint: '在Bark App中查看您的推送密鑰', - serverAddress: '伺服器地址', - serverAddressOptional: '(可選)', - serverAddressPlaceholder: '預設: https://api.day.app/push', - notificationLevel: '通知級別', - notificationSound: '通知聲音', - notificationGroup: '通知分組', - notificationGroupOptional: '(可選)', - notificationGroupPlaceholder: '預設: claude-relay', - - // 通知級別選項 - levelAuto: '自動(根據通知類型)', - levelPassive: '被動', - levelActive: '預設', - levelTimeSensitive: '時效性', - levelCritical: '緊急', - - // 聲音選項 - soundAuto: '自動(根據通知類型)', - soundDefault: '預設', - soundAlarm: '警報', - soundBell: '鈴聲', - soundBirdsong: '鳥鳴', - soundElectronic: '電子音', - soundGlass: '玻璃', - soundHorn: '喇叭', - soundSilence: '靜音', - - // Bark 提示信息 - barkInstructions: [ - '1. 在iPhone上安裝Bark App', - '2. 打開App獲取您的設備密鑰', - '3. 將密鑰貼上到上方輸入框' - ], - - // 簽名設置 - enableSignatureVerify: '啟用簽名驗證', - signatureEnabled: '已啟用', - signatureSecret: '簽名密鑰', - signatureSecretPlaceholder: 'SEC...', - - // 平台提示信息 - wechatWorkHint: '請在企業微信群機器人設置中獲取Webhook地址', - dingtalkHint: '請在釘釘群機器人設置中獲取Webhook地址', - feishuHint: '請在飛書群機器人設置中獲取Webhook地址', - slackHint: '請在Slack應用的Incoming Webhooks中獲取地址', - discordHint: '請在Discord伺服器的整合設置中建立Webhook', - barkHint: '請在Bark App中查看您的設備密鑰', - customHint: '請輸入完整的Webhook接收地址', - - // 模態框按鈕 - required: '必填項', - cancel: '取消', - testing: '測試中...', - saveChanges: '保存修改', - addPlatformBtn: '新增平台', - - // 成功/錯誤消息 - loadSettingsFailed: '載入設置失敗', - settingsSaved: '設置已保存', - saveSettingsFailed: '保存設置失敗', - oemSettingsSaved: 'OEM設置保存成功', - oemSettingsSaveFailed: '保存OEM設置失敗', - resetToDefault: '已重置為預設設置', - resetFailed: '重置失敗', - confirmReset: '確定要重置為預設設置嗎?\n\n這將清除所有自定義的網站名稱和圖標設置。', - - webhookConfigSaved: '配置已保存', - webhookConfigSaveFailed: '保存配置失敗', - getWebhookConfigFailed: '獲取webhook配置失敗', - - platformAdded: '平台已新增', - platformUpdated: '平台已更新', - platformDeleted: '平台已刪除', - platformDeleteFailed: '刪除失敗', - confirmDeletePlatform: '確定要刪除這個平台嗎?', - operationFailed: '操作失敗', - - testSuccess: '測試成功,webhook連線正常', - testFailed: '測試失敗', - testNotificationSent: '測試通知已發送', - testNotificationFailed: '發送失敗', - - // 表單驗證消息 - enterBarkDeviceKey: '請輸入Bark設備密鑰', - enterWebhookUrl: '請輸入Webhook URL', - enterValidWebhookUrl: '請輸入有效的Webhook URL', - enterWebhookUrlFirst: '請先輸入Webhook URL', - enterBarkDeviceKeyFirst: '請先輸入Bark設備密鑰', - - // 檔案上傳 - fileReadFailed: '檔案讀取失敗', - iconLoadFailed: 'Icon failed to load', - - // 平台名稱 - platforms: { - wechatWork: '企業微信', - dingtalk: '釘釘', - feishu: '飛書', - slack: 'Slack', - discord: 'Discord', - bark: 'Bark', - custom: '自定義' - } - }, - - // AccountForm 組件 - accountForm: { - // 標題和模態框 - editAccount: '編輯帳戶', - addAccount: '新增帳戶', - - // 步驟指示器 - stepBasicInfo: '基本資訊', - stepAuthorization: '授權認證', - - // 平台選擇 - platform: '平台', - platformClaude: 'Claude', - platformClaudeConsole: 'Claude Console', - platformGemini: 'Gemini', - platformOpenAI: 'OpenAI', - platformAzureOpenAI: 'Azure OpenAI', - platformBedrock: 'Bedrock', - - // 新增方式 - addMethod: '新增方式', - addTypeSetupToken: 'Setup Token(推薦)', - addTypeOAuth: 'OAuth 授權', - addTypeManual: '手動輸入 Access Token', - - // 基本資訊欄位 - accountName: '帳戶名稱', - accountNamePlaceholder: '為帳戶設置一個易識別的名稱', - description: '描述', - descriptionOptional: '描述(可選)', - descriptionPlaceholder: '帳戶用途說明...', - - // 帳戶類型 - accountType: '帳戶類型', - accountTypeShared: '共用帳戶', - accountTypeDedicated: '專屬帳戶', - accountTypeGroup: '群組排程', - accountTypeDescription: - '共用帳戶:供所有API Key使用;專屬帳戶:僅供特定API Key使用;群組排程:加入群組供群組內排程', - - // 群組選擇 - selectGroup: '選擇群組', - selectGroupRequired: '選擇群組 *', - noGroupsAvailable: '暫無可用群組', - memberCount: '個成員', - newGroup: '新建群組', - refreshGroups: '刷新群組', - - // Gemini 專案 ID - projectId: '專案 ID', - projectIdOptional: '專案 ID(可選)', - projectIdPlaceholder: '例如:verdant-wares-464411-k9', - projectIdDescription: 'Google Cloud/Workspace 帳號需要提供專案 ID', - projectIdInstructions: '如何獲取專案 ID:', - projectIdStep1: '訪問 Google Cloud Console', - projectIdStep2: '複製專案 ID(Project ID),通常是字串格式', - projectIdStep3: '⚠️ 注意:要複製專案 ID(Project ID),不要複製專案編號(Project Number)!', - projectIdTip: '提示:如果您的帳號是普通個人帳號(未綁定 Google Cloud),請留空此欄位。', - projectIdGoogleCloudRequired: 'Google Cloud/Workspace 帳號需要提供專案 ID', - projectIdGoogleCloudDescription: - '某些 Google 帳號(特別是綁定了 Google Cloud 的帳號)會被識別為 Workspace 帳號,需要提供額外的專案 ID。', - - // Bedrock 欄位 - awsAccessKeyId: 'AWS 存取金鑰 ID', - awsAccessKeyIdRequired: 'AWS 存取金鑰 ID *', - awsAccessKeyIdPlaceholder: '請輸入 AWS Access Key ID', - awsSecretAccessKey: 'AWS 秘密存取金鑰', - awsSecretAccessKeyRequired: 'AWS 秘密存取金鑰 *', - awsSecretAccessKeyPlaceholder: '請輸入 AWS Secret Access Key', - awsRegion: 'AWS 區域', - awsRegionRequired: 'AWS 區域 *', - awsRegionPlaceholder: '例如:us-east-1', - awsRegionReference: '常用 AWS 區域參考:', - awsRegionUsEast1: '• us-east-1(美國東部)', - awsRegionUsWest2: '• us-west-2(美國西部)', - awsRegionEuWest1: '• eu-west-1(歐洲愛爾蘭)', - awsRegionApSoutheast1: '• ap-southeast-1(新加坡)', - awsRegionApNortheast1: '• ap-northeast-1(東京)', - awsRegionEuCentral1: '• eu-central-1(法蘭克福)', - awsRegionTip: '💡 請輸入完整的區域代碼,如 us-east-1', - sessionToken: '會話權杖', - sessionTokenOptional: '會話權杖(可選)', - sessionTokenPlaceholder: '如果使用臨時憑證,請輸入會話權杖', - sessionTokenDescription: '僅在使用臨時 AWS 憑證時需要填寫', - defaultModel: '預設主模型', - defaultModelOptional: '預設主模型(可選)', - defaultModelPlaceholder: '例如:us.anthropic.claude-sonnet-4-20250514-v1:0', - defaultModelDescription: '留空將使用系統預設模型。支援 inference profile ID 或 ARN', - bedrockModelConfigTitle: 'Bedrock 模型配置說明:', - bedrockModelConfigInferenceProfile: '• 支援 Inference Profile ID(推薦)', - bedrockModelConfigArn: '• 支援 Application Inference Profile ARN', - bedrockModelConfigCommon: '• 常用模型:us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockModelConfigDefault: '• 留空將使用系統配置的預設模型', - smallFastModel: '小快速模型', - smallFastModelOptional: '小快速模型(可選)', - smallFastModelPlaceholder: '例如:us.anthropic.claude-3-5-haiku-20241022-v1:0', - smallFastModelDescription: '用於快速回應的輕量級模型,留空將使用系統預設', - - // Azure OpenAI 欄位 - azureEndpoint: 'Azure Endpoint', - azureEndpointRequired: 'Azure Endpoint *', - azureEndpointPlaceholder: 'https://your-resource.openai.azure.com', - azureEndpointDescription: - 'Azure OpenAI 資源的終結點 URL,格式:https://your-resource.openai.azure.com', - apiVersion: 'API 版本', - apiVersionPlaceholder: '2024-02-01', - apiVersionDescription: 'Azure OpenAI API 版本,預設使用最新穩定版本 2024-02-01', - deploymentName: '部署名稱', - deploymentNameRequired: '部署名稱 *', - deploymentNamePlaceholder: 'gpt-4', - deploymentNameDescription: '在 Azure OpenAI Studio 中建立的部署名稱', - apiKey: 'API Key', - apiKeyRequired: 'API Key *', - apiKeyPlaceholder: '請輸入 API Key', - apiKeyDescription: '從 Azure 入口網站取得的 API 金鑰', - supportedModels: '支援的模型', - supportedModelsDescription: '選擇此部署支援的模型類型', - - // Claude Console 欄位 - apiUrl: 'API URL', - apiUrlRequired: 'API URL *', - apiUrlPlaceholder: '例如:https://api.example.com', - apiKeyClaudeConsoleRequired: 'API Key *', - apiKeyClaudeConsolePlaceholder: '請輸入API Key', - dailyQuota: '每日額度限制', - dailyQuotaLabel: '每日額度限制($)', - dailyQuotaPlaceholder: '0 表示不限制', - dailyQuotaDescription: '設置每日使用額度,0 表示不限制', - quotaResetTime: '額度重置時間', - quotaResetTimePlaceholder: '00:00', - quotaResetTimeDescription: '每日自動重置額度的時間', - todayUsage: '今日使用情況', - remaining: '剩餘', - used: '已使用', - modelMapping: '模型映射表', - modelMappingOptional: '模型映射表(可選)', - modelMappingDescription: - '留空表示支援所有模型且不修改請求。配置映射後,左側模型會被識別為支援的模型,右側是實際發送的模型。', - originalModel: '原始模型名稱', - mappedModel: '映射後的模型名稱', - addModelMapping: '新增模型映射', - userAgent: '自定義 User-Agent', - userAgentOptional: '自定義 User-Agent(可選)', - userAgentPlaceholder: '留空則透傳用戶端 User-Agent', - userAgentDescription: '留空時將自動使用用戶端的 User-Agent,僅在需要固定特定 UA 時填寫', - rateLimitMechanism: '限流機制', - enableRateLimit: '啟用限流機制', - rateLimitDescription: '啟用後,當帳號返回429錯誤時將暫停排程一段時間', - rateLimitDuration: '限流時間(分鐘)', - rateLimitDurationDescription: '帳號被限流後暫停排程的時間(分鐘)', - - // Claude 訂閱類型 - - subscriptionClaudeMax: 'Claude Max', - subscriptionClaudePro: 'Claude Pro', - claudeProLimitation: 'Pro 帳號不支援 Claude Opus 4 模型', - - // Claude 特殊功能 - autoStopOnWarning: '5小時使用量接近限制時自動停止排程', - autoStopOnWarningDescription: - '當系統偵測到帳戶接近5小時使用限制時,自動暫停排程該帳戶。進入新的時間視窗後會自動恢復排程。', - useUnifiedUserAgent: '使用統一 Claude Code 版本', - useUnifiedUserAgentDescription: - '開啟後將使用從真實 Claude Code 用戶端擷取的統一 User-Agent,提高相容性', - currentUnifiedVersion: '💡 目前統一版本:', - clearCache: '清除快取', - clearing: '清除中...', - waitingForCapture: '⏳ 等待從 Claude Code 用戶端擷取 User-Agent', - captureHint: - '💡 提示:如果長時間未能擷取,請確認有 Claude Code 用戶端正在使用此帳戶,或聯繫開發者檢查 User-Agent 格式是否發生變化', - useUnifiedClientId: '使用統一的用戶端識別', - useUnifiedClientIdDescription: - '開啟後將使用固定的用戶端識別,使所有請求看起來來自同一個用戶端,減少特徵', - clientId: '用戶端識別 ID', - regenerate: '重新產生', - clientIdDescription: '此ID將替換請求中的user_id用戶端部分,保留session部分用於黏性會話', - - // 排程優先順序 - schedulePriority: '排程優先順序', - schedulePriorityRange: '排程優先順序(1-100)', - schedulePriorityPlaceholder: '數字越小優先順序越高,預設50', - schedulePriorityDescription: '數字越小優先順序越高,建議範圍:1-100', - - // 手動輸入 Token - manualTokenTitle: '手動輸入 Token', - manualTokenDescription: - '請輸入有效的 Access Token。如果您有 Refresh Token,建議也一併填寫以支援自動重新整理。', - manualTokenClaudeDescription: - '請輸入有效的 Claude Access Token。如果您有 Refresh Token,建議也一併填寫以支援自動重新整理。', - manualTokenGeminiDescription: - '請輸入有效的 Gemini Access Token。如果您有 Refresh Token,建議也一併填寫以支援自動重新整理。', - manualTokenOpenAIDescription: - '請輸入有效的 OpenAI Access Token。如果您有 Refresh Token,建議也一併填寫以支援自動重新整理。', - obtainTokenMethods: '取得 Access Token 的方法:', - claudeTokenPath: - '請從已登入 Claude Code 的機器上取得 ~/.claude/.credentials.json 檔案中的憑證,請勿使用 Claude 官網 API Keys 頁面的金鑰。', - geminiTokenPath: - '請從已登入 Gemini CLI 的機器上取得 ~/.config/gemini/credentials.json 檔案中的憑證。', - openaiTokenPath: - '請從已登入 OpenAI 帳戶的機器上取得認證憑證,或透過 OAuth 授權流程取得 Access Token。', - accessToken: 'Access Token', - accessTokenOptional: 'Access Token(可選)', - accessTokenRequired: 'Access Token *', - accessTokenPlaceholder: '請輸入 Access Token...', - accessTokenOptionalPlaceholder: '可選:如果不填寫,系統會自動透過 Refresh Token 取得...', - accessTokenOptionalDescription: - 'Access Token 可選填。如果不提供,系統會透過 Refresh Token 自動取得。', - refreshToken: 'Refresh Token', - refreshTokenOptional: 'Refresh Token(可選)', - refreshTokenRequired: 'Refresh Token *', - refreshTokenPlaceholder: '請輸入 Refresh Token...', - refreshTokenRequiredPlaceholder: '請輸入 Refresh Token(必填)...', - refreshTokenDescription: '系統將使用 Refresh Token 自動取得 Access Token 和使用者資訊', - refreshTokenTip: '💡 如果未填寫 Refresh Token,Token 過期後需要手動更新。', - - // Setup Token 流程 - setupTokenTitle: 'Claude Setup Token 授權', - setupTokenDescription: '請按照以下步驟透過 Setup Token 完成 Claude 帳戶的授權:', - setupTokenStep1Title: '點擊下方按鈕產生授權連結', - setupTokenStep2Title: '在瀏覽器中開啟連結並完成授權', - setupTokenStep2Description: - '請在新分頁中開啟授權連結,登入您的 Claude 帳戶並授權 Claude Code。', - setupTokenStep2Warning: '注意:如果您設置了代理,請確保瀏覽器也使用相同的代理訪問授權頁面。', - setupTokenStep3Title: '輸入 Authorization Code', - setupTokenStep3Description: - '授權完成後,從返回頁面複製 Authorization Code,並貼上到下方輸入框:', - generateSetupTokenUrl: '產生 Setup Token 授權連結', - generating: '產生中...', - copyLink: '複製連結', - regenerateLink: '重新產生', - authorizationCode: 'Authorization Code', - authorizationCodePlaceholder: '貼上從Claude Code授權頁面取得的Authorization Code...', - authorizationCodeDescription: '請貼上從Claude Code授權頁面複製的Authorization Code', - verifying: '驗證中...', - completeAuthorization: '完成授權', - - // Token 更新(編輯模式) - updateTokenTitle: '更新 Token', - updateTokenDescription: - '可以更新 Access Token 和 Refresh Token。為了安全起見,不會顯示目前的 Token 值。', - updateTokenTip: '💡 留空表示不更新該欄位。', - newAccessToken: '新的 Access Token', - newRefreshToken: '新的 Refresh Token', - leaveBlankNoUpdate: '留空表示不更新...', - - // 使用情況 - currentUsage: '目前使用情況', - - // 按鈕 - cancel: '取消', - nextStep: '下一步', - previousStep: '上一步', - create: '建立', - creating: '建立中...', - update: '更新', - updating: '更新中...', - - // 錯誤訊息 - pleaseEnterAccountName: '請填寫帳戶名稱', - pleaseSelectGroup: '請選擇一個群組', - pleaseEnterApiUrl: '請填寫 API URL', - pleaseEnterApiKey: '請填寫 API Key', - pleaseEnterAccessKeyId: '請填寫 AWS 存取金鑰 ID', - pleaseEnterSecretAccessKey: '請填寫 AWS 秘密存取金鑰', - pleaseEnterRegion: '請選擇 AWS 區域', - pleaseEnterAzureEndpoint: '請填寫 Azure Endpoint', - pleaseEnterDeploymentName: '請填寫部署名稱', - pleaseEnterAccessToken: '請填寫 Access Token', - pleaseEnterRefreshToken: '請填寫 Refresh Token', - - // 成功訊息 - linkCopied: '連結已複製', - extractedAuthCode: '成功提取授權碼!', - cacheClearedSuccess: '統一User-Agent快取已清除', - newClientIdGenerated: '已產生新的用戶端識別', - groupsRefreshed: '群組列表已重新整理', - modelMappingAdded: '已新增映射', - modelMappingExists: '模型映射已存在', - - // 警告和提示 - copyFailed: '複製失敗,請手動複製', - clearCacheFailed: '清除快取失敗', - urlNotFound: 'URL 中未找到授權碼參數,請檢查連結是否正確', - urlFormatError: '連結格式錯誤,請檢查是否為完整的 URL', - wrongUrlFormat: '請貼上以 http://localhost:45462 開頭的連結', - loadGroupsFailed: '載入群組列表失敗', - - // 確認對話框 - projectIdNotFilledTitle: '專案 ID 未填寫', - projectIdNotFilledMessage: - '您尚未填寫專案 ID。\n\n如果您的Google帳號綁定了Google Cloud或被識別為Workspace帳號,需要提供專案 ID。\n如果您使用的是普通個人帳號,可以繼續不填寫。', - continueButton: '繼續', - goBackToFill: '返回填寫', - continueSave: '繼續保存', - - // 快捷模型映射按鈕 - presetSonnet4: '+ Sonnet 4', - presetOpus41: '+ Opus 4.1', - presetHaiku35: '+ Haiku 3.5', - presetOpus41ToSonnet4: '+ Opus 4.1 → Sonnet 4', - - // 編輯模式特殊提示 - leaveBlankNoUpdateApiKey: '留空表示不更新 API Key', - leaveBlankNoUpdateAwsKey: '留空表示不更新 AWS Access Key ID', - leaveBlankNoUpdateAwsSecret: '留空表示不更新 AWS Secret Access Key', - leaveBlankNoUpdateSession: '留空表示不更新', - - // 通用描述文字 - allModelsIfEmpty: - '留空表示支援所有模型。如果指定模型,請求中的模型不在列表內將不會排程到此帳號', - systemDefaultIfEmpty: '留空將使用系統預設模型。支援 inference profile ID 或 ARN', - noUpdateIfEmpty: '留空表示不更新該欄位', - - // 手動 Token 輸入部分 - manualTokenInput: '手動輸入 Token', - getAccessTokenMethod: '取得 Access Token 的方法:', - claudeCredentialsPath: '請從已登入 Claude Code 的機器上取得', - geminiCredentialsPath: '請從已登入 Gemini CLI 的機器上取得', - openaiCredentialsPath: - '請從已登入 OpenAI 帳戶的機器上取得認證憑證,或透過 OAuth 授權流程取得 Access Token。', - claudeCredentialsWarning: '檔案中的憑證,請勿使用 Claude 官網 API Keys 頁面的金鑰。', - refreshTokenWarning: '💡 如果未填寫 Refresh Token,Token 過期後需要手動更新。', - accessTokenOptionalInfo: 'Access Token 可選填。如果不提供,系統會透過 Refresh Token 自動取得。', - accessTokenRequiredPlaceholder: '請輸入 Access Token...', - refreshTokenRequiredInfo: '系統將使用 Refresh Token 自動取得 Access Token 和使用者資訊', - refreshTokenOptionalPlaceholder: '請輸入 Refresh Token...', - - // 優先級設定 - priorityPlaceholder: '數字越小優先級越高,預設50', - priorityDescription: '數字越小優先級越高,建議範圍:1-100', - prioritySchedulingTitle: '排程優先級 (1-100)', - priorityEditPlaceholder: '數字越小優先級越高', - - // Claude 訂閱類型和進階選項 - - claudeMaxSubscription: 'Claude Max', - claudeProSubscription: 'Claude Pro', - clientIdLabel: '用戶端識別 ID', - regenerateClientId: '重新產生', - - // 編輯模式欄位 - accountNameEdit: '帳戶名稱', - accountNameEditPlaceholder: '為帳戶設定一個易識別的名稱', - descriptionOptionalEdit: '描述 (可選)', - descriptionOptionalEditPlaceholder: '帳戶用途說明...', - accountTypeEdit: '帳戶類型', - noAvailableGroups: '暫無可用群組', - membersCount: ' 個成員', - createNewGroup: '新建群組', - - // AWS Bedrock 設定 - bedrockCredentials: '憑證設定', - bedrockCredentialsDescription: '請填寫 AWS 存取憑證,用於呼叫 Amazon Bedrock 服務。', - sessionTokenOptionalPlaceholder: '臨時憑證的工作階段令牌...', - bedrockModelConfig: '模型設定', - defaultModelLabel: '預設模型', - smallFastModelLabel: '小型快速模型', - - // Azure OpenAI 設定 - azureOpenAIConfig: 'Azure OpenAI 設定', - azureOpenAIDescription: '請設定 Azure OpenAI 服務的連線資訊和部署詳情。', - azureApiKey: 'API Key *', - azureApiKeyPlaceholder: '請輸入 Azure OpenAI API Key...', - azureApiVersion: 'API 版本', - azureApiVersionDescription: 'Azure OpenAI API 版本,通常使用最新版本', - azureDeploymentName: '部署名稱 *', - azureDeploymentNamePlaceholder: '例如:gpt-4', - azureDeploymentDescription: '在 Azure OpenAI Studio 中建立的部署名稱', - azureSupportedModels: '支援的模型', - azureSupportedModelsPlaceholder: '例如:gpt-4, gpt-3.5-turbo', - azureSupportedModelsDescription: '此帳戶支援的模型清單,用逗號分隔。留空表示支援所有模型', - azureAccountSettings: '帳戶設定', - azureIsActive: '啟用此帳戶', - azureSchedulable: '允許調度', - - // Claude Console 模型映射 - claudeConsoleModels: '模型映射', - claudeConsoleModelsDescription: - '設定模型請求的映射關係,將客戶端請求的模型名映射為實際呼叫的模型。', - modelMappingFrom: '請求模型', - modelMappingFromPlaceholder: '例如:claude-3-5-sonnet-20241022', - modelMappingTo: '實際模型', - modelMappingToPlaceholder: '例如:claude-3-5-sonnet-latest', - removeMapping: '移除', - presetMappings: '預設映射', - modelMappingExample: '示例:claude-3-5-sonnet-20241022 → claude-3-5-sonnet-latest', - noMappingsConfigured: '暫未設定映射,將直接使用原模型名', - - // Setup Token 授權流程詳細步驟 - setupTokenAuth: 'Setup Token 授權', - setupTokenAuthDescription: 'Setup Token 是安全的授權方式,透過臨時授權碼完成帳戶驗證。', - setupTokenStep1: '步驟 1:產生授權連結', - setupTokenStep1Description: '系統將產生一個專用的授權連結,用於取得臨時授權碼。', - setupTokenStep2: '步驟 2:完成授權', - setupTokenStep3: '步驟 3:輸入授權碼', - setupTokenUrlGenerated: '授權連結已產生', - setupTokenOpenInBrowser: '在瀏覽器中開啟', - setupTokenCopyLink: '複製連結', - setupTokenUrlExpiry: '此連結10分鐘後過期,請盡快完成授權', - setupTokenAuthCode: 'Authorization Code *', - setupTokenAuthCodePlaceholder: '請貼上從授權頁面取得的授權碼...', - setupTokenAuthCodeDescription: '完成授權後,將顯示類似 "auth_code_xxx" 格式的授權碼', - setupTokenSmartDetection: '智能檢測', - setupTokenSmartDetectionDesc: '支援直接貼上完整的回調 URL,系統會自動提取授權碼', - - // 更多錯誤訊息和驗證文字 - unsupportedPlatform: '不支援的平台', - accountCreationFailed: '帳戶建立失敗', - accountUpdateFailed: '帳戶更新失敗', - detailsInfo: '詳細資訊', - accountCreationFailedConsole: '帳戶建立失敗:', - accountUpdateFailedConsole: '帳戶更新失敗:', - clearCacheFailedWithError: '清除快取失敗:', - unknownError: '未知錯誤', - modelMappingExistsInfo: '模型映射已存在', - modelAddedMapping: '已新增映射', - - // 限流和配額管理 - rateLimitSettings: '限流設定', - rateLimitDurationPlaceholder: '例如:60', - quotaManagement: '配額管理', - quotaResetTimeLabel: '配額重設時間', - quotaResetDescription: '每天配額重設的時間點', - currentDailyUsage: '今日已用', - - // 進階設定 - advancedSettings: '進階設定', - customUserAgent: '自定義 User-Agent', - customUserAgentPlaceholder: '留空使用預設 User-Agent...', - - // 通用提示和狀態 - notSet: '未設定', - unlimited: '無限制', - enabled: '已啟用', - disabled: '已禁用', - active: '活躍', - inactive: '非活躍', - optional: '可選', - required: '必填', - recommended: '推薦', - - // 額外的操作按鈕 - testConnection: '測試連線', - testing: '測試中...', - refresh: '刷新', - refreshing: '刷新中...', - validate: '驗證', - validating: '驗證中...', - save: '儲存', - saving: '儲存中...', - - // 使用情況和統計 - usageStats: '使用統計', - loadingUsage: '載入使用情況...', - usageLoadFailed: '使用情況載入失敗', - - // Gemini 專案 ID 詳細說明 - geminiProjectIdRequired: 'Google Cloud/Workspace 帳戶需要提供專案 ID', - geminiProjectIdDetail: - '某些 Google 帳戶(特別是綁定了 Google Cloud 的帳戶)會被識別為 Workspace 帳戶,需要提供額外的專案 ID。', - geminiHowToGetProjectId: '如何取得專案 ID:', - geminiVisitConsole: '造訪', - geminiCopyProjectId: '複製專案 ID(Project ID),通常是字串格式', - geminiProjectIdWarning: - '⚠️ 注意:要複製專案 ID(Project ID),不要複製專案編號(Project Number)!', - geminiPersonalAccountTip: - '提示:如果您的帳戶是普通個人帳戶(未綁定 Google Cloud),請留空此欄位。', - - // AWS 區域參考 - awsRegionEastUS: 'us-east-1 (美國東部)', - awsRegionWestUS: 'us-west-2 (美國西部)', - awsRegionEuropeIreland: 'eu-west-1 (歐洲愛爾蘭)', - awsRegionAsiaSingapore: 'ap-southeast-1 (新加坡)', - awsRegionAsiaTokyo: 'ap-northeast-1 (東京)', - awsRegionEuropeFrankfurt: 'eu-central-1 (法蘭克福)', - awsRegionInputTip: '💡 請輸入完整的區域代碼,如 us-east-1', - - // Bedrock 模型說明 - bedrockModelConfigDesc: 'Bedrock 模型設定說明:', - bedrockSupportsInferenceProfile: '支援 Inference Profile ID(推薦)', - bedrockSupportsARN: '支援 Application Inference Profile ARN', - bedrockCommonModel: '常用模型:us.anthropic.claude-sonnet-4-20250514-v1:0', - bedrockEmptyUsesDefault: '留空將使用系統設定的預設模型', - - // Azure OpenAI 模型選擇 - azureModelSelectionDesc: '選擇此部署支援的模型類型', - - // 限流機制 - enableRateLimitMechanism: '啟用限流機制', - rateLimitDescription2: '啟用後,當帳戶返回429錯誤時將暫停調度一段時間', - rateLimitDurationMinutes: '限流時間 (分鐘)', - rateLimitDefaultMinutes: '預設60分鐘', - rateLimitPauseDesc: '帳戶被限流後暫停調度的時間(分鐘)', - - // Claude Console 特定欄位 - claudeConsoleFields: 'Claude Console 特定欄位', - modelMappingTable: '模型映射表', - modelMappingTableOptional: '模型映射表 (可選)', - - // Claude 訂閱類型 - subscriptionType: '訂閱類型', - - // Setup Token 授權 - claudeSetupTokenAuth: 'Claude Setup Token 授權', - setupTokenAuthSteps: '請按照以下步驟透過 Setup Token 完成 Claude 帳戶的授權:', - generateSetupTokenLink: '產生 Setup Token 授權連結', - - // 按鈕和操作 - completeAuth: '完成授權', - - // 錯誤訊息 - generateSetupTokenFailed: '產生Setup Token授權連結失敗', - setupTokenAuthFailed: 'Setup Token授權失敗,請檢查授權碼是否正確', - accountCreationError: '帳戶建立失敗:', - - // 頁面結構註釋 - stepIndicator: '步驟指示器', - step1BasicInfo: '步驟1: 基本資訊和代理設定', - groupSelector: '分組選擇器', - multiSelectGroup: '多選分組介面', - newGroupOption: '新建分組選項', - geminiProjectId: 'Gemini 專案 ID 欄位', - bedrockFields: 'Bedrock 特定欄位', - azureOpenAIFields: 'Azure OpenAI 特定欄位', - - // 驗證訊息 - nameRequired: '請填寫帳戶名稱', - rateLimitDefault60: '預設60分鐘', - rateLimitPauseDescription: '帳戶被限流後暫停調度的時間(分鐘)', - dailyQuotaLimit: '每日額度限制 ($)', - quotaZeroUnlimited: '0 表示不限制', - - // 額度管理 - quotaManagementFields: '配額管理欄位', - dailyQuotaLimitDollar: '每日配額限制 ($)', - dailyQuotaDesc: '設定每日使用配額,0 表示不限制', - quotaResetTimeDesc: '每日自動重設配額的時間', - - // 模型映射 - modelMappingDesc: - '留空表示支援所有模型且不修改請求。設定映射後,左側模型會被識別為支援的模型,右側是實際發送的模型。', - originalModelName: '原始模型名稱', - mappedModelName: '映射後的模型名稱', - addModelMappingBtn: '新增模型映射', - customUserAgentOptional: '自定義 User-Agent (可選)', - customUserAgentDesc: '留空時將自動使用客戶端的 User-Agent,僅在需要固定特定 UA 時填寫', - userAgentPassthrough: '留空則透傳客戶端 User-Agent', - - // Claude 訂閱類型 - claudeSubscriptionType: '訂閱類型', - - // Claude 進階選項 - claudeAutoStopScheduling: '5小時使用量接近限制時自動停止調度', - claudeAutoStopDesc: - '當系統檢測到帳戶接近5小時使用限制時,自動暫停調度該帳戶。進入新的時間視窗後會自動恢復調度。', - claudeUseUnifiedUA: '使用統一 Claude Code 版本', - claudeUnifiedUADesc: '開啟後將使用從真實 Claude Code 客戶端捕獲的統一 User-Agent,提高相容性', - claudeCurrentUnifiedVersion: '💡 目前統一版本:', - claudeWaitingCapture: '⏳ 等待從 Claude Code 客戶端捕獲 User-Agent', - claudeCaptureHint: - '💡 提示:如果長時間未能捕獲,請確認有 Claude Code 客戶端正在使用此帳戶, 或聯繫開發者檢查 User-Agent 格式是否發生變化', - claudeUseUnifiedClientId: '使用統一的客戶端標識', - claudeUnifiedClientIdDesc: - '開啟後將使用固定的客戶端標識,使所有請求看起來來自同一個客戶端,減少特徵', - claudeClientIdLabel: '客戶端標識 ID', - claudeClientIdDesc: '此ID將替換請求中的user_id客戶端部分,保留session部分用於黏性工作階段', - - // Setup Token 流程 - setupTokenAuthProcess: 'Claude Setup Token 授權', - setupTokenProcessDesc: '請按照以下步驟透過 Setup Token 完成 Claude 帳戶的授權:', - setupTokenStepOneTitle: '步驟 1:產生授權連結', - setupTokenStepOneDesc: '點擊下方按鈕產生授權連結', - setupTokenGenerating: '產生中...', - setupTokenGenerateBtn: '產生 Setup Token 授權連結', - setupTokenCopyTitle: '複製連結', - - // 步驟指示器 - step2OAuth: '步驟2: OAuth授權', - step2SetupToken: '步驟2: Setup Token授權', - - // 群組選擇器 - multiGroupInterface: '多選群組介面', - createNewGroupOption: '新建群組選項', - - // 手動輸入Token提示 - credentialsFromFile: '檔案中的憑證。', - - // Placeholder 文字 - originalModelNamePlaceholder: '原始模型名稱', - mappedModelNamePlaceholder: '映射後的模型名稱', - authCodePlaceholder: '貼上Claude Code授權頁面獲取的Authorization Code...', - leaveEmptyNoUpdate: '留空表示不更新', - leaveEmptyNoUpdateKey: '留空表示不更新 API Key', - leaveEmptyNoUpdateToken: '留空表示不更新...', - - // 標籤和描述 - schedulePriorityLabel: '調度優先級 (1-100)', - attentionLabel: '注意:', - supportedModelsLabel: '支援的模型', - newAccessTokenLabel: '新的 Access Token', - newRefreshTokenLabel: '新的 Refresh Token', - updateTokenLabel: '更新 Token', - - // 按鈕文字 - regenerateBtn: '重新產生', - previousStepBtn: '上一步', - - // 描述性文字 - claude5HourLimitDesc: '5小時使用量接近限制時自動停止調度', - claude5HourLimitExplanation: - '當系統檢測到帳戶接近5小時使用限制時,自動暫停調度該帳戶。進入新的時間視窗後會自動恢復調度。', - useUnifiedClaudeVersion: '使用統一 Claude Code 版本', - unifiedVersionDesc: '開啟後將使用從真實 Claude Code 用戶端捕獲的統一 User-Agent,提高相容性', - waitingUserAgent: '⏳ 等待從 Claude Code 用戶端捕獲 User-Agent', - userAgentTip: '💡 提示:如果長時間未能捕獲,請確認有 Claude Code 用戶端正在使用此帳戶,', - contactDeveloper: '或聯繫開發者檢查 User-Agent 格式是否發生變化', - unifiedClientIdDesc: '開啟後將使用固定的用戶端標識,使所有請求看起來來自同一個用戶端,減少特徵', - clientIdReplaceDesc: '此ID將替換請求中的user_id用戶端部分,保留session部分用於黏性工作階段', - - // OAuth 步驟文字 - step1GenerateAuthLink: '步驟1: 產生授權連結', - clickButtonGenerate: '點擊下方按鈕產生授權連結', - copyLinkTitle: '複製連結', - step2AccessAndAuth: '步驟2: 訪問連結並授權', - openInBrowser: '在瀏覽器中開啟連結並完成授權', - browserAuthDesc: '請在新分頁中開啟授權連結,登錄您的 Claude 帳戶並授權 Claude Code。', - proxyNotice: '如果您設定了代理,請確保瀏覽器也使用相同的代理訪問授權頁面。', - step3InputAuthCode: '步驟3: 輸入授權碼', - inputAuthCodeTitle: '輸入 Authorization Code', - authCompleteDesc: '授權完成後,從返回頁面複製 Authorization Code,並貼上到下方輸入框:', - pasteAuthCodeDesc: '請貼上從 Claude Code 授權頁面複製的 Authorization Code', - - // AWS 區域參考 - awsRegionRef: '常用 AWS 區域參考:', - - // 錯誤訊息 - copyFailedManual: '複製失敗,請手動複製', - - // 表單描述 - modelSupportDesc: - '留空表示支援所有模型。如果指定模型,請求中的模型不在列表內將不會調度到此帳戶', - modelTypeSelectionDesc: '選擇此部署支援的模型類型', - userAgentDesc: '留空時將自動使用用戶端的 User-Agent,僅在需要固定特定 UA 時填寫', - - // 基础標籤 - apiUrlLabel: 'API URL', - apiKeyLabel: 'API Key', - - // 更多缺失的翻譯鍵 - copyLinkTooltip: '複製連結', - - // Claude 訂閱類型顯示 - claudeMaxDisplay: 'Claude Max', - claudeProDisplay: 'Claude Pro', - - // 新增缺失的翻譯鍵 - modelSupportDescription: - '留空表示支援所有模型。如果指定模型,請求中的模型不在列表內將不會調度到此帳戶', - regenerateButton: '重新產生', - cancelButton: '取消', - credentialsFileDescription: '檔案中的憑證。', - authCodeInputHint: '請貼上從 Claude Code 授權頁面複製的 Authorization Code', - dailyQuotaLimitLabel: '每日額度限制 ($)', - todayUsageLabel: '今日使用情況', - remainingQuota: '剩餘: ${amount}', - usedPercentage: '{percentage}% 已使用', - modelMappingTableLabel: '模型映射表 (可選)', - modelMappingTableDescription: - '留空表示支援所有模型且不修改請求。配置映射後,左側模型會被識別為支援的模型,右側是實際發送的模型。', - customUserAgentLabel: '自定義 User-Agent (可選)', - rateLimitDurationLabel: '限流時間 (分鐘)', - awsRegionReferenceTitle: '常用 AWS 區域參考:', - azureModelTypeDescription: '選擇此部署支援的模型類型', - tokenUpdateDescription: - '可以更新 Access Token 和 Refresh Token。為了安全起見,不會顯示當前的 Token 值。', - tokenUpdateHint: '💡 留空表示不更新該欄位。', - - // Setup Token 流程翻譯 - clickToGenerateAuthLink: '點擊產生授權連結', - openLinkInBrowser: '在瀏覽器中開啟連結並完成授權', - openAuthLinkDescription: '請在新分頁中開啟授權連結,登錄您的 Claude 帳戶並授權。', - proxyWarning: '注意:如果您配置了代理,請確保瀏覽器也使用相同的代理訪問授權頁面。', - enterAuthorizationCode: '輸入授權碼', - copyAuthCodeDescription: '授權完成後,從返回頁面複製 Authorization Code,並貼上到下方輸入框:' - }, - - // OAuth Flow Component - oauthFlow: { - // 平台標題 - claudeAccountAuth: 'Claude 帳戶授權', - geminiAccountAuth: 'Gemini 帳戶授權', - openaiAccountAuth: 'OpenAI 帳戶授權', - - // 流程說明 - claudeAuthDescription: '請按照以下步驟完成 Claude 帳戶的授權:', - geminiAuthDescription: '請按照以下步驟完成 Gemini 帳戶的授權:', - openaiAuthDescription: '請按照以下步驟完成 OpenAI 帳戶的授權:', - - // 步驟標題 - step1Title: '點擊下方按銮產生授權連結', - step2Title: '在瀏覽器中打開連結並完成授權', - step3Title: '輸入 Authorization Code', - step3TitleOpenAI: '輸入授權連結或 Code', - - // 步驟描述 - step2Description: '請在新分頁中打開授權連結,登入您的 Claude 帳戶並授權。', - step2DescriptionGemini: '請在新分頁中打開授權連結,登入您的 Gemini 帳戶並授權。', - step2DescriptionOpenAI: '請在新分頁中打開授權連結,登入您的 OpenAI 帳戶並授權。', - - step3Description: '授權完成後,頁面會顯示一個', - step3DescriptionMiddle: ',請將其複製並貼上到下方輸入框:', - step3DescriptionGemini: - '授權完成後,頁面會顯示一個 Authorization Code,請將其複製並貼上到下方輸入框:', - step3DescriptionOpenAI: '授權完成後,當頁面地址變為', - step3DescriptionOpenAIMiddle: '時:', - - // 按銮文字 - generating: '產生中...', - generateAuthLink: '產生授權連結', - regenerate: '重新產生', - previousStep: '上一步', - completeAuth: '完成授權', - verifying: '驗證中...', - - // 占位符 - authCodePlaceholder: '貼上Claude頁面獲取的Authorization Code...', - authCodePlaceholderGemini: '貼上Gemini頁面獲取的Authorization Code...', - authCodePlaceholderOpenAI: - '方式1:複製完整的連結(http://localhost:1455/auth/callback?code=...)\n方式2:僅複製 code 參數的值\n系統會自動識別並提取所需資訊', - - // 標籤 - authorizationCode: 'Authorization Code', - authLinkOrCode: '授權連結或 Code', - - // 提示資訊 - copyLinkTooltip: '複製連結', - authCodeHint: '請貼上Claude頁面複製的Authorization Code', - authCodeHintGemini: '請貼上Gemini頁面複製的Authorization Code', - - // 注意事項 - proxyNotice: '注意:', - proxyNoticeText: '如果您設定了代理,請確保瀏覽器也使用相同的代理訪問授權頁面。', - - // OpenAI 特有提示 - openaiImportantNote: '重要提示:', - openaiLoadingNote: '授權後頁面可能會加載較長時間,請耐心等待。', - openaiAddressNote: '當瀏覽器地址欄變為', - openaiAddressNoteMiddle: '開頭時,表示授權已完成。', - - openaiTip: '提示:', - openaiTipText: '您可以直接複製整個連結或僅複製 code 參數值,系統會自動識別。', - openaiLinkExample: '• 完整連結示例:', - openaiCodeExample: '• 僅 Code 示例:', - - // 成功和錯誤消息 - successExtractCode: '成功提取授權碼!', - errorCodeNotFound: 'URL 中未找到授權碼參數,請檢查連結是否正確', - errorLinkFormat: '連結格式錯誤,請檢查是否為完整的 URL', - errorWrongUrlFormat: '請貼上以 http://localhost:1455 或 http://localhost:45462 開頭的連結', - linkCopied: '連結已複製', - authFailed: '授權失敗,請檢查授權碼是否正確', - generateAuthFailed: '產生授權連結失敗' - }, - - // Group Management Modal - groupManagement: { - title: '帳戶分組管理', - createNewGroup: '創建新分組', - createGroup: '創建新分組', - groupNameRequired: '分組名稱 *', - groupNamePlaceholder: '輸入分組名稱', - platformTypeRequired: '平台類型 *', - descriptionOptional: '描述 (可選)', - descriptionPlaceholder: '分組描述...', - creating: '創建中...', - create: '創建', - cancel: '取消', - loading: '加載中...', - noGroups: '暫無分組', - noDescription: '暫無描述', - membersCount: ' 個成員', - edit: '編輯', - delete: '刪除', - editGroup: '編輯分組', - platformTypeLabel: '平台類型', - cannotModify: '(不可修改)', - updating: '更新中...', - update: '更新', - // Toast messages - loadGroupsFailed: '加載分組列表失敗', - fillRequiredFields: '請填寫必填項', - groupCreated: '分組創建成功', - createGroupFailed: '創建分組失敗', - fillGroupName: '請填寫分組名稱', - groupUpdated: '分組更新成功', - updateGroupFailed: '更新分組失敗', - groupHasMembers: '分組內還有成員,無法刪除', - confirmDelete: '確定要刪除分組 "{name}" 嗎?', - groupDeleted: '分組刪除成功', - deleteGroupFailed: '刪除分組失敗' - }, - - // Proxy Configuration - proxyConfig: { - title: '代理設置 (可選)', - enableProxy: '啟用代理', - configDescription: '配置代理以訪問受限的網路資源。支持 SOCKS5 和 HTTP 代理。', - stabilityNotice: '請確保代理伺服器穩定可用,否則會影響帳戶的正常使用。', - proxyType: '代理類型', - hostAddress: '主機地址', - hostPlaceholder: '例如: 192.168.1.100', - port: '端口', - portPlaceholder: '例如: 1080', - needsAuth: '需要身份驗證', - username: '用戶名', - usernamePlaceholder: '代理用戶名', - password: '密碼', - passwordPlaceholder: '代理密碼', - tip: '提示:', - apiRequestNotice: '代理設置將用於所有與此帳戶相關的API請求。請確保代理伺服器支持HTTPS流量轉發。' - } -} diff --git a/web/admin-spa/src/main.js b/web/admin-spa/src/main.js index 41beb5b8..79181b6b 100644 --- a/web/admin-spa/src/main.js +++ b/web/admin-spa/src/main.js @@ -1,12 +1,12 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' +import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import 'element-plus/dist/index.css' import 'element-plus/theme-chalk/dark/css-vars.css' import App from './App.vue' import router from './router' import { useUserStore } from './stores/user' -import i18n from './i18n' import './assets/styles/main.css' import './assets/styles/global.css' @@ -20,11 +20,10 @@ app.use(pinia) // 使用路由 app.use(router) -// 使用Vue I18n -app.use(i18n) - -// 使用Element Plus - 语言配置在 App.vue 中通过 ElConfigProvider 处理 -app.use(ElementPlus) +// 使用Element Plus +app.use(ElementPlus, { + locale: zhCn +}) // 设置axios拦截器 const userStore = useUserStore() diff --git a/web/admin-spa/src/router/index.js b/web/admin-spa/src/router/index.js index afd13807..16a2b50a 100644 --- a/web/admin-spa/src/router/index.js +++ b/web/admin-spa/src/router/index.js @@ -2,7 +2,6 @@ import { createRouter, createWebHistory } from 'vue-router' import { useAuthStore } from '@/stores/auth' import { useUserStore } from '@/stores/user' import { APP_CONFIG } from '@/config/app' -import { showToast } from '@/utils/toast' // 路由懒加载 const LoginView = () => import('@/views/LoginView.vue') @@ -151,7 +150,7 @@ router.beforeEach(async (to, from, next) => { const authStore = useAuthStore() const userStore = useUserStore() - console.log('Router navigation:', { + console.log('路由导航:', { to: to.path, from: from.path, fullPath: to.fullPath, @@ -178,6 +177,8 @@ router.beforeEach(async (to, from, next) => { } catch (error) { // If the error is about disabled account, redirect to login with error if (error.message && error.message.includes('disabled')) { + // Import showToast to display the error + const { showToast } = await import('@/utils/toast') showToast(error.message, 'error') } return next('/user-login') diff --git a/web/admin-spa/src/stores/accounts.js b/web/admin-spa/src/stores/accounts.js index 9ca33b4f..f21caffc 100644 --- a/web/admin-spa/src/stores/accounts.js +++ b/web/admin-spa/src/stores/accounts.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { apiClient } from '@/config/api' -import i18n from '@/i18n' export const useAccountsStore = defineStore('accounts', () => { // 状态 @@ -28,7 +27,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { claudeAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取Claude账户失败') } } catch (err) { error.value = err.message @@ -47,7 +46,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { claudeConsoleAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取Claude Console账户失败') } } catch (err) { error.value = err.message @@ -66,7 +65,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { bedrockAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取Bedrock账户失败') } } catch (err) { error.value = err.message @@ -85,7 +84,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { geminiAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取Gemini账户失败') } } catch (err) { error.value = err.message @@ -104,7 +103,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { openaiAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取OpenAI账户失败') } } catch (err) { error.value = err.message @@ -123,7 +122,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { azureOpenaiAccounts.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('accounts.loadAccountsFailed')) + throw new Error(response.message || '获取Azure OpenAI账户失败') } } catch (err) { error.value = err.message @@ -184,7 +183,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchClaudeAccounts() return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '创建Claude账户失败') } } catch (err) { error.value = err.message @@ -204,9 +203,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchClaudeConsoleAccounts() return response.data } else { - throw new Error( - response.message || i18n.global.t('common.errors.createClaudeConsoleAccountFailed') - ) + throw new Error(response.message || '创建Claude Console账户失败') } } catch (err) { error.value = err.message @@ -226,7 +223,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchBedrockAccounts() return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '创建Bedrock账户失败') } } catch (err) { error.value = err.message @@ -246,7 +243,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchGeminiAccounts() return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '创建Gemini账户失败') } } catch (err) { error.value = err.message @@ -266,7 +263,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchOpenAIAccounts() return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '创建OpenAI账户失败') } } catch (err) { error.value = err.message @@ -286,9 +283,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchAzureOpenAIAccounts() return response.data } else { - throw new Error( - response.message || i18n.global.t('common.errors.createAzureOpenAIAccountFailed') - ) + throw new Error(response.message || '创建Azure OpenAI账户失败') } } catch (err) { error.value = err.message @@ -328,7 +323,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchClaudeAccounts() return response } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '更新Claude账户失败') } } catch (err) { error.value = err.message @@ -348,9 +343,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchClaudeConsoleAccounts() return response } else { - throw new Error( - response.message || i18n.global.t('common.errors.updateClaudeConsoleAccountFailed') - ) + throw new Error(response.message || '更新Claude Console账户失败') } } catch (err) { error.value = err.message @@ -370,7 +363,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchBedrockAccounts() return response } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '更新Bedrock账户失败') } } catch (err) { error.value = err.message @@ -390,7 +383,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchGeminiAccounts() return response } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '更新Gemini账户失败') } } catch (err) { error.value = err.message @@ -410,7 +403,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchOpenAIAccounts() return response } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '更新OpenAI账户失败') } } catch (err) { error.value = err.message @@ -430,9 +423,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchAzureOpenAIAccounts() return response } else { - throw new Error( - response.message || i18n.global.t('common.errors.updateAzureOpenAIAccountFailed') - ) + throw new Error(response.message || '更新Azure OpenAI账户失败') } } catch (err) { error.value = err.message @@ -507,7 +498,7 @@ export const useAccountsStore = defineStore('accounts', () => { } return response } else { - throw new Error(response.message || i18n.global.t('accounts.schedulingToggleFailed')) + throw new Error(response.message || '切换状态失败') } } catch (err) { error.value = err.message @@ -562,7 +553,7 @@ export const useAccountsStore = defineStore('accounts', () => { } return response } else { - throw new Error(response.message || i18n.global.t('accounts.deleteFailed')) + throw new Error(response.message || '删除失败') } } catch (err) { error.value = err.message @@ -582,7 +573,7 @@ export const useAccountsStore = defineStore('accounts', () => { await fetchClaudeAccounts() return response } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || 'Token刷新失败') } } catch (err) { error.value = err.message @@ -599,7 +590,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data // 返回整个对象,包含authUrl和sessionId } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '生成授权URL失败') } } catch (err) { error.value = err.message @@ -614,7 +605,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '交换授权码失败') } } catch (err) { error.value = err.message @@ -632,9 +623,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data // 返回整个对象,包含authUrl和sessionId } else { - throw new Error( - response.message || i18n.global.t('common.errors.generateSetupTokenUrlFailed') - ) + throw new Error(response.message || '生成Setup Token URL失败') } } catch (err) { error.value = err.message @@ -652,7 +641,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data } else { - throw new Error(response.message || i18n.global.t('common.errors.exchangeSetupTokenFailed')) + throw new Error(response.message || '交换Setup Token授权码失败') } } catch (err) { error.value = err.message @@ -667,7 +656,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data // 返回整个对象,包含authUrl和sessionId } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '生成授权URL失败') } } catch (err) { error.value = err.message @@ -682,7 +671,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '交换授权码失败') } } catch (err) { error.value = err.message @@ -697,7 +686,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data // 返回整个对象,包含authUrl和sessionId } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '生成授权URL失败') } } catch (err) { error.value = err.message @@ -712,7 +701,7 @@ export const useAccountsStore = defineStore('accounts', () => { if (response.success) { return response.data } else { - throw new Error(response.message || i18n.global.t('accounts.operationFailed')) + throw new Error(response.message || '交换授权码失败') } } catch (err) { error.value = err.message diff --git a/web/admin-spa/src/stores/apiKeys.js b/web/admin-spa/src/stores/apiKeys.js index 4b38afd3..9f294c13 100644 --- a/web/admin-spa/src/stores/apiKeys.js +++ b/web/admin-spa/src/stores/apiKeys.js @@ -1,5 +1,4 @@ import { apiClient } from '@/config/api' -import i18n from '@/i18n' import { defineStore } from 'pinia' import { ref } from 'vue' @@ -23,7 +22,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { if (response.success) { apiKeys.value = response.data || [] } else { - throw new Error(response.message || i18n.global.t('apiKeys.loadFailed')) + throw new Error(response.message || '获取API Keys失败') } } catch (err) { error.value = err.message @@ -43,7 +42,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { await fetchApiKeys() return response.data } else { - throw new Error(response.message || i18n.global.t('apiKeys.operationFailed')) + throw new Error(response.message || '创建API Key失败') } } catch (err) { error.value = err.message @@ -63,7 +62,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { await fetchApiKeys() return response } else { - throw new Error(response.message || i18n.global.t('apiKeys.updateFailed')) + throw new Error(response.message || '更新API Key失败') } } catch (err) { error.value = err.message @@ -83,7 +82,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { await fetchApiKeys() return response } else { - throw new Error(response.message || i18n.global.t('apiKeys.operationFailed')) + throw new Error(response.message || '切换状态失败') } } catch (err) { error.value = err.message @@ -103,7 +102,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { await fetchApiKeys() return response } else { - throw new Error(response.message || i18n.global.t('apiKeys.operationFailed')) + throw new Error(response.message || '续期失败') } } catch (err) { error.value = err.message @@ -123,7 +122,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => { await fetchApiKeys() return response } else { - throw new Error(response.message || i18n.global.t('apiKeys.deleteFailed')) + throw new Error(response.message || '删除失败') } } catch (err) { error.value = err.message @@ -142,10 +141,10 @@ export const useApiKeysStore = defineStore('apiKeys', () => { if (response.success) { return response.stats } else { - throw new Error(response.message || i18n.global.t('apiKeys.operationFailed')) + throw new Error(response.message || '获取统计失败') } } catch (err) { - console.error(i18n.global.t('common.errors.getApiKeyStatsFailed'), err) + console.error('获取API Key统计失败:', err) return null } } @@ -167,10 +166,10 @@ export const useApiKeysStore = defineStore('apiKeys', () => { if (response.success) { return response.data || [] } else { - throw new Error(response.message || i18n.global.t('apiKeys.operationFailed')) + throw new Error(response.message || '获取标签失败') } } catch (err) { - console.error(i18n.global.t('common.errors.getTagsFailed'), err) + console.error('获取标签失败:', err) return [] } } diff --git a/web/admin-spa/src/stores/apistats.js b/web/admin-spa/src/stores/apistats.js index 36ec781d..c07b16c7 100644 --- a/web/admin-spa/src/stores/apistats.js +++ b/web/admin-spa/src/stores/apistats.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { apiStatsClient } from '@/config/apiStats' -import i18n from '@/i18n' export const useApiStatsStore = defineStore('apistats', () => { // 状态 @@ -94,7 +93,7 @@ export const useApiStatsStore = defineStore('apistats', () => { } if (!apiKey.value.trim()) { - error.value = i18n.global.t('apiStats.enterApiKey') + error.value = '请输入 API Key' return } @@ -126,18 +125,14 @@ export const useApiStatsStore = defineStore('apistats', () => { // 更新 URL updateURL() } else { - throw new Error( - statsResult.message || i18n.global.t('common.errors.requestFailed', { status: 500 }) - ) + throw new Error(statsResult.message || '查询失败') } } else { - throw new Error( - idResult.message || i18n.global.t('common.errors.requestFailed', { status: 500 }) - ) + throw new Error(idResult.message || '获取 API Key ID 失败') } } catch (err) { console.error('Query stats error:', err) - error.value = err.message || i18n.global.t('apiStats.errors.queryStatsFailed') + error.value = err.message || '查询统计数据失败,请检查您的 API Key 是否正确' statsData.value = null modelStats.value = [] apiId.value = null @@ -214,7 +209,7 @@ export const useApiStatsStore = defineStore('apistats', () => { if (result.success) { modelStats.value = result.data || [] } else { - throw new Error(result.message || i18n.global.t('apiStats.errors.loadModelStatsFailed')) + throw new Error(result.message || '加载模型统计失败') } } catch (err) { console.error('Load model stats error:', err) @@ -271,13 +266,11 @@ export const useApiStatsStore = defineStore('apistats', () => { // 清除错误信息 error.value = '' } else { - throw new Error( - result.message || i18n.global.t('common.errors.requestFailed', { status: 500 }) - ) + throw new Error(result.message || '查询失败') } } catch (err) { console.error('Load stats with apiId error:', err) - error.value = err.message || i18n.global.t('apiStats.errors.queryStatsFailed') + error.value = err.message || '查询统计数据失败' statsData.value = null modelStats.value = [] } finally { @@ -337,7 +330,7 @@ export const useApiStatsStore = defineStore('apistats', () => { async function queryBatchStats() { const keys = parseApiKeys() if (keys.length === 0) { - error.value = i18n.global.t('apiStats.errors.enterAtLeastOneKey') + error.value = '请输入至少一个有效的 API Key' return } @@ -367,7 +360,7 @@ export const useApiStatsStore = defineStore('apistats', () => { }) if (validIds.length === 0) { - throw new Error(i18n.global.t('common.errors.allApiKeysInvalid')) + throw new Error('所有 API Key 都无效') } apiIds.value = validIds @@ -391,11 +384,11 @@ export const useApiStatsStore = defineStore('apistats', () => { // 更新 URL updateBatchURL() } else { - throw new Error(batchResult.message || i18n.global.t('apiStats.errors.batchQueryFailed')) + throw new Error(batchResult.message || '批量查询失败') } } catch (err) { console.error('Batch query error:', err) - error.value = err.message || i18n.global.t('apiStats.errors.batchQueryFailed') + error.value = err.message || '批量查询统计数据失败' aggregatedStats.value = null individualStats.value = [] } finally { @@ -415,7 +408,7 @@ export const useApiStatsStore = defineStore('apistats', () => { if (result.success) { modelStats.value = result.data || [] } else { - throw new Error(result.message || i18n.global.t('apiStats.errors.batchModelStatsFailed')) + throw new Error(result.message || '加载批量模型统计失败') } } catch (err) { console.error('Load batch model stats error:', err) diff --git a/web/admin-spa/src/stores/auth.js b/web/admin-spa/src/stores/auth.js index 61240b71..bbd39c62 100644 --- a/web/admin-spa/src/stores/auth.js +++ b/web/admin-spa/src/stores/auth.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import router from '@/router' -import i18n from '@/i18n' import { apiClient } from '@/config/api' export const useAuthStore = defineStore('auth', () => { @@ -40,10 +39,10 @@ export const useAuthStore = defineStore('auth', () => { await router.push('/dashboard') } else { - loginError.value = result.message || i18n.global.t('login.loginFailed') + loginError.value = result.message || '登录失败' } } catch (error) { - loginError.value = error.message || i18n.global.t('login.loginFailedCheck') + loginError.value = error.message || '登录失败,请检查用户名和密码' } finally { loginLoading.value = false } @@ -103,11 +102,11 @@ export const useAuthStore = defineStore('auth', () => { // 设置页面标题 if (result.data.siteName) { - document.title = `${result.data.siteName} - ${i18n.global.t('header.adminPanel')}` + document.title = `${result.data.siteName} - 管理后台` } } } catch (error) { - console.error(i18n.global.t('common.errors.loadOemSettingsFailed'), error) + console.error('加载OEM设置失败:', error) } finally { oemLoading.value = false } diff --git a/web/admin-spa/src/stores/clients.js b/web/admin-spa/src/stores/clients.js index 10a0476b..7926c69d 100644 --- a/web/admin-spa/src/stores/clients.js +++ b/web/admin-spa/src/stores/clients.js @@ -1,6 +1,5 @@ import { defineStore } from 'pinia' import { apiClient } from '@/config/api' -import i18n from '@/i18n' export const useClientsStore = defineStore('clients', { state: () => ({ @@ -25,13 +24,13 @@ export const useClientsStore = defineStore('clients', { if (response.success) { this.supportedClients = response.data || [] } else { - this.error = response.message || i18n.global.t('common.errors.loadSupportedClientsFailed') + this.error = response.message || '加载支持的客户端失败' console.error('Failed to load supported clients:', this.error) } return this.supportedClients } catch (error) { - this.error = error.message || i18n.global.t('common.errors.loadSupportedClientsFailed') + this.error = error.message || '加载支持的客户端失败' console.error('Error loading supported clients:', error) return [] } finally { diff --git a/web/admin-spa/src/stores/dashboard.js b/web/admin-spa/src/stores/dashboard.js index 9531fcb1..20cc40b6 100644 --- a/web/admin-spa/src/stores/dashboard.js +++ b/web/admin-spa/src/stores/dashboard.js @@ -2,7 +2,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { apiClient } from '@/config/api' import { showToast } from '@/utils/toast' -import i18n from '@/i18n' export const useDashboardStore = defineStore('dashboard', () => { // 状态 @@ -42,7 +41,7 @@ export const useDashboardStore = defineStore('dashboard', () => { realtimeTPM: 0, metricsWindow: 5, isHistoricalMetrics: false, - systemStatus: i18n.global.t('system.status.normal'), + systemStatus: '正常', uptime: 0, systemTimezone: 8 // 默认 UTC+8 }) @@ -69,9 +68,9 @@ export const useDashboardStore = defineStore('dashboard', () => { customEnd: '', customRange: null, presetOptions: [ - { value: 'today', label: i18n.global.t('dashboard.today'), days: 1 }, - { value: '7days', label: i18n.global.t('dashboard.last7Days'), days: 7 }, - { value: '30days', label: i18n.global.t('dashboard.last30Days'), days: 30 } + { value: 'today', label: '今日', days: 1 }, + { value: '7days', label: '7天', days: 7 }, + { value: '30days', label: '30天', days: 30 } ] }) @@ -90,11 +89,11 @@ export const useDashboardStore = defineStore('dashboard', () => { const minutes = Math.floor((seconds % 3600) / 60) if (days > 0) { - return i18n.global.t('dashboard.uptimeFormat.daysHours', { days, hours }) + return `${days}天 ${hours}小时` } else if (hours > 0) { - return i18n.global.t('dashboard.uptimeFormat.hoursMinutes', { hours, minutes }) + return `${hours}小时 ${minutes}分钟` } else { - return i18n.global.t('dashboard.uptimeFormat.minutes', { minutes }) + return `${minutes}分钟` } }) @@ -199,9 +198,7 @@ export const useDashboardStore = defineStore('dashboard', () => { realtimeTPM: realtimeMetrics.tpm || 0, metricsWindow: realtimeMetrics.windowMinutes || 5, isHistoricalMetrics: realtimeMetrics.isHistorical || false, - systemStatus: systemHealth.redisConnected - ? i18n.global.t('system.status.normal') - : i18n.global.t('system.status.abnormal'), + systemStatus: systemHealth.redisConnected ? '正常' : '异常', uptime: systemHealth.uptime || 0, systemTimezone: dashboardResponse.data.systemTimezone || 8 } @@ -221,7 +218,7 @@ export const useDashboardStore = defineStore('dashboard', () => { } } } catch (error) { - console.error(i18n.global.t('common.errors.loadDashboardFailed'), error) + console.error('加载仪表板数据失败:', error) } finally { loading.value = false } @@ -309,7 +306,7 @@ export const useDashboardStore = defineStore('dashboard', () => { trendData.value = response.data } } catch (error) { - console.error(i18n.global.t('common.errors.loadUsageTrendFailed'), error) + console.error('加载使用趋势失败:', error) } } @@ -400,7 +397,7 @@ export const useDashboardStore = defineStore('dashboard', () => { dashboardModelStats.value = response.data } } catch (error) { - console.error(i18n.global.t('common.errors.loadModelStatsFailed'), error) + console.error('加载模型统计失败:', error) } } @@ -502,7 +499,7 @@ export const useDashboardStore = defineStore('dashboard', () => { } } } catch (error) { - console.error(i18n.global.t('common.errors.loadApiKeysTrendFailed'), error) + console.error('加载API Keys趋势失败:', error) } } @@ -639,14 +636,14 @@ export const useDashboardStore = defineStore('dashboard', () => { // 小时粒度:限制 24 小时 const hoursDiff = (end - start) / (1000 * 60 * 60) if (hoursDiff > 24) { - showToast(i18n.global.t('dashboard.errors.rangeTooLongHour'), 'warning') + showToast('小时粒度下日期范围不能超过24小时', 'warning') return } } else { // 天粒度:限制 31 天 const daysDiff = Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1 if (daysDiff > 31) { - showToast(i18n.global.t('dashboard.errors.rangeTooLongDay'), 'warning') + showToast('日期范围不能超过 31 天', 'warning') return } } @@ -665,13 +662,9 @@ export const useDashboardStore = defineStore('dashboard', () => { // 根据粒度更新预设选项 if (granularity === 'hour') { dateFilter.value.presetOptions = [ - { - value: 'last24h', - label: i18n.global.t('dashboard.usageTrend.periodOptions.last24Hours'), - hours: 24 - }, - { value: 'yesterday', label: i18n.global.t('dashboard.yesterday'), hours: 24 }, - { value: 'dayBefore', label: i18n.global.t('dashboard.dayBefore'), hours: 24 } + { value: 'last24h', label: '近24小时', hours: 24 }, + { value: 'yesterday', label: '昨天', hours: 24 }, + { value: 'dayBefore', label: '前天', hours: 24 } ] // 检查当前自定义日期范围是否超过24小时 @@ -684,7 +677,7 @@ export const useDashboardStore = defineStore('dashboard', () => { const end = new Date(dateFilter.value.customRange[1]) const hoursDiff = (end - start) / (1000 * 60 * 60) if (hoursDiff > 24) { - showToast(i18n.global.t('dashboard.errors.rangeTooLongHourSwitched'), 'warning') + showToast('小时粒度下日期范围不能超过24小时,已切换到近24小时', 'warning') setDateFilterPreset('last24h') return } @@ -698,9 +691,9 @@ export const useDashboardStore = defineStore('dashboard', () => { } else { // 天粒度 dateFilter.value.presetOptions = [ - { value: 'today', label: i18n.global.t('dashboard.today'), days: 1 }, - { value: '7days', label: i18n.global.t('dashboard.last7Days'), days: 7 }, - { value: '30days', label: i18n.global.t('dashboard.last30Days'), days: 30 } + { value: 'today', label: '今日', days: 1 }, + { value: '7days', label: '7天', days: 7 }, + { value: '30days', label: '30天', days: 30 } ] // 如果当前是小时粒度的预设,切换到天粒度的默认预设 diff --git a/web/admin-spa/src/stores/locale.js b/web/admin-spa/src/stores/locale.js deleted file mode 100644 index eb795048..00000000 --- a/web/admin-spa/src/stores/locale.js +++ /dev/null @@ -1,47 +0,0 @@ -import { defineStore } from 'pinia' -import { ref } from 'vue' -import { i18n, SUPPORTED_LOCALES, getSupportedLocalesWithI18n } from '@/i18n' - -export const useLocaleStore = defineStore('locale', () => { - const currentLocale = ref(i18n.global.locale.value) - - // 切换语言 - const setLocale = (locale) => { - if (!SUPPORTED_LOCALES[locale]) { - console.warn(`Unsupported locale: ${locale}`) - return - } - - currentLocale.value = locale - i18n.global.locale.value = locale - localStorage.setItem('app-locale', locale) - - // 更新HTML lang属性 - document.documentElement.setAttribute('lang', locale) - } - - // 获取当前语言信息(兼容i18n) - const getCurrentLocaleInfo = (t = null) => { - if (t) { - const supportedLocales = getSupportedLocalesWithI18n(t) - return supportedLocales[currentLocale.value] || supportedLocales['zh-cn'] - } - return SUPPORTED_LOCALES[currentLocale.value] || SUPPORTED_LOCALES['zh-cn'] - } - - // 获取所有支持的语言(兼容i18n) - const getSupportedLocales = (t = null) => { - const supportedLocales = t ? getSupportedLocalesWithI18n(t) : SUPPORTED_LOCALES - return Object.entries(supportedLocales).map(([key, value]) => ({ - code: key, - ...value - })) - } - - return { - currentLocale, - setLocale, - getCurrentLocaleInfo, - getSupportedLocales - } -}) diff --git a/web/admin-spa/src/stores/settings.js b/web/admin-spa/src/stores/settings.js index 82279a7a..986ce327 100644 --- a/web/admin-spa/src/stores/settings.js +++ b/web/admin-spa/src/stores/settings.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { apiClient } from '@/config/api' -import i18n from '@/i18n' export const useSettingsStore = defineStore('settings', () => { // 状态 @@ -78,7 +77,7 @@ export const useSettingsStore = defineStore('settings', () => { const applyOemSettings = () => { // 更新页面标题 if (oemSettings.value.siteName) { - document.title = `${oemSettings.value.siteName} - ${i18n.global.t('header.adminPanel')}` + document.title = `${oemSettings.value.siteName} - 管理后台` } // 更新favicon @@ -95,9 +94,7 @@ export const useSettingsStore = defineStore('settings', () => { // 格式化日期时间 const formatDateTime = (dateString) => { if (!dateString) return '' - const localeMap = { 'zh-cn': 'zh-CN', 'zh-tw': 'zh-TW', en: 'en-US' } - const currentLocale = localeMap[i18n.global.locale.value] || 'en-US' - return new Date(dateString).toLocaleString(currentLocale, { + return new Date(dateString).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', @@ -113,13 +110,13 @@ export const useSettingsStore = defineStore('settings', () => { // 检查文件大小 (350KB) if (file.size > 350 * 1024) { - errors.push(i18n.global.t('settings.validation.iconTooLarge')) + errors.push('图标文件大小不能超过 350KB') } // 检查文件类型 const allowedTypes = ['image/x-icon', 'image/png', 'image/jpeg', 'image/jpg', 'image/svg+xml'] if (!allowedTypes.includes(file.type)) { - errors.push(i18n.global.t('settings.validation.iconTypeNotSupported')) + errors.push('不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件') } return { diff --git a/web/admin-spa/src/stores/user.js b/web/admin-spa/src/stores/user.js index 8473853f..db6a4dfc 100644 --- a/web/admin-spa/src/stores/user.js +++ b/web/admin-spa/src/stores/user.js @@ -1,6 +1,5 @@ import { defineStore } from 'pinia' import axios from 'axios' -import i18n from '@/i18n' import { showToast } from '@/utils/toast' const API_BASE = '/users' @@ -41,7 +40,7 @@ export const useUserStore = defineStore('user', { return response.data } else { - throw new Error(response.data.message || i18n.global.t('user.login.loginFailed')) + throw new Error(response.data.message || 'Login failed') } } catch (error) { this.clearAuth() @@ -116,9 +115,7 @@ export const useUserStore = defineStore('user', { this.clearAuth() // If it's a disabled account error, throw a specific error if (error.response?.status === 403) { - throw new Error( - error.response.data?.message || i18n.global.t('user.login.accountDisabled') - ) + throw new Error(error.response.data?.message || 'Your account has been disabled') } } throw error diff --git a/web/admin-spa/src/utils/format.js b/web/admin-spa/src/utils/format.js index 5ee258c3..dba9a58b 100644 --- a/web/admin-spa/src/utils/format.js +++ b/web/admin-spa/src/utils/format.js @@ -37,9 +37,7 @@ export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { .replace('ss', seconds) } -// 相对时间格式化(使用 i18n) -import i18n from '@/i18n' - +// 相对时间格式化 export function formatRelativeTime(date) { if (!date) return '' @@ -52,13 +50,13 @@ export function formatRelativeTime(date) { const diffDays = Math.floor(diffHours / 24) if (diffDays > 0) { - return i18n.global.t('common.time.daysAgo', { days: diffDays }) + return `${diffDays}天前` } else if (diffHours > 0) { - return i18n.global.t('common.time.hoursAgo', { hours: diffHours }) + return `${diffHours}小时前` } else if (diffMins > 0) { - return i18n.global.t('common.time.minutesAgo', { minutes: diffMins }) + return `${diffMins}分钟前` } else { - return i18n.global.t('common.time.justNow') + return '刚刚' } } diff --git a/web/admin-spa/src/views/AccountsView.vue b/web/admin-spa/src/views/AccountsView.vue index f89cfa02..779d140d 100644 --- a/web/admin-spa/src/views/AccountsView.vue +++ b/web/admin-spa/src/views/AccountsView.vue @@ -4,10 +4,10 @@

- {{ t('accounts.title') }} + 账户管理

- {{ t('accounts.description') }} + 管理您的 Claude、Gemini、OpenAI、Azure OpenAI、OpenAI-Responses 与 CCR 账户及代理配置

@@ -23,7 +23,7 @@ icon="fa-sort-amount-down" icon-color="text-indigo-500" :options="sortOptions" - :placeholder="t('accounts.sortBy')" + placeholder="选择排序" @change="sortAccounts()" />
@@ -38,7 +38,7 @@ icon="fa-server" icon-color="text-blue-500" :options="platformOptions" - :placeholder="t('accounts.selectPlatform')" + placeholder="选择平台" @change="filterByPlatform" />
@@ -53,14 +53,18 @@ icon="fa-layer-group" icon-color="text-purple-500" :options="groupOptions" - :placeholder="t('accounts.selectGroup')" + placeholder="选择分组" @change="filterByGroup" />
- +
@@ -89,14 +93,14 @@ @click.stop="openCreateAccountModal" > - {{ t('accounts.addAccount') }} + 添加账户
-

{{ t('accounts.loadingAccounts') }}

+

正在加载账户...

@@ -105,10 +109,8 @@ >
-

{{ t('accounts.noAccounts') }}

-

- {{ t('accounts.noAccountsHint') }} -

+

暂无账户

+

点击上方按钮添加您的第一个账户

@@ -120,7 +122,7 @@ class="w-[22%] min-w-[180px] cursor-pointer px-3 py-4 text-left text-xs font-bold uppercase tracking-wider text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600" @click="sortAccounts('name')" > - {{ t('accounts.name') }} + 名称 - {{ t('accounts.platformType') }} + 平台/类型 - {{ t('accounts.status') }} + 状态 - {{ t('accounts.priority') }} + 优先级 - {{ t('accounts.proxy') }} + 代理 - {{ t('accounts.dailyUsage') }} + 今日使用
- {{ t('accounts.sessionWindow') }} + 会话窗口 + + diff --git a/web/admin-spa/src/views/UserDashboardView.vue b/web/admin-spa/src/views/UserDashboardView.vue index f13e52d5..92b2ed4d 100644 --- a/web/admin-spa/src/views/UserDashboardView.vue +++ b/web/admin-spa/src/views/UserDashboardView.vue @@ -32,7 +32,7 @@ ]" @click="handleTabChange('overview')" > - {{ t('user.dashboard.overview') }} + Overview
@@ -95,11 +94,9 @@
-

- {{ t('user.dashboard.title') }} -

+

Dashboard Overview

- {{ t('user.dashboard.welcomeMessage') }} + Welcome to your Claude Relay dashboard

@@ -126,7 +123,7 @@
- {{ t('user.dashboard.activeApiKeys') }} + Active API Keys
{{ apiKeysStats.active }} @@ -158,7 +155,7 @@
- {{ t('user.dashboard.deletedApiKeys') }} + Deleted API Keys
{{ apiKeysStats.deleted }} @@ -190,7 +187,7 @@
- {{ t('user.dashboard.totalRequests') }} + Total Requests
{{ formatNumber(userProfile?.totalUsage?.requests || 0) }} @@ -222,7 +219,7 @@
- {{ t('user.dashboard.inputTokens') }} + Input Tokens
{{ formatNumber(userProfile?.totalUsage?.inputTokens || 0) }} @@ -254,7 +251,7 @@
- {{ t('user.dashboard.totalCost') }} + Total Cost
${{ (userProfile?.totalUsage?.totalCost || 0).toFixed(4) }} @@ -270,38 +267,30 @@

- {{ t('user.dashboard.accountInformation') }} + Account Information

-
- {{ t('user.dashboard.username') }} -
+
Username
{{ userProfile?.username }}
-
- {{ t('user.dashboard.displayName') }} -
+
Display Name
- {{ userProfile?.displayName || t('user.dashboard.notAvailable') }} + {{ userProfile?.displayName || 'N/A' }}
-
- {{ t('user.dashboard.email') }} -
+
Email
- {{ userProfile?.email || t('user.dashboard.notAvailable') }} + {{ userProfile?.email || 'N/A' }}
-
- {{ t('user.dashboard.role') }} -
+
Role
-
- {{ t('user.dashboard.memberSince') }} -
+
Member Since
{{ formatDate(userProfile?.createdAt) }}
-
- {{ t('user.dashboard.lastLogin') }} -
+
Last Login
- {{ formatDate(userProfile?.lastLoginAt) || t('user.dashboard.notAvailable') }} + {{ formatDate(userProfile?.lastLoginAt) || 'N/A' }}
@@ -353,7 +338,6 @@ - - diff --git a/web/admin-spa/src/views/tutorials/TutorialView-zh-cn.vue b/web/admin-spa/src/views/tutorials/TutorialView-zh-cn.vue deleted file mode 100644 index ac938825..00000000 --- a/web/admin-spa/src/views/tutorials/TutorialView-zh-cn.vue +++ /dev/null @@ -1,1690 +0,0 @@ - - - - - diff --git a/web/admin-spa/src/views/tutorials/TutorialView-zh-tw.vue b/web/admin-spa/src/views/tutorials/TutorialView-zh-tw.vue deleted file mode 100644 index f24feab3..00000000 --- a/web/admin-spa/src/views/tutorials/TutorialView-zh-tw.vue +++ /dev/null @@ -1,1690 +0,0 @@ - - - - - diff --git a/web/admin-spa/vite.config.js b/web/admin-spa/vite.config.js index 3193bf80..7b677438 100644 --- a/web/admin-spa/vite.config.js +++ b/web/admin-spa/vite.config.js @@ -51,9 +51,7 @@ export default defineConfig(({ mode }) => { imports: ['vue', 'vue-router', 'pinia'] }), Components({ - // 仅自动注册 Element Plus 组件;本地组件显式导入 - resolvers: [ElementPlusResolver()], - dirs: [] + resolvers: [ElementPlusResolver()] }) ], resolve: { @@ -105,8 +103,6 @@ export default defineConfig(({ mode }) => { build: { outDir: 'dist', assetsDir: 'assets', - // 提升 chunk 大小限制以消除 UI 库的大量警告 - chunkSizeWarningLimit: 1024, rollupOptions: { output: { manualChunks(id) {