fix(i18n): Wave 3 完成 - thinking-terminal + 翻譯補充
- thinking-terminal.tsx: 所有 hardcode 改用 useTranslations - DependencyPathVisualizer: blastRadius/rootCauseChain - ServiceChainVisualizer: upstreamImpact/downstreamDependencies - FinOpsVisualizer: finopsAnalysis/wastedPerMonth/realizable/freed - ThinkingTerminal: title/executing/initiate/waiting/stream/events - live-host-card.tsx: 移除未使用的 baselineLabel 預設值 - zh-TW.json/en.json: 新增 terminal 區塊翻譯 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -163,15 +163,25 @@
|
||||
"terminal": {
|
||||
"title": "AWOOOI Terminal",
|
||||
"version": "Version",
|
||||
"waiting": "Waiting for command...",
|
||||
"waiting": "> Waiting for command...",
|
||||
"initiate": "INITIATE SYNC",
|
||||
"executing": "EXECUTING...",
|
||||
"events": "events",
|
||||
"stream": "STREAM",
|
||||
"executing": ">_ EXECUTING...",
|
||||
"events": "{count} events",
|
||||
"stream": "STREAM: /agent/thinking",
|
||||
"waitingForData": "Waiting for decision chain data...",
|
||||
"steps": "Steps",
|
||||
"streaming": "Streaming",
|
||||
"paused": "Paused"
|
||||
"paused": "Paused",
|
||||
"blastRadius": "[ BLAST RADIUS ]",
|
||||
"rootCauseChain": "[ ROOT CAUSE CHAIN ]",
|
||||
"upstreamImpact": "[ UPSTREAM IMPACT ]",
|
||||
"downstreamDependencies": "[ DOWNSTREAM DEPENDENCIES ]",
|
||||
"dependsOn": "depends on",
|
||||
"calls": "calls",
|
||||
"finopsAnalysis": "[ FINOPS ANALYSIS ]",
|
||||
"wastedPerMonth": "Wasted/mo",
|
||||
"realizable": "Realizable",
|
||||
"freed": "Freed"
|
||||
},
|
||||
"omniTerminal": {
|
||||
"title": "OMNI-TERMINAL",
|
||||
|
||||
@@ -163,15 +163,25 @@
|
||||
"terminal": {
|
||||
"title": "AWOOOI 終端機",
|
||||
"version": "版本",
|
||||
"waiting": "等待指令...",
|
||||
"waiting": "> 等待指令...",
|
||||
"initiate": "啟動同步",
|
||||
"executing": "執行中...",
|
||||
"events": "事件",
|
||||
"stream": "串流",
|
||||
"executing": ">_ 執行中...",
|
||||
"events": "{count} 事件",
|
||||
"stream": "串流: /agent/thinking",
|
||||
"waitingForData": "等待決策鏈資料...",
|
||||
"steps": "步驟",
|
||||
"streaming": "串流中",
|
||||
"paused": "已暫停"
|
||||
"paused": "已暫停",
|
||||
"blastRadius": "[ 爆炸半徑 ]",
|
||||
"rootCauseChain": "[ 根因分析鏈 ]",
|
||||
"upstreamImpact": "[ 上游影響 ]",
|
||||
"downstreamDependencies": "[ 下游依賴 ]",
|
||||
"dependsOn": "依賴",
|
||||
"calls": "呼叫",
|
||||
"finopsAnalysis": "[ FINOPS 分析 ]",
|
||||
"wastedPerMonth": "每月浪費",
|
||||
"realizable": "可實現",
|
||||
"freed": "已釋放"
|
||||
},
|
||||
"omniTerminal": {
|
||||
"title": "OMNI-TERMINAL",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
useAgentStore,
|
||||
@@ -50,12 +51,13 @@ function DependencyPathVisualizer({
|
||||
paths: string[]
|
||||
direction: 'upstream' | 'downstream'
|
||||
}) {
|
||||
const t = useTranslations('terminal')
|
||||
if (paths.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
|
||||
<div className="text-xs text-nothing-gray-500 mb-2">
|
||||
{direction === 'upstream' ? '[ BLAST RADIUS ]' : '[ ROOT CAUSE CHAIN ]'}
|
||||
{direction === 'upstream' ? t('blastRadius') : t('rootCauseChain')}
|
||||
</div>
|
||||
<div className="font-mono text-sm space-y-1">
|
||||
{paths.map((path, i) => (
|
||||
@@ -84,13 +86,14 @@ function ServiceChainVisualizer({
|
||||
target: string
|
||||
type: 'blast_radius' | 'root_cause'
|
||||
}) {
|
||||
const t = useTranslations('terminal')
|
||||
// 建構 ASCII 風格的依賴圖
|
||||
const isBlastRadius = type === 'blast_radius'
|
||||
|
||||
return (
|
||||
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
|
||||
<div className="text-xs text-nothing-gray-500 mb-2">
|
||||
{isBlastRadius ? '[ UPSTREAM IMPACT ]' : '[ DOWNSTREAM DEPENDENCIES ]'}
|
||||
{isBlastRadius ? t('upstreamImpact') : t('downstreamDependencies')}
|
||||
</div>
|
||||
|
||||
{/* ASCII Art 風格圖形 */}
|
||||
@@ -103,7 +106,7 @@ function ServiceChainVisualizer({
|
||||
{' │ '}{services.slice(0, 3).join(', ').padEnd(19)}{'│'}
|
||||
</div>
|
||||
<div className="text-nothing-gray-600">{' └─────────┬───────────┘'}</div>
|
||||
<div className="text-nothing-gray-600">{' │ depends on'}</div>
|
||||
<div className="text-nothing-gray-600">{` │ ${t('dependsOn')}`}</div>
|
||||
<div className="text-nothing-gray-600">{' ▼'}</div>
|
||||
<div className="text-nothing-gray-600">{' ┌─────────────────────┐'}</div>
|
||||
<div className="text-status-critical">
|
||||
@@ -119,7 +122,7 @@ function ServiceChainVisualizer({
|
||||
{' │ '}{target.padEnd(19)}{'│ '}<span className="text-status-warning">!</span>
|
||||
</div>
|
||||
<div className="text-nothing-gray-600">{' └─────────┬───────────┘'}</div>
|
||||
<div className="text-nothing-gray-600">{' │ calls'}</div>
|
||||
<div className="text-nothing-gray-600">{` │ ${t('calls')}`}</div>
|
||||
<div className="text-nothing-gray-600">{' ▼'}</div>
|
||||
<div className="text-nothing-gray-600">{' ┌─────────────────────┐'}</div>
|
||||
<div className="text-status-critical">
|
||||
@@ -157,9 +160,10 @@ function ServiceChainVisualizer({
|
||||
// ==================== FinOps 視覺化 ====================
|
||||
|
||||
function FinOpsVisualizer({ data }: { data: NonNullable<ThinkingStep['finopsData']> }) {
|
||||
const t = useTranslations('terminal')
|
||||
return (
|
||||
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
|
||||
<div className="text-xs text-nothing-gray-500 mb-2">[ FINOPS ANALYSIS ]</div>
|
||||
<div className="text-xs text-nothing-gray-500 mb-2">{t('finopsAnalysis')}</div>
|
||||
|
||||
{/* 成本摘要 */}
|
||||
<div className="grid grid-cols-3 gap-2 mb-3">
|
||||
@@ -167,19 +171,19 @@ function FinOpsVisualizer({ data }: { data: NonNullable<ThinkingStep['finopsData
|
||||
<div className="text-lg font-bold text-status-critical">
|
||||
${data.totalWastedUsd.toFixed(0)}
|
||||
</div>
|
||||
<div className="text-xs text-nothing-gray-500">Wasted/mo</div>
|
||||
<div className="text-xs text-nothing-gray-500">{t('wastedPerMonth')}</div>
|
||||
</div>
|
||||
<div className="text-center p-2 bg-nothing-gray-900 rounded">
|
||||
<div className="text-lg font-bold text-status-healthy">
|
||||
${data.realizableSavingsUsd.toFixed(0)}
|
||||
</div>
|
||||
<div className="text-xs text-nothing-gray-500">Realizable</div>
|
||||
<div className="text-xs text-nothing-gray-500">{t('realizable')}</div>
|
||||
</div>
|
||||
<div className="text-center p-2 bg-nothing-gray-900 rounded">
|
||||
<div className="text-lg font-bold text-status-warning">
|
||||
${data.freedResourcesUsd.toFixed(0)}
|
||||
</div>
|
||||
<div className="text-xs text-nothing-gray-500">Freed</div>
|
||||
<div className="text-xs text-nothing-gray-500">{t('freed')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -305,6 +309,7 @@ export function ThinkingTerminal({
|
||||
className,
|
||||
maxHeight = '300px',
|
||||
}: ThinkingTerminalProps) {
|
||||
const t = useTranslations('terminal')
|
||||
const thinkingStream = useAgentStore(selectThinkingStream)
|
||||
const status = useAgentStore(selectAgentStatus)
|
||||
const error = useAgentStore(selectError)
|
||||
@@ -331,7 +336,7 @@ export function ThinkingTerminal({
|
||||
<div className="w-3 h-3 rounded-full bg-status-thinking" />
|
||||
<div className="w-3 h-3 rounded-full bg-status-healthy" />
|
||||
</div>
|
||||
<h2 className="font-display text-display-sm">AWOOOI Terminal</h2>
|
||||
<h2 className="font-display text-display-sm">{t('title')}</h2>
|
||||
</div>
|
||||
<span className="font-mono text-xs text-nothing-gray-500">
|
||||
v0.1.0 | SSE
|
||||
@@ -349,7 +354,7 @@ export function ThinkingTerminal({
|
||||
: 'bg-nothing-white text-nothing-black border-transparent hover:bg-nothing-gray-200'
|
||||
)}
|
||||
>
|
||||
{isStreaming ? '>_ EXECUTING...' : 'INITIATE SYNC'}
|
||||
{isStreaming ? t('executing') : t('initiate')}
|
||||
</button>
|
||||
|
||||
{/* Terminal Output */}
|
||||
@@ -359,7 +364,7 @@ export function ThinkingTerminal({
|
||||
>
|
||||
{thinkingStream.length === 0 && !isStreaming && !error && (
|
||||
<div className="text-nothing-gray-600">
|
||||
{'>'} Waiting for command...
|
||||
{t('waiting')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -379,10 +384,10 @@ export function ThinkingTerminal({
|
||||
{/* Footer */}
|
||||
<div className="flex items-center justify-between pt-2 border-t border-nothing-gray-800">
|
||||
<p className="font-mono text-xs text-nothing-gray-600">
|
||||
STREAM: /agent/thinking
|
||||
{t('stream')}
|
||||
</p>
|
||||
<p className="font-mono text-xs text-nothing-gray-600">
|
||||
{thinkingStream.length} events
|
||||
{t('events', { count: thinkingStream.length })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -324,7 +324,9 @@ function MetricBar({
|
||||
criticalThreshold = 80,
|
||||
collectingLabel = '-',
|
||||
baselineValue,
|
||||
baselineLabel = '基準線',
|
||||
// Note: baselineLabel prop kept for future use but not used currently
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
baselineLabel,
|
||||
}: MetricBarProps) {
|
||||
// Handle null/undefined values - show collecting state
|
||||
if (value === null || value === undefined) {
|
||||
|
||||
Reference in New Issue
Block a user