feat(i18n): localize Redis dashboard

This commit is contained in:
二丫讲梵
2026-07-20 01:23:59 +08:00
committed by GitHub
parent b43be528e1
commit 67df8be6d9
8 changed files with 358 additions and 14 deletions
@@ -48,6 +48,22 @@ const searchQuery = ref("");
const collapsedSections = ref<Set<string>>(new Set());
const autoRefreshInterval = ref<number>(0); // 0 = off, 5, 10, 30, 60 (seconds)
const infoSectionI18nKeys: Record<string, string> = {
Server: "redis.dashboard.sections.server",
Clients: "redis.dashboard.sections.clients",
Memory: "redis.dashboard.sections.memory",
Persistence: "redis.dashboard.sections.persistence",
Stats: "redis.dashboard.sections.stats",
Replication: "redis.dashboard.sections.replication",
CPU: "redis.dashboard.sections.cpu",
Modules: "redis.dashboard.sections.modules",
Commandstats: "redis.dashboard.sections.commandstats",
Errorstats: "redis.dashboard.sections.errorstats",
Latencystats: "redis.dashboard.sections.latencystats",
Cluster: "redis.dashboard.sections.cluster",
Keyspace: "redis.dashboard.sections.keyspace",
};
let refreshTimer: ReturnType<typeof setInterval> | null = null;
// ---------------------------------------------------------------------------
@@ -228,6 +244,11 @@ function isCollapsed(name: string): boolean {
return collapsedSections.value.has(name);
}
function infoSectionTitle(name: string): string {
const key = infoSectionI18nKeys[name];
return key ? t(key) : name;
}
// ---------------------------------------------------------------------------
// Auto-refresh
// ---------------------------------------------------------------------------
@@ -286,7 +307,7 @@ function copyToClipboard(text: string) {
navigator.clipboard
.writeText(text)
.then(() => {
toast("Copied", 1500);
toast(t("redis.copied"), 1500);
})
.catch(() => {
// Fallback for environments without clipboard API
@@ -318,7 +339,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 min-w-0">
<div v-if="showNodeSelector && nodeOptions.length > 0" class="flex items-center gap-2">
<span class="text-xs text-muted-foreground whitespace-nowrap">Node:</span>
<span class="text-xs text-muted-foreground whitespace-nowrap">{{ t("redis.dashboard.node") }}:</span>
<Select v-model="selectedNodeIndex" @update:model-value="fetchInfo">
<SelectTrigger class="h-7 w-auto min-w-[140px] text-xs">
<SelectValue />
@@ -339,7 +360,7 @@ function copyEntryValue(entry: InfoEntry) {
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem :value="0" class="text-xs">Off</SelectItem>
<SelectItem :value="0" class="text-xs">{{ t("redis.dashboard.off") }}</SelectItem>
<SelectItem :value="1" class="text-xs">1s</SelectItem>
<SelectItem :value="5" class="text-xs">5s</SelectItem>
<SelectItem :value="15" class="text-xs">15s</SelectItem>
@@ -354,7 +375,7 @@ function copyEntryValue(entry: InfoEntry) {
<RefreshCw v-else class="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent>Refresh</TooltipContent>
<TooltipContent>{{ t("redis.dashboard.refresh") }}</TooltipContent>
</Tooltip>
</div>
</div>
@@ -374,7 +395,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<HardDrive class="h-3.5 w-3.5" />
<span>Total Memory</span>
<span>{{ t("redis.dashboard.totalMemory") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="totalMemory">
{{ totalMemory }}
@@ -384,7 +405,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<Activity class="h-3.5 w-3.5" />
<span>Used Memory</span>
<span>{{ t("redis.dashboard.usedMemory") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="usedMemory">
{{ usedMemory }}
@@ -394,7 +415,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<Terminal class="h-3.5 w-3.5" />
<span>Operations</span>
<span>{{ t("redis.dashboard.operations") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="operation">
{{ operation }}
@@ -404,7 +425,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<Target class="h-3.5 w-3.5" />
<span>Hit Ratio</span>
<span>{{ t("redis.dashboard.hitRatio") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="hitRatio">
{{ hitRatio }}
@@ -414,7 +435,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<Database class="h-3.5 w-3.5" />
<span>Key Count</span>
<span>{{ t("redis.dashboard.keyCount") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="keyCount">
{{ keyCount }}
@@ -424,7 +445,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="rounded-lg border bg-card text-card-foreground p-3">
<div class="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<Users class="h-3.5 w-3.5" />
<span>Clients</span>
<span>{{ t("redis.dashboard.clients") }}</span>
</div>
<div class="text-xl font-semibold tabular-nums dbx-editor-font-family truncate" :title="connectedClients">
{{ connectedClients }}
@@ -436,7 +457,7 @@ function copyEntryValue(entry: InfoEntry) {
<div class="px-4 pb-2">
<div class="relative">
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
<Input v-model="searchQuery" class="h-8 pl-7 text-xs" placeholder="Search indicators..." />
<Input v-model="searchQuery" class="h-8 pl-7 text-xs" :placeholder="t('redis.dashboard.searchIndicators')" />
</div>
</div>
@@ -452,7 +473,7 @@ function copyEntryValue(entry: InfoEntry) {
<!-- Section header (clickable) -->
<button class="w-full flex items-center gap-2 px-3 py-2 text-xs font-semibold text-foreground hover:bg-muted/50 transition-colors text-left" @click="toggleSection(section.name)">
<component :is="isCollapsed(section.name) ? ChevronRight : ChevronDown" class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<span>{{ section.name }}</span>
<span>{{ infoSectionTitle(section.name) }}</span>
<span class="ml-auto text-muted-foreground font-normal">{{ section.entries.length }}</span>
</button>
<!-- Section body -->
@@ -472,8 +493,8 @@ function copyEntryValue(entry: InfoEntry) {
<!-- Empty / no data -->
<div v-else-if="!loading" class="flex flex-col items-center justify-center py-12 text-xs text-muted-foreground">
<Info class="mb-2 h-5 w-5" />
<span v-if="searchQuery">No matching indicators</span>
<span v-else>No data available</span>
<span v-if="searchQuery">{{ t("redis.dashboard.noMatchingIndicators") }}</span>
<span v-else>{{ t("redis.dashboard.noDataAvailable") }}</span>
</div>
</div>
</div>
+29
View File
@@ -2593,6 +2593,35 @@ export default {
slowlogColumnClientName: "Client Name",
slowlogTotal: "{count} entries",
slowlogDetailTitle: "Slow Log Entry #{id}",
dashboard: {
totalMemory: "Total Memory",
usedMemory: "Used Memory",
operations: "Operations",
hitRatio: "Hit Ratio",
keyCount: "Key Count",
clients: "Clients",
searchIndicators: "Search indicators...",
node: "Node",
off: "Off",
refresh: "Refresh",
noMatchingIndicators: "No matching indicators",
noDataAvailable: "No data available",
sections: {
server: "Server",
clients: "Clients",
memory: "Memory",
persistence: "Persistence",
stats: "Stats",
replication: "Replication",
cpu: "CPU",
modules: "Modules",
commandstats: "Command Stats",
errorstats: "Error Stats",
latencystats: "Latency Stats",
cluster: "Cluster",
keyspace: "Keyspace",
},
},
},
mongo: {
documents: "{count} documents",
+49
View File
@@ -2442,6 +2442,35 @@ export default withEnglishFallback({
slowlogColumnClientName: "Nombre del cliente",
slowlogTotal: "{count} entradas",
slowlogDetailTitle: "Entrada de Slow Log #{id}",
dashboard: {
totalMemory: "Memoria total",
usedMemory: "Memoria utilizada",
operations: "Operaciones",
hitRatio: "Tasa de aciertos",
keyCount: "Cantidad de claves",
clients: "Clientes",
searchIndicators: "Buscar indicadores...",
node: "Nodo",
off: "Desactivado",
refresh: "Actualizar",
noMatchingIndicators: "No hay indicadores coincidentes",
noDataAvailable: "No hay datos disponibles",
sections: {
server: "Servidor",
clients: "Clientes",
memory: "Memoria",
persistence: "Persistencia",
stats: "Estadísticas",
replication: "Replicación",
cpu: "CPU",
modules: "Módulos",
commandstats: "Estadísticas de comandos",
errorstats: "Estadísticas de errores",
latencystats: "Estadísticas de latencia",
cluster: "Clúster",
keyspace: "Espacio de claves",
},
},
},
mongo: {
documents: "{count} documentos",
@@ -4636,6 +4665,26 @@ export default withEnglishFallback({
commandChart: "Comandos por segundo",
rawStatus: "Variables de estado",
filterStatus: "Filtrar variables de estado",
tps: "Transacciones por segundo",
activeQueries: "Consultas activas",
cacheHit: "Tasa de aciertos de caché",
deadlocks: "Interbloqueos",
tempFiles: "Archivos temporales",
walRate: "Tasa de WAL",
commit: "Confirmaciones",
rollback: "Reversiones",
total: "Total",
active: "Activas",
idle: "Inactivas",
fetched: "Obtenidas",
returned: "Devueltas",
read: "Lecturas",
hits: "Aciertos",
serverSessionsChart: "Sesiones del servidor",
transactionsChart: "Transacciones por segundo",
tuplesInChart: "Escritura de tuplas",
tuplesOutChart: "Lectura de tuplas",
blockIoChart: "E/S de bloques",
},
databaseBackup: {
title: "Copia de seguridad de la base de datos",
+49
View File
@@ -2440,6 +2440,35 @@ export default withEnglishFallback({
slowlogColumnClientName: "Nome Client",
slowlogTotal: "{count} voci",
slowlogDetailTitle: "Voce Slow Log #{id}",
dashboard: {
totalMemory: "Memoria totale",
usedMemory: "Memoria utilizzata",
operations: "Operazioni",
hitRatio: "Percentuale di hit",
keyCount: "Numero di chiavi",
clients: "Client",
searchIndicators: "Cerca indicatori...",
node: "Nodo",
off: "Disattivato",
refresh: "Aggiorna",
noMatchingIndicators: "Nessun indicatore corrispondente",
noDataAvailable: "Nessun dato disponibile",
sections: {
server: "Server",
clients: "Client",
memory: "Memoria",
persistence: "Persistenza",
stats: "Statistiche",
replication: "Replica",
cpu: "CPU",
modules: "Moduli",
commandstats: "Statistiche dei comandi",
errorstats: "Statistiche degli errori",
latencystats: "Statistiche della latenza",
cluster: "Cluster",
keyspace: "Spazio delle chiavi",
},
},
},
mongo: {
documents: "{count} documenti",
@@ -4634,6 +4663,26 @@ export default withEnglishFallback({
commandChart: "Comandi al secondo",
rawStatus: "Variabili di stato",
filterStatus: "Filtra variabili di stato",
tps: "Transazioni al secondo",
activeQueries: "Query attive",
cacheHit: "Percentuale di hit della cache",
deadlocks: "Deadlock",
tempFiles: "File temporanei",
walRate: "Velocità WAL",
commit: "Commit",
rollback: "Rollback",
total: "Totale",
active: "Attive",
idle: "Inattive",
fetched: "Recuperate",
returned: "Restituite",
read: "Letture",
hits: "Hit",
serverSessionsChart: "Sessioni del server",
transactionsChart: "Transazioni al secondo",
tuplesInChart: "Scrittura delle tuple",
tuplesOutChart: "Lettura delle tuple",
blockIoChart: "I/O a blocchi",
},
databaseBackup: {
title: "Backup del database",
+49
View File
@@ -2441,6 +2441,35 @@ export default withEnglishFallback({
slowlogColumnClientName: "クライアント名",
slowlogTotal: "{count} エントリ",
slowlogDetailTitle: "スローログエントリ #{id}",
dashboard: {
totalMemory: "総メモリ",
usedMemory: "使用メモリ",
operations: "操作数",
hitRatio: "ヒット率",
keyCount: "キー数",
clients: "クライアント数",
searchIndicators: "指標を検索...",
node: "ノード",
off: "オフ",
refresh: "更新",
noMatchingIndicators: "一致する指標がありません",
noDataAvailable: "データがありません",
sections: {
server: "サーバー",
clients: "クライアント",
memory: "メモリ",
persistence: "永続化",
stats: "統計",
replication: "レプリケーション",
cpu: "CPU",
modules: "モジュール",
commandstats: "コマンド統計",
errorstats: "エラー統計",
latencystats: "レイテンシ統計",
cluster: "クラスター",
keyspace: "キースペース",
},
},
},
mongo: {
documents: "{count}ドキュメント",
@@ -4635,6 +4664,26 @@ export default withEnglishFallback({
commandChart: "毎秒コマンド数",
rawStatus: "ステータス変数",
filterStatus: "ステータス変数の絞り込み",
tps: "毎秒トランザクション数",
activeQueries: "アクティブなクエリ",
cacheHit: "キャッシュヒット率",
deadlocks: "デッドロック",
tempFiles: "一時ファイル",
walRate: "WAL 生成速度",
commit: "コミット",
rollback: "ロールバック",
total: "合計",
active: "アクティブ",
idle: "アイドル",
fetched: "フェッチ",
returned: "返却",
read: "読み取り",
hits: "ヒット",
serverSessionsChart: "サーバーセッション",
transactionsChart: "毎秒トランザクション数",
tuplesInChart: "タプル書き込み",
tuplesOutChart: "タプル読み取り",
blockIoChart: "ブロック I/O",
},
databaseBackup: {
title: "データベースバックアップ",
+49
View File
@@ -2442,6 +2442,35 @@ export default withEnglishFallback({
pubsubSend: "Enviar",
pubsubPublishFailed: "Publicação falhou: {error}",
pubsubWsConnectFailed: "Conexão WebSocket falhou: {error}",
dashboard: {
totalMemory: "Memória total",
usedMemory: "Memória utilizada",
operations: "Operações",
hitRatio: "Taxa de acertos",
keyCount: "Quantidade de chaves",
clients: "Clientes",
searchIndicators: "Buscar indicadores...",
node: "Nó",
off: "Desativado",
refresh: "Atualizar",
noMatchingIndicators: "Nenhum indicador correspondente",
noDataAvailable: "Nenhum dado disponível",
sections: {
server: "Servidor",
clients: "Clientes",
memory: "Memória",
persistence: "Persistência",
stats: "Estatísticas",
replication: "Replicação",
cpu: "CPU",
modules: "Módulos",
commandstats: "Estatísticas de comandos",
errorstats: "Estatísticas de erros",
latencystats: "Estatísticas de latência",
cluster: "Cluster",
keyspace: "Espaço de chaves",
},
},
},
mongo: {
documents: "{count} documentos",
@@ -4636,6 +4665,26 @@ export default withEnglishFallback({
commandChart: "Comandos por segundo",
rawStatus: "Variáveis de estado",
filterStatus: "Filtrar variáveis de estado",
tps: "Transações por segundo",
activeQueries: "Consultas ativas",
cacheHit: "Taxa de acertos do cache",
deadlocks: "Deadlocks",
tempFiles: "Arquivos temporários",
walRate: "Taxa de WAL",
commit: "Confirmações",
rollback: "Reversões",
total: "Total",
active: "Ativas",
idle: "Ociosas",
fetched: "Buscadas",
returned: "Retornadas",
read: "Leituras",
hits: "Acertos",
serverSessionsChart: "Sessões do servidor",
transactionsChart: "Transações por segundo",
tuplesInChart: "Gravação de tuplas",
tuplesOutChart: "Leitura de tuplas",
blockIoChart: "E/S de blocos",
},
databaseBackup: {
title: "Backup de banco de dados",
+49
View File
@@ -1925,6 +1925,26 @@ export default withEnglishFallback({
commandChart: "每秒命令数",
rawStatus: "状态变量",
filterStatus: "筛选状态变量",
tps: "每秒事务数",
activeQueries: "活动查询",
cacheHit: "缓存命中率",
deadlocks: "死锁",
tempFiles: "临时文件",
walRate: "WAL 生成速率",
commit: "提交",
rollback: "回滚",
total: "总计",
active: "活跃",
idle: "空闲",
fetched: "提取",
returned: "返回",
read: "读取",
hits: "命中",
serverSessionsChart: "服务器会话",
transactionsChart: "每秒事务数",
tuplesInChart: "元组写入",
tuplesOutChart: "元组读取",
blockIoChart: "块 I/O",
},
userAdmin: {
title: "用户与权限",
@@ -2563,6 +2583,35 @@ export default withEnglishFallback({
slowlogColumnClientName: "客户端名称",
slowlogTotal: "共 {count} 条",
slowlogDetailTitle: "慢日志条目 #{id}",
dashboard: {
totalMemory: "总内存",
usedMemory: "已用内存",
operations: "操作数",
hitRatio: "命中率",
keyCount: "键数量",
clients: "客户端",
searchIndicators: "搜索指标...",
node: "节点",
off: "关闭",
refresh: "刷新",
noMatchingIndicators: "没有匹配的指标",
noDataAvailable: "暂无数据",
sections: {
server: "服务器",
clients: "客户端",
memory: "内存",
persistence: "持久化",
stats: "统计",
replication: "复制",
cpu: "CPU",
modules: "模块",
commandstats: "命令统计",
errorstats: "错误统计",
latencystats: "延迟统计",
cluster: "集群",
keyspace: "键空间",
},
},
},
mongo: {
documents: "{count} 个文档",
+49
View File
@@ -2259,6 +2259,35 @@ export default withEnglishFallback({
pubsubSend: "傳送",
pubsubPublishFailed: "發布失敗: {error}",
pubsubWsConnectFailed: "WebSocket 連線失敗: {error}",
dashboard: {
totalMemory: "總記憶體",
usedMemory: "已用記憶體",
operations: "操作數",
hitRatio: "命中率",
keyCount: "鍵數量",
clients: "客戶端",
searchIndicators: "搜尋指標...",
node: "節點",
off: "關閉",
refresh: "重新整理",
noMatchingIndicators: "沒有符合的指標",
noDataAvailable: "暫無資料",
sections: {
server: "伺服器",
clients: "客戶端",
memory: "記憶體",
persistence: "持久化",
stats: "統計",
replication: "複寫",
cpu: "CPU",
modules: "模組",
commandstats: "命令統計",
errorstats: "錯誤統計",
latencystats: "延遲統計",
cluster: "叢集",
keyspace: "鍵空間",
},
},
},
mongo: {
documents: "{count} 個文件",
@@ -4636,6 +4665,26 @@ export default withEnglishFallback({
commandChart: "每秒命令數",
rawStatus: "狀態變數",
filterStatus: "篩選狀態變數",
tps: "每秒事務數",
activeQueries: "作用中查詢",
cacheHit: "快取命中率",
deadlocks: "死鎖",
tempFiles: "暫存檔案",
walRate: "WAL 產生速率",
commit: "提交",
rollback: "回溯",
total: "總計",
active: "作用中",
idle: "閒置",
fetched: "擷取",
returned: "傳回",
read: "讀取",
hits: "命中",
serverSessionsChart: "伺服器工作階段",
transactionsChart: "每秒事務數",
tuplesInChart: "元組寫入",
tuplesOutChart: "元組讀取",
blockIoChart: "區塊 I/O",
},
databaseBackup: {
title: "資料庫備份",