refactor(frontend): modularize i18n architecture

This commit is contained in:
elky
2026-06-30 17:01:39 +08:00
parent bf29129a4b
commit 9f138d09e6
118 changed files with 11940 additions and 5368 deletions
@@ -1002,7 +1002,7 @@
variant="outline"
@click="handleClose"
>
关闭
{{ legacyT('关闭') }}
</Button>
</template>
</Dialog>
@@ -1010,10 +1010,10 @@
<!-- 删除端点确认弹窗 -->
<AlertDialog
:model-value="deleteConfirmOpen"
title="删除端点"
:title="legacyT('删除端点')"
:description="deleteConfirmDescription"
confirm-text="删除"
cancel-text="取消"
:confirm-text="legacyT('删除')"
:cancel-text="legacyT('取消')"
type="danger"
@update:model-value="deleteConfirmOpen = $event"
@confirm="confirmDeleteEndpoint"
@@ -1047,6 +1047,7 @@ import { Settings, Trash2, Check, X, Power, ChevronRight, Plus, Shuffle, RotateC
import { useToast } from '@/composables/useToast'
import { parseApiError } from '@/utils/errorParser'
import { log } from '@/utils/logger'
import { useI18n } from '@/i18n'
import AlertDialog from '@/components/common/AlertDialog.vue'
import EndpointConditionEditor from './EndpointConditionEditor.vue'
import ProxyNodeSelect from './ProxyNodeSelect.vue'
@@ -1142,16 +1143,21 @@ const isEndpointFormatConversionDisabled = computed(() => {
// 获取禁用提示
const formatConversionDisabledTooltip = computed(() => {
if (props.systemFormatConversionEnabled) {
return '请先关闭系统级开关'
return legacyT('请先关闭系统级开关')
}
if (props.providerFormatConversionEnabled) {
return '请先关闭提供商级开关'
return legacyT('请先关闭提供商级开关')
}
return ''
})
const { success, error: showError } = useToast()
const proxyNodesStore = useProxyNodesStore()
const { legacyT, locale } = useI18n()
function localizedApiError(error: unknown, fallback: string): string {
return legacyT(parseApiError(error, fallback))
}
// 规则 Select 的展开状态(与 Collapsible 分开管理)
const ruleSelectOpen = ref<Record<string, boolean>>({})
@@ -1488,58 +1494,70 @@ function isJsonObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === 'object' && !Array.isArray(value)
}
function formatJsonRuleFieldLabel(label: string, index: number, field: string): string {
return locale.value === 'en-US'
? `${label} entry ${index + 1}: ${field}`
: `${label}第 ${index + 1} 条:${field}`
}
function formatJsonRuleError(label: string, index: number, message: string): string {
return locale.value === 'en-US'
? `${label} entry ${index + 1}: ${legacyT(message)}`
: `${label}第 ${index + 1} 条:${legacyT(message)}`
}
function readJsonRulesArray(root: Record<string, unknown>, key: keyof EndpointRulesJsonPayload, label: string): { value: unknown[]; error: string | null } {
const raw = root[key]
if (raw === undefined || raw === null) return { value: [], error: null }
if (!Array.isArray(raw)) return { value: [], error: `${label} 必须是数组或 null` }
if (!Array.isArray(raw)) return { value: [], error: `${label} ${legacyT('必须是数组或 null')}` }
return { value: raw, error: null }
}
function validateJsonCondition(rule: Record<string, unknown>, label: string, index: number): string | null {
const raw = rule.condition
if (raw === undefined || raw === null) return null
if (!isJsonObject(raw)) return `${label}第 ${index + 1} 条:condition 必须是对象`
const shapeError = validateJsonConditionShape(raw, `${label}第 ${index + 1} 条:condition`)
if (!isJsonObject(raw)) return formatJsonRuleError(label, index, 'condition 必须是对象')
const shapeError = validateJsonConditionShape(raw, formatJsonRuleFieldLabel(label, index, 'condition'))
if (shapeError) return shapeError
const editable = conditionToEditable(raw as BodyRule['condition'])
const err = validateEditableCondition(editable)
return err ? `${label}第 ${index + 1} 条:${err}` : null
return err ? formatJsonRuleError(label, index, err) : null
}
function validateJsonConditionShape(condition: Record<string, unknown>, label: string): string | null {
if (Object.prototype.hasOwnProperty.call(condition, 'all')) {
if (!Array.isArray(condition.all)) return `${label}.all 必须是数组`
if (!Array.isArray(condition.all)) return `${label}.all ${legacyT('必须是数组')}`
for (let i = 0; i < condition.all.length; i++) {
const child = condition.all[i]
if (!isJsonObject(child)) return `${label}.all[${i}] 必须是对象`
if (!isJsonObject(child)) return `${label}.all[${i}] ${legacyT('必须是对象')}`
const err = validateJsonConditionShape(child, `${label}.all[${i}]`)
if (err) return err
}
return null
}
if (Object.prototype.hasOwnProperty.call(condition, 'any')) {
if (!Array.isArray(condition.any)) return `${label}.any 必须是数组`
if (!Array.isArray(condition.any)) return `${label}.any ${legacyT('必须是数组')}`
for (let i = 0; i < condition.any.length; i++) {
const child = condition.any[i]
if (!isJsonObject(child)) return `${label}.any[${i}] 必须是对象`
if (!isJsonObject(child)) return `${label}.any[${i}] ${legacyT('必须是对象')}`
const err = validateJsonConditionShape(child, `${label}.any[${i}]`)
if (err) return err
}
return null
}
if (typeof condition.path !== 'string') return `${label}.path 必须是字符串`
if (typeof condition.path !== 'string') return `${label}.path ${legacyT('必须是字符串')}`
if (typeof condition.op !== 'string' || !CONDITION_JSON_OPS.has(condition.op)) {
return `${label}.op 无效`
return `${label}.op ${legacyT('无效')}`
}
if (condition.source !== undefined && (typeof condition.source !== 'string' || !CONDITION_JSON_SOURCES.has(condition.source))) {
return `${label}.source 无效`
return `${label}.source ${legacyT('无效')}`
}
return null
}
function requireJsonString(rule: Record<string, unknown>, key: string, label: string, index: number): string | null {
return typeof rule[key] === 'string' ? null : `${label}第 ${index + 1} 条:${key} 必须是字符串`
return typeof rule[key] === 'string' ? null : formatJsonRuleError(label, index, `${key} ${legacyT('必须是字符串')}`)
}
function normalizeHeaderRuleName(raw: string): string {
@@ -1548,12 +1566,12 @@ function normalizeHeaderRuleName(raw: string): string {
function reservedHeaderRuleError(raw: string): string | null {
const name = normalizeHeaderRuleName(raw)
return name && RESERVED_HEADERS.has(name) ? `"${raw}" 是系统保留的请求头` : null
return name && RESERVED_HEADERS.has(name) ? `"${raw}" ${legacyT('是系统保留的请求头')}` : null
}
function reservedResponseHeaderRuleError(raw: string): string | null {
const name = normalizeHeaderRuleName(raw)
return name && RESERVED_RESPONSE_HEADERS.has(name) ? `"${raw}" 是系统保留的响应头` : null
return name && RESERVED_RESPONSE_HEADERS.has(name) ? `"${raw}" ${legacyT('是系统保留的响应头')}` : null
}
function bodyRuleTopLevelField(rawPath: string): string | null {
@@ -1567,7 +1585,7 @@ function bodyRuleTopLevelField(rawPath: string): string | null {
function reservedBodyRuleFieldError(rawPath: string): string | null {
const topField = bodyRuleTopLevelField(rawPath)
return topField && RESERVED_BODY_FIELDS.has(topField)
? `"${topField}" 是系统保留的顶层字段`
? `"${topField}" ${legacyT('是系统保留的顶层字段')}`
: null
}
@@ -1577,13 +1595,13 @@ function validateHeaderRuleJson(
index: number,
reservedNameError: (raw: string) => string | null = reservedHeaderRuleError
): string | null {
if (!isJsonObject(rule)) return `${label}第 ${index + 1} 条必须是对象`
if (!isJsonObject(rule)) return formatJsonRuleError(label, index, '必须是对象')
if (rule.enabled !== undefined && typeof rule.enabled !== 'boolean') {
return `${label}第 ${index + 1} 条:enabled 必须是布尔值`
return formatJsonRuleError(label, index, 'enabled 必须是布尔值')
}
const action = rule.action
if (action !== 'set' && action !== 'drop' && action !== 'rename') {
return `${label}第 ${index + 1} 条:action 必须是 set/drop/rename`
return formatJsonRuleError(label, index, 'action 必须是 set/drop/rename')
}
if (action === 'set') {
return requireJsonString(rule, 'key', label, index)
@@ -1604,19 +1622,19 @@ function validateHeaderRuleJson(
}
function validateBodyRuleJson(rule: unknown, label: string, index: number): string | null {
if (!isJsonObject(rule)) return `${label}第 ${index + 1} 条必须是对象`
if (!isJsonObject(rule)) return formatJsonRuleError(label, index, '必须是对象')
if (rule.enabled !== undefined && typeof rule.enabled !== 'boolean') {
return `${label}第 ${index + 1} 条:enabled 必须是布尔值`
return formatJsonRuleError(label, index, 'enabled 必须是布尔值')
}
const action = typeof rule.action === 'string' ? rule.action : ''
if (!BODY_RULE_JSON_ACTIONS.has(action)) {
return `${label}第 ${index + 1} 条:action 无效`
return formatJsonRuleError(label, index, 'action 无效')
}
if (action === 'set' || action === 'append') {
return requireJsonString(rule, 'path', label, index)
|| reservedBodyRuleFieldError(rule.path as string)
|| (Object.prototype.hasOwnProperty.call(rule, 'value') ? null : `${label}第 ${index + 1} 条:value 不能为空`)
|| (Object.prototype.hasOwnProperty.call(rule, 'value') ? null : formatJsonRuleError(label, index, 'value 不能为空'))
|| validateJsonCondition(rule, label, index)
}
if (action === 'drop') {
@@ -1634,20 +1652,20 @@ function validateBodyRuleJson(rule: unknown, label: string, index: number): stri
if (action === 'insert') {
if (requireJsonString(rule, 'path', label, index)) return requireJsonString(rule, 'path', label, index)
if (reservedBodyRuleFieldError(rule.path as string)) return reservedBodyRuleFieldError(rule.path as string)
if (!Number.isInteger(rule.index)) return `${label}第 ${index + 1} 条:index 必须是整数`
if (!Object.prototype.hasOwnProperty.call(rule, 'value')) return `${label}第 ${index + 1} 条:value 不能为空`
if (!Number.isInteger(rule.index)) return formatJsonRuleError(label, index, 'index 必须是整数')
if (!Object.prototype.hasOwnProperty.call(rule, 'value')) return formatJsonRuleError(label, index, 'value 不能为空')
return validateJsonCondition(rule, label, index)
}
if (action === 'regex_replace') {
if (requireJsonString(rule, 'path', label, index)) return requireJsonString(rule, 'path', label, index)
if (reservedBodyRuleFieldError(rule.path as string)) return reservedBodyRuleFieldError(rule.path as string)
if (requireJsonString(rule, 'pattern', label, index)) return requireJsonString(rule, 'pattern', label, index)
if (typeof rule.replacement !== 'string') return `${label}第 ${index + 1} 条:replacement 必须是字符串`
if (rule.flags !== undefined && typeof rule.flags !== 'string') return `${label}第 ${index + 1} 条:flags 必须是字符串`
if (rule.count !== undefined && !Number.isInteger(rule.count)) return `${label}第 ${index + 1} 条:count 必须是整数`
if (typeof rule.replacement !== 'string') return formatJsonRuleError(label, index, 'replacement 必须是字符串')
if (rule.flags !== undefined && typeof rule.flags !== 'string') return formatJsonRuleError(label, index, 'flags 必须是字符串')
if (rule.count !== undefined && !Number.isInteger(rule.count)) return formatJsonRuleError(label, index, 'count 必须是整数')
return validateJsonCondition(rule, label, index)
}
return `${label}第 ${index + 1} 条:action 无效`
return formatJsonRuleError(label, index, 'action 无效')
}
function parseEndpointRulesJsonDraft(draft: string): { value: EndpointRulesJsonPayload | null; error: string | null } {
@@ -1660,9 +1678,9 @@ function parseEndpointRulesJsonDraft(draft: string): { value: EndpointRulesJsonP
try {
parsed = JSON.parse(raw)
} catch (error: unknown) {
return { value: null, error: error instanceof Error ? error.message : 'JSON 格式无效' }
return { value: null, error: error instanceof Error ? error.message : legacyT('JSON 格式无效') }
}
if (!isJsonObject(parsed)) return { value: null, error: '规则 JSON 必须是对象' }
if (!isJsonObject(parsed)) return { value: null, error: legacyT('规则 JSON 必须是对象') }
const header = readJsonRulesArray(parsed, 'header_rules', 'header_rules')
if (header.error) return { value: null, error: header.error }
@@ -1707,14 +1725,14 @@ function applyEndpointRulesJsonDraft(
const parsed = parseEndpointRulesJsonDraft(endpointRulesJsonDraft.value[endpointId] ?? '')
if (!parsed.value) {
endpointRulesJsonError.value[endpointId] = parsed.error
if (notifyError) showError(parsed.error || '规则 JSON 无效')
if (notifyError) showError(legacyT(parsed.error || '规则 JSON 无效'))
return false
}
const state = ensureEndpointEditState(endpointId)
if (!state) {
endpointRulesJsonError.value[endpointId] = '端点编辑状态不可用'
if (notifyError) showError('端点编辑状态不可用')
endpointRulesJsonError.value[endpointId] = legacyT('端点编辑状态不可用')
if (notifyError) showError(legacyT('端点编辑状态不可用'))
return false
}
@@ -1734,14 +1752,14 @@ function applyEndpointRulesJsonDraft(
|| getBodyValidationErrorForEndpoint(endpointId)
if (validationError) {
endpointRulesJsonError.value[endpointId] = validationError
if (notifyError) showError(validationError)
if (notifyError) showError(legacyT(validationError))
return false
}
endpointRulesJsonDraft.value[endpointId] = stringifyEndpointRulesJsonPayload(parsed.value)
endpointRulesJsonError.value[endpointId] = null
endpointRulesJsonDirty.value[endpointId] = false
if (options.notify !== false) success('JSON 规则已应用')
if (options.notify !== false) success(legacyT('JSON 规则已应用'))
return true
}
@@ -1878,7 +1896,9 @@ const availableFormats = computed(() => {
const deleteConfirmDescription = computed(() => {
if (!endpointToDelete.value) return ''
const formatLabel = formatApiFormat(endpointToDelete.value.api_format)
return `确定要删除 ${formatLabel} 端点吗?关联密钥将移除对该 API 格式的支持。`
return locale.value === 'en-US'
? `Delete the ${formatLabel} endpoint? Linked keys will no longer support this API format.`
: `确定要删除 ${formatLabel} 端点吗?关联密钥将移除对该 API 格式的支持。`
})
function defaultBodyRulesCacheKey(apiFormat: string): string {
@@ -1985,14 +2005,14 @@ function endpointProxyNodeId(endpoint: ProviderEndpoint): string {
function getEndpointProxyNodeName(endpoint: ProviderEndpoint): string {
const nodeId = endpointProxyNodeId(endpoint)
if (!nodeId) return '未知节点'
if (!nodeId) return legacyT('未知节点')
const node = proxyNodesStore.nodes.find(n => n.id === nodeId)
return node ? node.name : `${nodeId.slice(0, 8)}...`
}
function getEndpointProxyTitle(endpoint: ProviderEndpoint): string {
const nodeId = endpointProxyNodeId(endpoint)
return nodeId ? `端点代理: ${getEndpointProxyNodeName(endpoint)}` : '设置端点代理节点'
return nodeId ? `${legacyT('端点代理')}: ${getEndpointProxyNodeName(endpoint)}` : legacyT('设置端点代理节点')
}
function handleEndpointProxyPopoverToggle(endpointId: string, open: boolean) {
@@ -2019,10 +2039,10 @@ async function setEndpointProxy(endpoint: ProviderEndpoint, nodeId: string) {
})
replaceLocalEndpoint(updated)
endpointProxyPopoverOpen.value[endpoint.id] = false
success('端点代理已更新')
success(legacyT('端点代理已更新'))
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '更新代理失败'), '错误')
showError(localizedApiError(error, '更新代理失败'), legacyT('错误'))
} finally {
savingEndpointId.value = null
}
@@ -2034,10 +2054,10 @@ async function clearEndpointProxy(endpoint: ProviderEndpoint) {
const updated = await updateEndpoint(endpoint.id, { proxy: null })
replaceLocalEndpoint(updated)
endpointProxyPopoverOpen.value[endpoint.id] = false
success('端点代理已清除')
success(legacyT('端点代理已清除'))
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '清除代理失败'), '错误')
showError(localizedApiError(error, '清除代理失败'), legacyT('错误'))
} finally {
savingEndpointId.value = null
}
@@ -2399,7 +2419,7 @@ function validateRuleKeyForEndpoint(endpointId: string, key: string, index: numb
)
)
if (duplicate >= 0) {
return '请求头名称重复'
return legacyT('请求头名称重复')
}
return null
@@ -2423,7 +2443,7 @@ function validateRenameFromForEndpoint(endpointId: string, from: string, index:
(r.action === 'rename' && r.from.trim().toLowerCase() === trimmedFrom))
)
if (duplicate >= 0) {
return '该请求头已被其他规则处理'
return legacyT('该请求头已被其他规则处理')
}
return null
@@ -2446,7 +2466,7 @@ function validateRenameToForEndpoint(endpointId: string, to: string, index: numb
(r.action === 'rename' && r.to.trim().toLowerCase() === trimmedTo))
)
if (duplicate >= 0) {
return '请求头名称重复'
return legacyT('请求头名称重复')
}
return null
@@ -2575,7 +2595,7 @@ function validateBodyRulePathForEndpoint(endpointId: string, path: string, index
const dotPart = raw.includes('[') ? raw.slice(0, raw.indexOf('[')) : raw
const parts = dotPart ? parseBodyRulePathParts(dotPart) : [raw.split('[')[0] || raw]
if (!parts) {
return '路径格式无效'
return legacyT('路径格式无效')
}
const reservedErr = reservedBodyRuleFieldError(raw)
@@ -2594,7 +2614,7 @@ function validateBodyRulePathForEndpoint(endpointId: string, path: string, index
)
)
if (duplicate >= 0) {
return '字段路径重复'
return legacyT('字段路径重复')
}
return null
@@ -2607,7 +2627,7 @@ function validateBodyRenameFromForEndpoint(endpointId: string, from: string, ind
const parts = parseBodyRulePathParts(raw)
if (!parts) {
return '路径格式无效(不允许 .a / a. / a..b)'
return legacyT('路径格式无效(不允许 .a / a. / a..b)')
}
const reservedErr = reservedBodyRuleFieldError(raw)
@@ -2625,7 +2645,7 @@ function validateBodyRenameFromForEndpoint(endpointId: string, from: string, ind
(r.action === 'rename' && r.from.trim().toLowerCase() === normalizedFrom))
)
if (duplicate >= 0) {
return '该路径已被其他规则处理'
return legacyT('该路径已被其他规则处理')
}
return null
@@ -2638,7 +2658,7 @@ function validateBodyRenameToForEndpoint(endpointId: string, to: string, index:
const parts = parseBodyRulePathParts(raw)
if (!parts) {
return '路径格式无效(不允许 .a / a. / a..b)'
return legacyT('路径格式无效(不允许 .a / a. / a..b)')
}
const reservedErr = reservedBodyRuleFieldError(raw)
@@ -2655,7 +2675,7 @@ function validateBodyRenameToForEndpoint(endpointId: string, to: string, index:
(r.action === 'rename' && r.to.trim().toLowerCase() === normalizedTo))
)
if (duplicate >= 0) {
return '字段路径重复'
return legacyT('字段路径重复')
}
return null
@@ -2665,12 +2685,12 @@ function validateBodySetValue(rule: EditableBodyRule): string | null {
if (rule.action !== 'set' && rule.action !== 'append' && rule.action !== 'insert') return null
const raw = rule.value.trim()
if (!raw) return '值不能为空'
if (!raw) return legacyT('值不能为空')
try {
JSON.parse(prepareValueForJsonParse(raw))
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err)
return `JSON 格式错误:${msg}`
return locale.value === 'en-US' ? `JSON format error: ${msg}` : `JSON 格式错误:${msg}`
}
return null
}
@@ -2712,12 +2732,12 @@ function getRegexPatternValidation(rule: EditableBodyRule): boolean | null {
// 获取正则验证提示
function getRegexPatternValidationTip(rule: EditableBodyRule): string {
const validation = getRegexPatternValidation(rule)
if (validation === null) return '输入正则表达式'
if (validation === true) return '有效的正则表达式'
if (validation === null) return legacyT('输入正则表达式')
if (validation === true) return legacyT('有效的正则表达式')
try {
new RegExp(rule.pattern.trim())
// 正则有效但 flags 无效
return '无效的 flags(仅允许 i/m/s)'
return legacyT('无效的 flags(仅允许 i/m/s)')
} catch (err: unknown) {
return err instanceof Error ? err.message : String(err)
}
@@ -2726,11 +2746,21 @@ function getRegexPatternValidationTip(rule: EditableBodyRule): string {
// 获取验证提示
function getBodySetValueValidationTip(rule: EditableBodyRule): string {
const validation = getBodySetValueValidation(rule)
if (validation === null) return '点击验证 JSON'
if (validation === null) return legacyT('点击验证 JSON')
if (validation === true) {
const parsed = restoreOriginalPlaceholder(JSON.parse(prepareValueForJsonParse(rule.value.trim())))
const type = Array.isArray(parsed) ? '数组' : typeof parsed === 'object' && parsed !== null ? '对象' : typeof parsed === 'string' ? '字符串' : typeof parsed === 'number' ? '数字' : typeof parsed === 'boolean' ? '布尔' : 'null'
return `有效的 JSON (${type})`
const type = Array.isArray(parsed)
? legacyT('数组')
: typeof parsed === 'object' && parsed !== null
? legacyT('对象')
: typeof parsed === 'string'
? legacyT('字符串')
: typeof parsed === 'number'
? legacyT('数字')
: typeof parsed === 'boolean'
? legacyT('布尔')
: 'null'
return `${legacyT('有效的 JSON')} (${type})`
}
try {
JSON.parse(prepareValueForJsonParse(rule.value.trim()))
@@ -2793,43 +2823,43 @@ function getTotalRulesCount(endpoint: ProviderEndpoint): number {
// 格式化请求头规则的显示标签
function _formatHeaderRuleLabel(rule: EditableRule): string {
if (rule.action === 'set') {
if (!rule.key) return '(未设置)'
if (!rule.key) return legacyT('(未设置)')
return `${rule.key}=${rule.value || '...'}`
} else if (rule.action === 'drop') {
if (!rule.key) return '(未设置)'
if (!rule.key) return legacyT('(未设置)')
return `-${rule.key}`
} else if (rule.action === 'rename') {
if (!rule.from || !rule.to) return '(未设置)'
if (!rule.from || !rule.to) return legacyT('(未设置)')
return `${rule.from}→${rule.to}`
}
return '(未知)'
return legacyT('(未知)')
}
// 格式化请求体规则的显示标签
function _formatBodyRuleLabel(rule: EditableBodyRule): string {
if (rule.action === 'set') {
if (!rule.path) return '(未设置)'
if (!rule.path) return legacyT('(未设置)')
return `${rule.path}=${rule.value || '...'}`
} else if (rule.action === 'drop') {
if (!rule.path) return '(未设置)'
if (!rule.path) return legacyT('(未设置)')
return `-${rule.path}`
} else if (rule.action === 'rename') {
if (!rule.from || !rule.to) return '(未设置)'
if (!rule.from || !rule.to) return legacyT('(未设置)')
return `${rule.from}→${rule.to}`
} else if (rule.action === 'append') {
if (!rule.path) return '(未设置)'
if (!rule.path) return legacyT('(未设置)')
return `${rule.path}[]+=${rule.value || '...'}`
} else if (rule.action === 'insert') {
if (!rule.path) return '(未设置)'
const idx = rule.index?.trim() || '末尾'
if (!rule.path) return legacyT('(未设置)')
const idx = rule.index?.trim() || legacyT('末尾')
return `${rule.path}[${idx}]+=${rule.value || '...'}`
} else if (rule.action === 'regex_replace') {
if (!rule.path || !rule.pattern) return '(未设置)'
if (!rule.path || !rule.pattern) return legacyT('(未设置)')
const flags = rule.flags.trim()
const count = rule.count.trim()
return `${rule.path}: s/${rule.pattern}/${rule.replacement || ''}/${flags}${count ? ` ×${count}` : ''}`
}
return '(未知)'
return legacyT('(未知)')
}
// 检查端点请求体规则是否有修改
@@ -2921,7 +2951,7 @@ function getBodyValidationErrorForEndpoint(endpointId: string): string | null {
for (let i = 0; i < rules.length; i++) {
const rule = rules[i]
if (!rule.enabled) continue
const prefix = `第 ${i + 1} 条请求体规则:`
const prefix = locale.value === 'en-US' ? `Body rule ${i + 1}: ` : `第 ${i + 1} 条请求体规则:`
if (rule.action === 'set' || rule.action === 'drop') {
const pathErr = validateBodyRulePathForEndpoint(endpointId, rule.path, i)
@@ -2944,29 +2974,32 @@ function getBodyValidationErrorForEndpoint(endpointId: string): string | null {
const pathErr = validateBodyRulePathForEndpoint(endpointId, rule.path, i)
if (pathErr) return `${prefix}${pathErr}`
const indexStr = rule.index.trim()
if (!indexStr) return `${prefix}插入位置不能为空`
if (!isStrictIntegerString(indexStr)) return `${prefix}位置必须为整数`
if (!indexStr) return `${prefix}${legacyT('插入位置不能为空')}`
if (!isStrictIntegerString(indexStr)) return `${prefix}${legacyT('位置必须为整数')}`
const valueErr = validateBodySetValue(rule)
if (valueErr) return `${prefix}${valueErr}`
} else if (rule.action === 'regex_replace') {
const pathErr = validateBodyRulePathForEndpoint(endpointId, rule.path, i)
if (pathErr) return `${prefix}${pathErr}`
if (!rule.pattern.trim()) return `${prefix}正则表达式不能为空`
if (!rule.pattern.trim()) return `${prefix}${legacyT('正则表达式不能为空')}`
try {
new RegExp(rule.pattern.trim())
} catch (err: unknown) {
return `${prefix}正则表达式无效:${err instanceof Error ? err.message : String(err)}`
const message = err instanceof Error ? err.message : String(err)
return locale.value === 'en-US'
? `${prefix}Invalid regular expression: ${message}`
: `${prefix}正则表达式无效:${message}`
}
const flags = rule.flags.trim()
if (flags) {
const validFlags = new Set(['i', 'm', 's'])
for (const f of flags) {
if (!validFlags.has(f)) return `${prefix}flags 仅允许 i/m/s,非法字符: ${f}`
if (!validFlags.has(f)) return `${prefix}${legacyT('flags 仅允许 i/m/s,非法字符')}: ${f}`
}
}
const count = rule.count.trim()
if (count) {
if (!isStrictNonNegativeIntegerString(count)) return `${prefix}替换次数必须是大于等于 0 的整数`
if (!isStrictNonNegativeIntegerString(count)) return `${prefix}${legacyT('替换次数必须是大于等于 0 的整数')}`
}
}
@@ -3058,7 +3091,7 @@ async function handleResetBodyRulesToDefault(endpoint: ProviderEndpoint) {
try {
const defaultRules = await loadDefaultBodyRulesForFormat(endpoint.api_format, true)
if (!defaultRules.length) {
showError('该端点没有默认请求体规则')
showError(legacyT('该端点没有默认请求体规则'))
return
}
@@ -3079,9 +3112,9 @@ async function handleResetBodyRulesToDefault(endpoint: ProviderEndpoint) {
if (isEndpointRulesJsonMode(endpoint.id)) {
refreshEndpointRulesJsonDraft(endpoint.id)
}
success('已重置请求体为默认规则,请点击保存生效')
success(legacyT('已重置请求体为默认规则,请点击保存生效'))
} catch (error: unknown) {
showError(parseApiError(error, '重置失败'), '错误')
showError(localizedApiError(error, '重置失败'), legacyT('错误'))
} finally {
resettingDefaultRulesEndpointId.value = null
}
@@ -3121,7 +3154,7 @@ function getHeaderValidationErrorForEndpoint(endpointId: string): string | null
for (let i = 0; i < rules.length; i++) {
const rule = rules[i]
if (!rule.enabled) continue
const prefix = `第 ${i + 1} 条请求头规则:`
const prefix = locale.value === 'en-US' ? `Header rule ${i + 1}: ` : `第 ${i + 1} 条请求头规则:`
if (rule.action === 'set' || rule.action === 'drop') {
const err = validateRuleKeyForEndpoint(endpointId, rule.key, i)
if (err) return `${prefix}${err}`
@@ -3156,7 +3189,7 @@ function validateResponseHeaderNameForEndpoint(endpointId: string, name: string,
)
)
if (duplicate >= 0) {
return field === 'from' ? '该响应头已被其他规则处理' : '响应头名称重复'
return legacyT(field === 'from' ? '该响应头已被其他规则处理' : '响应头名称重复')
}
return null
@@ -3167,7 +3200,7 @@ function getResponseHeaderValidationErrorForEndpoint(endpointId: string): string
for (let i = 0; i < rules.length; i++) {
const rule = rules[i]
if (!rule.enabled) continue
const prefix = `第 ${i + 1} 条响应头规则:`
const prefix = locale.value === 'en-US' ? `Response header rule ${i + 1}: ` : `第 ${i + 1} 条响应头规则:`
if (rule.action === 'set' || rule.action === 'drop') {
const err = validateResponseHeaderNameForEndpoint(endpointId, rule.key, i, 'key')
if (err) return `${prefix}${err}`
@@ -3274,20 +3307,20 @@ async function saveEndpoint(endpoint: ProviderEndpoint) {
// 检查规则是否有验证错误
const headerErr = getHeaderValidationErrorForEndpoint(endpoint.id)
if (headerErr) {
showError(headerErr)
showError(legacyT(headerErr))
return
}
const responseHeaderErr = getResponseHeaderValidationErrorForEndpoint(endpoint.id)
if (responseHeaderErr) {
showError(responseHeaderErr)
showError(legacyT(responseHeaderErr))
return
}
// 检查请求体规则是否有验证错误
const bodyErr = getBodyValidationErrorForEndpoint(endpoint.id)
if (bodyErr) {
showError(bodyErr)
showError(legacyT(bodyErr))
return
}
@@ -3315,10 +3348,10 @@ async function saveEndpoint(endpoint: ProviderEndpoint) {
if (Object.keys(payload).length === 0) return
await updateEndpoint(endpoint.id, payload)
success('端点已更新')
success(legacyT('端点已更新'))
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '更新失败'), '错误')
showError(localizedApiError(error, '更新失败'), legacyT('错误'))
} finally {
savingEndpointId.value = null
}
@@ -3334,10 +3367,10 @@ async function handleToggleFormatConversion(endpoint: ProviderEndpoint) {
await updateEndpoint(endpoint.id, {
format_acceptance_config: newEnabled ? { enabled: true } : null,
})
success(newEnabled ? '已启用格式转换' : '已关闭格式转换')
success(legacyT(newEnabled ? '已启用格式转换' : '已关闭格式转换'))
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '操作失败'), '错误')
showError(localizedApiError(error, '操作失败'), legacyT('错误'))
} finally {
togglingFormatEndpointId.value = null
}
@@ -3369,11 +3402,11 @@ function getUpstreamStreamButtonClass(endpoint: ProviderEndpoint): string {
// 获取上游流式按钮的提示文字
function getUpstreamStreamTooltip(endpoint: ProviderEndpoint): string {
if (isUpstreamStreamPolicyLocked(endpoint)) return '固定流式(Codex OpenAI Responses,已锁定)'
if (isUpstreamStreamPolicyLocked(endpoint)) return legacyT('固定流式(Codex OpenAI Responses,已锁定)')
const policy = getCurrentUpstreamStreamPolicy(endpoint)
if (policy === 'force_stream') return '固定流式(点击切换为固定非流)'
if (policy === 'force_non_stream') return '固定非流(点击切换为跟随请求)'
return '跟随请求(点击切换为固定流式)'
if (policy === 'force_stream') return legacyT('固定流式(点击切换为固定非流)')
if (policy === 'force_non_stream') return legacyT('固定非流(点击切换为跟随请求)')
return legacyT('跟随请求(点击切换为固定流式)')
}
// 循环切换上游流式策略并直接保存
@@ -3387,13 +3420,13 @@ async function handleCycleUpstreamStream(endpoint: ProviderEndpoint) {
// 循环:auto -> force_stream -> force_non_stream -> auto
if (currentPolicy === 'auto') {
nextPolicy = 'force_stream'
nextLabel = '固定流式'
nextLabel = legacyT('固定流式')
} else if (currentPolicy === 'force_stream') {
nextPolicy = 'force_non_stream'
nextLabel = '固定非流'
nextLabel = legacyT('固定非流')
} else {
nextPolicy = 'auto'
nextLabel = '跟随请求'
nextLabel = legacyT('跟随请求')
}
savingEndpointId.value = endpoint.id
@@ -3417,10 +3450,10 @@ async function handleCycleUpstreamStream(endpoint: ProviderEndpoint) {
endpointEditStates.value[endpoint.id].upstreamStreamPolicy = nextPolicy
}
success(`已切换为${nextLabel}`)
success(locale.value === 'en-US' ? `Switched to ${nextLabel}` : `已切换为${nextLabel}`)
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '操作失败'), '错误')
showError(localizedApiError(error, '操作失败'), legacyT('错误'))
} finally {
savingEndpointId.value = null
}
@@ -3433,7 +3466,7 @@ async function handleAddEndpoint() {
// 如果没有输入 base_url,使用按格式规范化后的提供商 website 作为默认值。
const baseUrl = getNewEndpointBaseUrl()
if (!baseUrl) {
showError('请输入 Base URL')
showError(legacyT('请输入 Base URL'))
return
}
@@ -3446,12 +3479,14 @@ async function handleAddEndpoint() {
custom_path: newEndpoint.value.custom_path || undefined,
is_active: true,
})
success(`已添加 ${formatApiFormat(newEndpoint.value.api_format)} 端点`)
success(locale.value === 'en-US'
? `Added ${formatApiFormat(newEndpoint.value.api_format)} endpoint`
: `已添加 ${formatApiFormat(newEndpoint.value.api_format)} 端点`)
// 重置表单,保留 URL
newEndpoint.value = { api_format: '', base_url: baseUrl, custom_path: '' }
emit('endpointCreated')
} catch (error: unknown) {
showError(parseApiError(error, '添加失败'), '错误')
showError(localizedApiError(error, '添加失败'), legacyT('错误'))
} finally {
addingEndpoint.value = false
}
@@ -3463,10 +3498,10 @@ async function handleToggleEndpoint(endpoint: ProviderEndpoint) {
try {
const newStatus = !endpoint.is_active
await updateEndpoint(endpoint.id, { is_active: newStatus })
success(newStatus ? '端点已启用' : '端点已停用')
success(legacyT(newStatus ? '端点已启用' : '端点已停用'))
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '操作失败'), '错误')
showError(localizedApiError(error, '操作失败'), legacyT('错误'))
} finally {
togglingEndpointId.value = null
}
@@ -3488,10 +3523,12 @@ async function confirmDeleteEndpoint() {
try {
await deleteEndpoint(endpoint.id)
success(`已删除 ${formatApiFormat(endpoint.api_format)} 端点`)
success(locale.value === 'en-US'
? `Deleted ${formatApiFormat(endpoint.api_format)} endpoint`
: `已删除 ${formatApiFormat(endpoint.api_format)} 端点`)
emit('endpointUpdated')
} catch (error: unknown) {
showError(parseApiError(error, '删除失败'), '错误')
showError(localizedApiError(error, '删除失败'), legacyT('错误'))
} finally {
deletingEndpointId.value = null
endpointToDelete.value = null