fix: localize English UI strings

This commit is contained in:
Heatherm Huang committed 2026-06-30 20:05:23 +08:00
1 parent d3acd8e96e
commit 6e54776509
24 files changed
+322 -196

No files matched your search

@@ -889,7 +889,7 @@ function handleToolbarRefresh() {
<template v-if="props.autoRefreshEnabled && props.autoRefreshCountdown !== undefined">
<span>·</span>
<span>剩余 {{ props.autoRefreshCountdown }}s</span>
<span>{{ t('admin.ops.autoRefreshRemaining', { seconds: props.autoRefreshCountdown }) }}</span>
</template>
</div>
</div>
@@ -82,7 +82,7 @@ const timeRangeOptions = [
]
const filterLevelOptions = [
{ value: '', label: '全部' },
{ value: '', label: 'All' },
{ value: 'debug', label: 'debug' },
{ value: 'info', label: 'info' },
{ value: 'warn', label: 'warn' },
@@ -203,7 +203,7 @@ const fetchLogs = async () => {
total.value = res.total || 0
} catch (err: any) {
console.error('[OpsSystemLogTable] Failed to fetch logs', err)
appStore.showError(err?.response?.data?.detail || '系统日志加载失败')
appStore.showError(err?.response?.data?.detail || 'Failed to load system logs')
} finally {
loading.value = false
}
@@ -246,17 +246,17 @@ const saveRuntimeConfig = async () => {
runtimeConfig.caller = saved.caller
runtimeConfig.stacktrace_level = saved.stacktrace_level
runtimeConfig.retention_days = saved.retention_days
appStore.showSuccess('日志运行时配置已生效')
appStore.showSuccess('Runtime log configuration is active')
} catch (err: any) {
console.error('[OpsSystemLogTable] Failed to save runtime log config', err)
appStore.showError(err?.response?.data?.detail || '保存日志配置失败')
appStore.showError(err?.response?.data?.detail || 'Failed to save log configuration')
} finally {
runtimeSaving.value = false
}
}
const resetRuntimeConfig = async () => {
const ok = window.confirm('确认回滚为启动配置(env/yaml)并立即生效?')
const ok = window.confirm('Reset to startup configuration (env/yaml) and apply immediately?')
if (!ok) return
runtimeSaving.value = true
@@ -269,18 +269,18 @@ const resetRuntimeConfig = async () => {
runtimeConfig.caller = saved.caller
runtimeConfig.stacktrace_level = saved.stacktrace_level
runtimeConfig.retention_days = saved.retention_days
appStore.showSuccess('已回滚到启动日志配置')
appStore.showSuccess('Reset to startup log configuration')
await fetchHealth()
} catch (err: any) {
console.error('[OpsSystemLogTable] Failed to reset runtime log config', err)
appStore.showError(err?.response?.data?.detail || '回滚日志配置失败')
appStore.showError(err?.response?.data?.detail || 'Failed to reset log configuration')
} finally {
runtimeSaving.value = false
}
}
const cleanupCurrentFilter = async () => {
const ok = window.confirm('确认按当前筛选条件清理系统日志?该操作不可撤销。')
const ok = window.confirm('Clean up system logs matching the current filters? This cannot be undone.')
if (!ok) return
try {
const payload = {
@@ -298,12 +298,12 @@ const cleanupCurrentFilter = async () => {
q: filters.q.trim() || undefined
}
const res = await opsAPI.cleanupSystemLogs(payload)
appStore.showSuccess(`清理完成,删除 ${res.deleted || 0} 条日志`)
appStore.showSuccess(`Cleanup complete. Deleted ${res.deleted || 0} log entries.`)
page.value = 1
await Promise.all([fetchLogs(), fetchHealth()])
} catch (err: any) {
console.error('[OpsSystemLogTable] Failed to cleanup logs', err)
appStore.showError(err?.response?.data?.detail || '清理系统日志失败')
appStore.showError(err?.response?.data?.detail || 'Failed to clean up system logs')
}
}
@@ -368,41 +368,41 @@ onMounted(async () => {
<section class="rounded-2xl border border-gray-200 bg-white p-4 shadow-sm dark:border-dark-700 dark:bg-dark-900/60">
<div class="mb-4 flex flex-wrap items-center justify-between gap-3">
<div>
<h3 class="text-sm font-bold text-gray-900 dark:text-white">系统日志</h3>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">默认按最新时间倒序,支持筛选搜索与按条件清理。</p>
<h3 class="text-sm font-bold text-gray-900 dark:text-white">System Logs</h3>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Newest logs are shown first. Filter, search, and clean up by condition.</p>
</div>
<div class="flex flex-wrap items-center gap-2 text-xs">
<span class="rounded-md bg-gray-100 px-2 py-1 text-gray-700 dark:bg-dark-700 dark:text-gray-200">队列 {{ health.queue_depth }}/{{ health.queue_capacity }}</span>
<span class="rounded-md bg-gray-100 px-2 py-1 text-gray-700 dark:bg-dark-700 dark:text-gray-200">写入 {{ health.written_count }}</span>
<span class="rounded-md bg-amber-100 px-2 py-1 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300">丢弃 {{ health.dropped_count }}</span>
<span class="rounded-md bg-red-100 px-2 py-1 text-red-700 dark:bg-red-900/30 dark:text-red-300">失败 {{ health.write_failed_count }}</span>
<span class="rounded-md bg-gray-100 px-2 py-1 text-gray-700 dark:bg-dark-700 dark:text-gray-200">Queue {{ health.queue_depth }}/{{ health.queue_capacity }}</span>
<span class="rounded-md bg-gray-100 px-2 py-1 text-gray-700 dark:bg-dark-700 dark:text-gray-200">Written {{ health.written_count }}</span>
<span class="rounded-md bg-amber-100 px-2 py-1 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300">Dropped {{ health.dropped_count }}</span>
<span class="rounded-md bg-red-100 px-2 py-1 text-red-700 dark:bg-red-900/30 dark:text-red-300">Failed {{ health.write_failed_count }}</span>
</div>
</div>
<div class="mb-4 rounded-xl border border-gray-200 bg-gray-50 p-3 dark:border-dark-700 dark:bg-dark-800/70">
<div class="mb-2 flex items-center justify-between">
<div class="text-xs font-semibold text-gray-700 dark:text-gray-200">运行时日志配置(实时生效)</div>
<span v-if="runtimeLoading" class="text-xs text-gray-500">加载中...</span>
<div class="text-xs font-semibold text-gray-700 dark:text-gray-200">Runtime Log Configuration (applies immediately)</div>
<span v-if="runtimeLoading" class="text-xs text-gray-500">Loading...</span>
</div>
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-6">
<label class="text-xs text-gray-600 dark:text-gray-300">
级别
Level
<Select v-model="runtimeConfig.level" class="mt-1" :options="runtimeLevelOptions" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
堆栈阈值
Stacktrace threshold
<Select v-model="runtimeConfig.stacktrace_level" class="mt-1" :options="stacktraceLevelOptions" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
采样初始
Sampling initial
<input v-model.number="runtimeConfig.sampling_initial" type="number" min="1" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
采样后续
Sampling thereafter
<input v-model.number="runtimeConfig.sampling_thereafter" type="number" min="1" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
保留天数
Retention days
<input v-model.number="runtimeConfig.retention_days" type="number" min="1" max="3650" class="input mt-1" />
</label>
<div class="md:col-span-2 xl:col-span-6">
@@ -419,38 +419,38 @@ onMounted(async () => {
</div>
<div class="flex flex-wrap items-center gap-2 lg:justify-end">
<button type="button" class="btn btn-primary btn-sm" :disabled="runtimeSaving" @click="saveRuntimeConfig">
{{ runtimeSaving ? '保存中...' : '保存并生效' }}
{{ runtimeSaving ? 'Saving...' : 'Save and apply' }}
</button>
<button type="button" class="btn btn-secondary btn-sm" :disabled="runtimeSaving" @click="resetRuntimeConfig">
回滚默认值
Reset defaults
</button>
</div>
</div>
</div>
</div>
<p v-if="health.last_error" class="mt-2 text-xs text-red-600 dark:text-red-400">最近写入错误:{{ health.last_error }}</p>
<p v-if="health.last_error" class="mt-2 text-xs text-red-600 dark:text-red-400">Latest write error: {{ health.last_error }}</p>
</div>
<div class="mb-4 grid grid-cols-1 gap-3 md:grid-cols-5">
<label class="text-xs text-gray-600 dark:text-gray-300">
时间范围
Time range
<Select v-model="filters.time_range" class="mt-1" :options="timeRangeOptions" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
开始时间(可选)
Start time (optional)
<input v-model="filters.start_time" type="datetime-local" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
结束时间(可选)
End time (optional)
<input v-model="filters.end_time" type="datetime-local" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
级别
Level
<Select v-model="filters.level" class="mt-1" :options="filterLevelOptions" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
组件
<input v-model="filters.component" type="text" class="input mt-1" placeholder="如 http.access" />
Component
<input v-model="filters.component" type="text" class="input mt-1" placeholder="e.g. http.access" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
request_id
@@ -473,36 +473,36 @@ onMounted(async () => {
<input v-model="filters.account_id" type="text" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
平台
Platform
<input v-model="filters.platform" type="text" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
模型
Model
<input v-model="filters.model" type="text" class="input mt-1" />
</label>
<label class="text-xs text-gray-600 dark:text-gray-300">
关键词
<input v-model="filters.q" type="text" class="input mt-1" placeholder="消息/request_id" />
Keyword
<input v-model="filters.q" type="text" class="input mt-1" placeholder="message/request_id" />
</label>
</div>
<div class="mb-3 flex flex-wrap gap-2">
<button type="button" class="btn btn-primary btn-sm" @click="applyFilters">查询</button>
<button type="button" class="btn btn-secondary btn-sm" @click="resetFilters">重置</button>
<button type="button" class="btn btn-danger btn-sm" @click="cleanupCurrentFilter">按当前筛选清理</button>
<button type="button" class="btn btn-secondary btn-sm" @click="fetchHealth">刷新健康指标</button>
<button type="button" class="btn btn-primary btn-sm" @click="applyFilters">Search</button>
<button type="button" class="btn btn-secondary btn-sm" @click="resetFilters">Reset</button>
<button type="button" class="btn btn-danger btn-sm" @click="cleanupCurrentFilter">Clean current filters</button>
<button type="button" class="btn btn-secondary btn-sm" @click="fetchHealth">Refresh health</button>
</div>
<div class="overflow-hidden rounded-xl border border-gray-200 dark:border-dark-700">
<div v-if="loading" class="px-4 py-8 text-center text-sm text-gray-500">加载中...</div>
<div v-else-if="!hasData" class="px-4 py-8 text-center text-sm text-gray-500">暂无系统日志</div>
<div v-if="loading" class="px-4 py-8 text-center text-sm text-gray-500">Loading...</div>
<div v-else-if="!hasData" class="px-4 py-8 text-center text-sm text-gray-500">No system logs</div>
<div v-else class="overflow-auto">
<table class="min-w-full table-fixed divide-y divide-gray-200 dark:divide-dark-700">
<thead class="bg-gray-50 dark:bg-dark-900">
<tr>
<th class="w-[170px] px-3 py-2 text-left text-[11px] font-semibold text-gray-500">时间</th>
<th class="w-[80px] px-3 py-2 text-left text-[11px] font-semibold text-gray-500">级别</th>
<th class="px-3 py-2 text-left text-[11px] font-semibold text-gray-500">日志详细信息</th>
<th class="w-[170px] px-3 py-2 text-left text-[11px] font-semibold text-gray-500">Time</th>
<th class="w-[80px] px-3 py-2 text-left text-[11px] font-semibold text-gray-500">Level</th>
<th class="px-3 py-2 text-left text-[11px] font-semibold text-gray-500">Log Details</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-dark-800">