+ type="number" step="any" min="0" class="input text-sm" :placeholder="t('admin.channels.form.pricePlaceholder', 'Default')" />
@@ -255,8 +255,8 @@ const collapsed = ref(props.entry.models.length > 0)
const billingModeOptions = computed(() => [
{ value: 'token', label: 'Token' },
- { value: 'per_request', label: t('admin.channels.billingMode.perRequest', '按次') },
- { value: 'image', label: t('admin.channels.billingMode.image', '图片(按次)') }
+ { value: 'per_request', label: t('admin.channels.billingMode.perRequest', 'Per Request') },
+ { value: 'image', label: t('admin.channels.billingMode.image', 'Image (per request)') }
])
const billingModeLabel = computed(() => {
diff --git a/frontend/src/components/admin/channel/__tests__/types.spec.ts b/frontend/src/components/admin/channel/__tests__/types.spec.ts
index 9fd8e066e0..6594baa73f 100644
--- a/frontend/src/components/admin/channel/__tests__/types.spec.ts
+++ b/frontend/src/components/admin/channel/__tests__/types.spec.ts
@@ -23,7 +23,7 @@ describe('validateIntervals', () => {
makeInterval({ min_tokens: 0, max_tokens: null, input_price: 1, output_price: 1 }),
makeInterval({ min_tokens: 200000, max_tokens: 500000, input_price: 2, output_price: 2 }),
]
- expect(validateIntervals(intervals, 'token')).toMatch(/无上限/)
+ expect(validateIntervals(intervals, 'token')).toMatch(/unbounded interval/)
})
it('accepts unbounded interval at the end', () => {
@@ -39,7 +39,7 @@ describe('validateIntervals', () => {
makeInterval({ min_tokens: 0, max_tokens: 250000, input_price: 1, output_price: 1 }),
makeInterval({ min_tokens: 200000, max_tokens: 500000, input_price: 2, output_price: 2 }),
]
- expect(validateIntervals(intervals, 'token')).toMatch(/重叠/)
+ expect(validateIntervals(intervals, 'token')).toMatch(/overlap/)
})
it('defaults mode to token when omitted', () => {
@@ -47,7 +47,7 @@ describe('validateIntervals', () => {
makeInterval({ min_tokens: 0, max_tokens: null, input_price: 1, output_price: 1 }),
makeInterval({ min_tokens: 100, max_tokens: 200, input_price: 2, output_price: 2 }),
]
- expect(validateIntervals(intervals)).toMatch(/无上限/)
+ expect(validateIntervals(intervals)).toMatch(/unbounded interval/)
})
})
@@ -66,14 +66,14 @@ describe('validateIntervals', () => {
const intervals: IntervalFormEntry[] = [
makeInterval({ tier_label: '1K', per_request_price: -1 }),
]
- expect(validateIntervals(intervals, 'image')).toMatch(/不能为负数/)
+ expect(validateIntervals(intervals, 'image')).toMatch(/cannot be negative/)
})
it('still rejects max <= min on a single tier', () => {
const intervals: IntervalFormEntry[] = [
makeInterval({ tier_label: '1K', min_tokens: 100, max_tokens: 50, per_request_price: 0.04 }),
]
- expect(validateIntervals(intervals, 'image')).toMatch(/必须大于/)
+ expect(validateIntervals(intervals, 'image')).toMatch(/must be greater/)
})
})
})
diff --git a/frontend/src/components/admin/channel/types.ts b/frontend/src/components/admin/channel/types.ts
index dd9dcd1a52..053279a7bb 100644
--- a/frontend/src/components/admin/channel/types.ts
+++ b/frontend/src/components/admin/channel/types.ts
@@ -143,14 +143,14 @@ export function validateIntervals(
function validateSingleInterval(iv: IntervalFormEntry, idx: number): string | null {
if (iv.min_tokens < 0) {
- return `区间 #${idx + 1}: 最小 token 数 (${iv.min_tokens}) 不能为负数`
+ return `Interval #${idx + 1}: minimum token count (${iv.min_tokens}) cannot be negative`
}
if (iv.max_tokens != null) {
if (iv.max_tokens <= 0) {
- return `区间 #${idx + 1}: 最大 token 数 (${iv.max_tokens}) 必须大于 0`
+ return `Interval #${idx + 1}: maximum token count (${iv.max_tokens}) must be greater than 0`
}
if (iv.max_tokens <= iv.min_tokens) {
- return `区间 #${idx + 1}: 最大 token 数 (${iv.max_tokens}) 必须大于最小 token 数 (${iv.min_tokens})`
+ return `Interval #${idx + 1}: maximum token count (${iv.max_tokens}) must be greater than minimum token count (${iv.min_tokens})`
}
}
return validateIntervalPrices(iv, idx)
@@ -158,15 +158,15 @@ function validateSingleInterval(iv: IntervalFormEntry, idx: number): string | nu
function validateIntervalPrices(iv: IntervalFormEntry, idx: number): string | null {
const prices: [string, number | string | null][] = [
- ['输入价格', iv.input_price],
- ['输出价格', iv.output_price],
- ['缓存写入价格', iv.cache_write_price],
- ['缓存读取价格', iv.cache_read_price],
- ['单次价格', iv.per_request_price],
+ ['input price', iv.input_price],
+ ['output price', iv.output_price],
+ ['cache write price', iv.cache_write_price],
+ ['cache read price', iv.cache_read_price],
+ ['per-request price', iv.per_request_price],
]
for (const [name, val] of prices) {
if (val != null && val !== '' && Number(val) < 0) {
- return `区间 #${idx + 1}: ${name}不能为负数`
+ return `Interval #${idx + 1}: ${name} cannot be negative`
}
}
return null
@@ -176,14 +176,14 @@ function checkIntervalOverlap(sorted: IntervalFormEntry[]): string | null {
for (let i = 0; i < sorted.length; i++) {
// 无上限区间必须是最后一个
if (sorted[i].max_tokens == null && i < sorted.length - 1) {
- return `区间 #${i + 1}: 无上限区间(最大 token 数为空)只能是最后一个`
+ return `Interval #${i + 1}: an unbounded interval (empty maximum token count) must be last`
}
if (i === 0) continue
const prev = sorted[i - 1]
// (min, max] 语义:前一个区间上界 > 当前区间下界则重叠
if (prev.max_tokens == null || prev.max_tokens > sorted[i].min_tokens) {
const prevMax = prev.max_tokens == null ? '∞' : String(prev.max_tokens)
- return `区间 #${i} 和 #${i + 1} 重叠:前一个区间上界 (${prevMax}) 大于当前区间下界 (${sorted[i].min_tokens})`
+ return `Intervals #${i} and #${i + 1} overlap: previous upper bound (${prevMax}) is greater than current lower bound (${sorted[i].min_tokens})`
}
}
return null
diff --git a/frontend/src/components/auth/DingTalkOAuthSection.vue b/frontend/src/components/auth/DingTalkOAuthSection.vue
index 9003225d78..55b919e659 100644
--- a/frontend/src/components/auth/DingTalkOAuthSection.vue
+++ b/frontend/src/components/auth/DingTalkOAuthSection.vue
@@ -19,7 +19,7 @@
font-weight="bold"
fill="white"
text-anchor="middle"
- >钉
+ >D
{{ t('auth.dingtalk.signIn') }}
diff --git a/frontend/src/components/auth/LoginAgreementPrompt.vue b/frontend/src/components/auth/LoginAgreementPrompt.vue
index dd71cbdcbc..f73a3b1c4c 100644
--- a/frontend/src/components/auth/LoginAgreementPrompt.vue
+++ b/frontend/src/components/auth/LoginAgreementPrompt.vue
@@ -17,7 +17,7 @@
for="login-agreement-consent"
class="cursor-pointer text-gray-700 dark:text-dark-200"
>
- 我已阅读并同意
+ {{ t('legal.loginAgreementPrompt.checkboxPrefix') }}
{{ doc.title }}
- 、
+ {{ t('legal.loginAgreementPrompt.documentSeparator') }}
@@ -42,9 +42,9 @@
-
继续登录前需要先同意最新条款。
+
{{ t('legal.loginAgreementPrompt.noticeTitle') }}
- 未同意前,账号密码输入和快捷登录会保持禁用。
+ {{ t('legal.loginAgreementPrompt.noticeDescription') }}