feat(phase6-9): Complete modular architecture and Agent Teams
Phase 6.4 - Modular Architecture: - Add lewooogo-brain adapters for LLM providers - Add lewooogo-data dual memory (Redis + PostgreSQL) - Implement consensus engine for multi-agent decisions - Add incident memory service for historical context Phase 9 - Agent Teams (Claude Agent SDK): - Add base agent class with Claude Sonnet 4 integration - Implement action planner, blast radius, and security agents - Add agent API endpoints and proposal workflow - Integrate ADR-009 OpenClaw Agent Teams architecture DevOps & CI/CD: - Add GitHub Actions CI/CD workflows (ci.yaml, cd.yaml) - Add pre-commit hooks and secrets baseline - Add docker-compose for local development - Update Kubernetes network policies Frontend Improvements: - Add auto-healing error boundary component - Update i18n messages for agent features - Enhance dual-state incident card with execution feedback Documentation: - Add 7 ADRs covering MCP, design system, architecture decisions - Update ARCHITECTURE_MEMORY.md with modular design - Add GLOBAL_RULES.md and SOUL.md for project identity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
655
docs/design/COMPONENT_LIBRARY.md
Normal file
655
docs/design/COMPONENT_LIBRARY.md
Normal file
@@ -0,0 +1,655 @@
|
||||
# AWOOOI 原子組件庫規格
|
||||
|
||||
> **版本**: v1.0
|
||||
> **建立日期**: 2026-03-20
|
||||
> **負責人**: CPO
|
||||
> **設計系統**: Nothing.tech 純白工業風
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本文件定義 AWOOOI 前端組件庫的設計規格,採用 Atomic Design 原則,確保視覺一致性與開發效率。
|
||||
|
||||
---
|
||||
|
||||
## 設計 Token
|
||||
|
||||
### 色彩系統
|
||||
|
||||
```typescript
|
||||
// packages/lewooogo-ui/src/tokens/colors.ts
|
||||
|
||||
export const colors = {
|
||||
// 基底色 (Pure White Base)
|
||||
white: '#FFFFFF',
|
||||
snow: '#FAFAFA', // 主背景
|
||||
cloud: '#F5F5F5', // 次背景/卡片
|
||||
mist: '#E5E5E5', // 邊框/分隔線
|
||||
|
||||
// 文字色 (High Contrast)
|
||||
ink: '#0A0A0A', // 主文字
|
||||
gray: {
|
||||
600: '#6B7280', // 次文字
|
||||
400: '#9CA3AF', // 輔助文字
|
||||
200: '#E5E7EB', // 禁用文字
|
||||
},
|
||||
|
||||
// 功能色
|
||||
status: {
|
||||
success: '#10B981',
|
||||
warning: '#F59E0B',
|
||||
error: '#EF4444',
|
||||
info: '#3B82F6',
|
||||
thinking: '#8B5CF6', // AI 思考中
|
||||
},
|
||||
|
||||
// 品牌色
|
||||
brand: {
|
||||
primary: '#FF6B35', // AWOOOI 橘
|
||||
nothingRed: '#D71921', // Nothing 品牌紅
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 間距系統
|
||||
|
||||
```typescript
|
||||
// packages/lewooogo-ui/src/tokens/spacing.ts
|
||||
|
||||
export const spacing = {
|
||||
0: '0',
|
||||
1: '4px',
|
||||
2: '8px',
|
||||
3: '12px',
|
||||
4: '16px',
|
||||
5: '20px',
|
||||
6: '24px',
|
||||
8: '32px',
|
||||
10: '40px',
|
||||
12: '48px',
|
||||
16: '64px',
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 字體系統
|
||||
|
||||
```typescript
|
||||
// packages/lewooogo-ui/src/tokens/typography.ts
|
||||
|
||||
export const typography = {
|
||||
fontFamily: {
|
||||
display: '"NDot", monospace', // AI 介面/數字
|
||||
body: '"Inter", system-ui', // 一般文字
|
||||
mono: '"JetBrains Mono", monospace', // 程式碼
|
||||
},
|
||||
|
||||
fontSize: {
|
||||
xs: '12px',
|
||||
sm: '14px',
|
||||
base: '16px',
|
||||
lg: '18px',
|
||||
xl: '20px',
|
||||
'2xl': '24px',
|
||||
'3xl': '30px',
|
||||
'4xl': '36px',
|
||||
},
|
||||
|
||||
fontWeight: {
|
||||
normal: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
},
|
||||
|
||||
lineHeight: {
|
||||
tight: 1.25,
|
||||
normal: 1.5,
|
||||
relaxed: 1.75,
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 效果系統
|
||||
|
||||
```typescript
|
||||
// packages/lewooogo-ui/src/tokens/effects.ts
|
||||
|
||||
export const effects = {
|
||||
// 白玻璃效果 (White Glassmorphism)
|
||||
glass: {
|
||||
background: 'rgba(255, 255, 255, 0.7)',
|
||||
blur: 'blur(20px)',
|
||||
border: 'rgba(0, 0, 0, 0.05)',
|
||||
},
|
||||
|
||||
// 點陣紋理 (Dot Matrix)
|
||||
dotMatrix: {
|
||||
pattern: 'radial-gradient(circle, rgba(0, 0, 0, 0.03) 1px, transparent 1px)',
|
||||
size: '16px 16px',
|
||||
},
|
||||
|
||||
// 陰影
|
||||
shadow: {
|
||||
sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
|
||||
md: '0 4px 6px rgba(0, 0, 0, 0.05)',
|
||||
lg: '0 10px 15px rgba(0, 0, 0, 0.05)',
|
||||
glow: '0 0 20px rgba(255, 107, 53, 0.2)', // 品牌光暈
|
||||
},
|
||||
|
||||
// 圓角
|
||||
radius: {
|
||||
sm: '4px',
|
||||
md: '8px',
|
||||
lg: '12px',
|
||||
xl: '16px',
|
||||
full: '9999px',
|
||||
},
|
||||
|
||||
// 過渡
|
||||
transition: {
|
||||
fast: '150ms ease',
|
||||
normal: '250ms ease',
|
||||
slow: '350ms ease',
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Atoms (原子組件)
|
||||
|
||||
### StatusOrb - 狀態呼吸燈
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/atoms/StatusOrb.tsx
|
||||
|
||||
interface StatusOrbProps {
|
||||
status: 'healthy' | 'warning' | 'critical' | 'unknown' | 'thinking';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
pulse?: boolean;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 狀態呼吸燈
|
||||
* - 即時反映系統/主機狀態
|
||||
* - 支援脈衝動畫 (告警/思考中)
|
||||
*
|
||||
* @example
|
||||
* <StatusOrb status="healthy" size="md" />
|
||||
* <StatusOrb status="thinking" pulse label="AI 處理中" />
|
||||
*/
|
||||
```
|
||||
|
||||
**視覺規格**:
|
||||
| Size | 直徑 | 光暈半徑 |
|
||||
|------|------|---------|
|
||||
| sm | 8px | 12px |
|
||||
| md | 12px | 18px |
|
||||
| lg | 16px | 24px |
|
||||
|
||||
**狀態色彩**:
|
||||
| Status | 色彩 | 脈衝 |
|
||||
|--------|------|------|
|
||||
| healthy | `#10B981` | 無 |
|
||||
| warning | `#F59E0B` | 慢 (2s) |
|
||||
| critical | `#EF4444` | 快 (0.5s) |
|
||||
| unknown | `#9CA3AF` | 無 |
|
||||
| thinking | `#8B5CF6` | 中 (1s) |
|
||||
|
||||
---
|
||||
|
||||
### MetricValue - 數值顯示
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/atoms/MetricValue.tsx
|
||||
|
||||
interface MetricValueProps {
|
||||
value: number | string;
|
||||
unit?: string;
|
||||
trend?: 'up' | 'down' | 'stable';
|
||||
trendValue?: string;
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||
format?: 'number' | 'percent' | 'bytes' | 'duration';
|
||||
}
|
||||
|
||||
/**
|
||||
* 數值顯示組件
|
||||
* - NDot 字體呈現數字
|
||||
* - 支援趨勢箭頭與變化值
|
||||
*
|
||||
* @example
|
||||
* <MetricValue value={99.9} unit="%" trend="up" trendValue="+0.1%" />
|
||||
* <MetricValue value={1024} format="bytes" /> // 顯示 "1 KB"
|
||||
*/
|
||||
```
|
||||
|
||||
**視覺規格**:
|
||||
| Size | 字體大小 | 權重 |
|
||||
|------|---------|------|
|
||||
| sm | 18px | 500 |
|
||||
| md | 24px | 600 |
|
||||
| lg | 36px | 700 |
|
||||
| xl | 48px | 700 |
|
||||
|
||||
---
|
||||
|
||||
### IconButton - 圖示按鈕
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/atoms/IconButton.tsx
|
||||
|
||||
interface IconButtonProps {
|
||||
icon: ReactNode;
|
||||
variant?: 'ghost' | 'outline' | 'solid';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
color?: 'default' | 'primary' | 'danger';
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 圖示按鈕
|
||||
* - 用於工具列、操作區
|
||||
* - 必須有 tooltip 說明
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Badge - 標籤徽章
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/atoms/Badge.tsx
|
||||
|
||||
interface BadgeProps {
|
||||
children: ReactNode;
|
||||
variant?: 'solid' | 'outline' | 'subtle';
|
||||
color?: 'gray' | 'green' | 'yellow' | 'red' | 'blue' | 'purple' | 'orange';
|
||||
size?: 'sm' | 'md';
|
||||
dot?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 標籤徽章
|
||||
* - 用於狀態標示、分類
|
||||
*
|
||||
* @example
|
||||
* <Badge color="green">Active</Badge>
|
||||
* <Badge color="red" dot>3 Alerts</Badge>
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Molecules (分子組件)
|
||||
|
||||
### GlassCard - 玻璃卡片
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/molecules/GlassCard.tsx
|
||||
|
||||
interface GlassCardProps {
|
||||
children: ReactNode;
|
||||
variant?: 'default' | 'elevated' | 'bordered';
|
||||
padding?: 'sm' | 'md' | 'lg';
|
||||
interactive?: boolean;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 白玻璃卡片
|
||||
* - Nothing.tech 核心視覺元素
|
||||
* - 支援點擊交互與選中狀態
|
||||
*
|
||||
* CSS:
|
||||
* - background: rgba(255, 255, 255, 0.7)
|
||||
* - backdrop-filter: blur(20px)
|
||||
* - border: 1px solid rgba(0, 0, 0, 0.05)
|
||||
*/
|
||||
```
|
||||
|
||||
**視覺規格**:
|
||||
| Variant | 背景 | 邊框 | 陰影 |
|
||||
|---------|------|------|------|
|
||||
| default | glass | subtle | sm |
|
||||
| elevated | glass | subtle | lg |
|
||||
| bordered | white | solid | none |
|
||||
|
||||
---
|
||||
|
||||
### HostCard - 主機卡片
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/molecules/HostCard.tsx
|
||||
|
||||
interface HostCardProps {
|
||||
host: {
|
||||
id: string;
|
||||
name: string;
|
||||
ip: string;
|
||||
role: string;
|
||||
status: 'healthy' | 'warning' | 'critical' | 'unknown';
|
||||
metrics?: {
|
||||
cpu: number;
|
||||
memory: number;
|
||||
disk: number;
|
||||
};
|
||||
lastSeen?: string;
|
||||
};
|
||||
variant?: 'compact' | 'detailed';
|
||||
showMetrics?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主機狀態卡片
|
||||
* - 戰情室核心組件
|
||||
* - 整合 StatusOrb + MetricValue
|
||||
*
|
||||
* @example
|
||||
* <HostCard host={hostData} variant="detailed" showMetrics />
|
||||
*/
|
||||
```
|
||||
|
||||
**佈局**:
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ ● web-server-01 [healthy] │
|
||||
│ 192.168.0.188 · AI+Web 中心 │
|
||||
├──────────────────────────────────────┤
|
||||
│ CPU Memory Disk │
|
||||
│ 45% 72% 58% │
|
||||
│ ████░ ███████░ █████░ │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### AlertPanel - 告警面板
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/molecules/AlertPanel.tsx
|
||||
|
||||
interface AlertPanelProps {
|
||||
alerts: Alert[];
|
||||
maxVisible?: number;
|
||||
showTimestamp?: boolean;
|
||||
onAlertClick?: (alert: Alert) => void;
|
||||
onAcknowledge?: (alertId: string) => void;
|
||||
}
|
||||
|
||||
interface Alert {
|
||||
id: string;
|
||||
severity: 'info' | 'warning' | 'critical';
|
||||
title: string;
|
||||
message: string;
|
||||
source: string;
|
||||
timestamp: string;
|
||||
acknowledged?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 告警列表面板
|
||||
* - 即時更新 (SSE)
|
||||
* - 支援確認操作
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ApprovalCard - HITL 審批卡片
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/molecules/ApprovalCard.tsx
|
||||
|
||||
interface ApprovalCardProps {
|
||||
approval: {
|
||||
id: string;
|
||||
type: 'deploy' | 'rollback' | 'config' | 'security';
|
||||
title: string;
|
||||
description: string;
|
||||
requester: string;
|
||||
blastRadius: 'low' | 'medium' | 'high' | 'critical';
|
||||
signaturesRequired: number;
|
||||
signaturesCollected: Signature[];
|
||||
expiresAt: string;
|
||||
aiSummary?: string;
|
||||
aiConfidence?: number;
|
||||
};
|
||||
currentUser: string;
|
||||
onApprove?: () => void;
|
||||
onReject?: () => void;
|
||||
onRequestInfo?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Human-In-The-Loop 審批卡片
|
||||
* - 顯示 Blast Radius 風險等級
|
||||
* - 支援 Multi-Sig 簽核進度
|
||||
* - AI 摘要與信心度
|
||||
*/
|
||||
```
|
||||
|
||||
**佈局**:
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 🚀 部署請求: web-api v2.3.1 │
|
||||
│ ════════════════════════════════════════════════ │
|
||||
│ │
|
||||
│ 📊 Blast Radius: ████████░░ HIGH │
|
||||
│ 影響: 3 服務 · 12 Pods · ~5000 用戶 │
|
||||
│ │
|
||||
│ 🤖 AI 摘要: 此更新包含 API 破壞性變更,建議 │
|
||||
│ 先通知下游服務團隊。信心度: 87% │
|
||||
│ │
|
||||
│ ✍️ 簽核進度: 1/2 │
|
||||
│ ✅ CTO (2026-03-20 10:30) │
|
||||
│ ⏳ CISO │
|
||||
│ │
|
||||
│ [詢問更多] [拒絕] [批准] │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Organisms (組織組件)
|
||||
|
||||
### CommandPalette - 快捷命令面板
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/organisms/CommandPalette.tsx
|
||||
|
||||
interface CommandPaletteProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
commands: Command[];
|
||||
recentCommands?: string[];
|
||||
onCommandSelect: (command: Command) => void;
|
||||
}
|
||||
|
||||
interface Command {
|
||||
id: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
icon?: ReactNode;
|
||||
shortcut?: string;
|
||||
category: 'navigation' | 'action' | 'ai' | 'settings';
|
||||
action: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cmd+K 快捷命令面板
|
||||
* - 全站快速導航與操作
|
||||
* - 支援模糊搜尋
|
||||
* - 顯示快捷鍵提示
|
||||
*/
|
||||
```
|
||||
|
||||
**快捷鍵**:
|
||||
| 快捷鍵 | 功能 |
|
||||
|-------|------|
|
||||
| `Cmd+K` | 開啟面板 |
|
||||
| `Esc` | 關閉面板 |
|
||||
| `↑/↓` | 選擇項目 |
|
||||
| `Enter` | 執行命令 |
|
||||
|
||||
---
|
||||
|
||||
### ThinkingTerminal - AI 思考終端
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/organisms/ThinkingTerminal.tsx
|
||||
|
||||
interface ThinkingTerminalProps {
|
||||
isOpen: boolean;
|
||||
stream: ThinkingStream | null;
|
||||
position?: 'bottom' | 'right';
|
||||
collapsible?: boolean;
|
||||
}
|
||||
|
||||
interface ThinkingStream {
|
||||
status: 'idle' | 'thinking' | 'completed' | 'error';
|
||||
steps: ThinkingStep[];
|
||||
result?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface ThinkingStep {
|
||||
id: string;
|
||||
type: 'input' | 'process' | 'output' | 'tool_call';
|
||||
content: string;
|
||||
timestamp: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI 思考過程終端
|
||||
* - SSE 即時串流
|
||||
* - 打字機效果
|
||||
* - 支援摺疊
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DataPincer - 數據鉗視覺化
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/organisms/DataPincer.tsx
|
||||
|
||||
interface DataPincerProps {
|
||||
data: {
|
||||
hosts: HostData[];
|
||||
connections: Connection[];
|
||||
flows: DataFlow[];
|
||||
};
|
||||
viewMode?: '2d' | '3d';
|
||||
interactive?: boolean;
|
||||
highlightHost?: string;
|
||||
onHostClick?: (hostId: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 數據鉗視覺化組件
|
||||
* - AWOOOI 品牌視覺符號
|
||||
* - 四主機架構拓撲圖
|
||||
* - 即時資料流動畫
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Templates (模板)
|
||||
|
||||
### DashboardLayout - 儀表板佈局
|
||||
|
||||
```tsx
|
||||
// packages/lewooogo-ui/src/templates/DashboardLayout.tsx
|
||||
|
||||
interface DashboardLayoutProps {
|
||||
children: ReactNode;
|
||||
sidebar?: ReactNode;
|
||||
header?: ReactNode;
|
||||
background?: 'default' | 'dotMatrix';
|
||||
}
|
||||
|
||||
/**
|
||||
* 儀表板頁面佈局
|
||||
* - 支援側邊欄
|
||||
* - 點陣背景紋理
|
||||
* - 響應式設計
|
||||
*/
|
||||
```
|
||||
|
||||
**斷點**:
|
||||
| 斷點 | 寬度 | 佈局 |
|
||||
|------|------|------|
|
||||
| mobile | < 768px | 單欄 + 抽屜選單 |
|
||||
| tablet | 768-1024px | 摺疊側欄 |
|
||||
| desktop | 1024-1440px | 展開側欄 |
|
||||
| wide | > 1440px | 展開側欄 + 更多欄位 |
|
||||
|
||||
---
|
||||
|
||||
## 無障礙規範 (WCAG 2.1 AA)
|
||||
|
||||
### 對比度要求
|
||||
|
||||
| 元素 | 最小對比度 | 實際值 |
|
||||
|------|-----------|--------|
|
||||
| 正文文字 | 4.5:1 | 17.3:1 (ink/snow) |
|
||||
| 大標題 | 3:1 | 17.3:1 |
|
||||
| 非文字元素 | 3:1 | 符合 |
|
||||
| 狀態色 (綠) | 3:1 | 4.5:1 (success/snow) |
|
||||
|
||||
### 互動元素
|
||||
|
||||
- 所有可點擊元素必須有 `focus-visible` 樣式
|
||||
- 鍵盤可操作 (Tab 導航)
|
||||
- 適當的 `aria-label`
|
||||
- 螢幕閱讀器支援
|
||||
|
||||
### Focus 樣式
|
||||
|
||||
```css
|
||||
.focus-visible {
|
||||
outline: 2px solid var(--brand-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 組件狀態
|
||||
|
||||
| 組件 | 狀態 | 負責人 |
|
||||
|------|------|--------|
|
||||
| StatusOrb | ⏳ 待開發 | CPO |
|
||||
| MetricValue | ⏳ 待開發 | CPO |
|
||||
| IconButton | ⏳ 待開發 | CPO |
|
||||
| Badge | ⏳ 待開發 | CPO |
|
||||
| GlassCard | ⏳ 待開發 | CPO |
|
||||
| HostCard | ⏳ 待開發 | CPO |
|
||||
| AlertPanel | ⏳ 待開發 | CPO |
|
||||
| ApprovalCard | ⏳ 待開發 | CPO |
|
||||
| CommandPalette | ⏳ 待開發 | CPO |
|
||||
| ThinkingTerminal | ⏳ 待開發 | CPO |
|
||||
| DataPincer | ⏳ 待開發 | CPO |
|
||||
| DashboardLayout | ⏳ 待開發 | CPO |
|
||||
|
||||
---
|
||||
|
||||
## 變更記錄
|
||||
|
||||
| 日期 | 版本 | 變更 | 作者 |
|
||||
|------|------|------|------|
|
||||
| 2026-03-20 | v1.0 | 初版建立 | CPO |
|
||||
|
||||
---
|
||||
|
||||
*此文件由 CPO 維護,前端開發者必須遵守此規格。*
|
||||
425
docs/design/I18N_STRUCTURE.md
Normal file
425
docs/design/I18N_STRUCTURE.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# AWOOOI i18n 字典檔結構規範
|
||||
|
||||
> **版本**: v1.0
|
||||
> **建立日期**: 2026-03-20
|
||||
> **負責人**: CPO
|
||||
> **框架**: next-intl
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
> 🎯 **顧問深度討論 #4**: 語意化 Key 命名學
|
||||
|
||||
此文件定義 AWOOOI 國際化字典檔的結構規範,採用語意化樹狀命名,
|
||||
確保 500+ 個翻譯項目在工程師與翻譯員之間有明確的上下文。
|
||||
|
||||
---
|
||||
|
||||
## 命名規範
|
||||
|
||||
### Key 命名格式
|
||||
|
||||
```
|
||||
[頁面].[組件].[元素]_[動作/狀態]
|
||||
```
|
||||
|
||||
### 正確與錯誤範例
|
||||
|
||||
```json
|
||||
// ❌ 錯誤: 無上下文,容易混淆
|
||||
{
|
||||
"approve": "批准",
|
||||
"cancel": "取消",
|
||||
"error": "發生錯誤",
|
||||
"warning": "警告"
|
||||
}
|
||||
|
||||
// ✅ 正確: 語意化命名,清楚上下文
|
||||
{
|
||||
"dashboard": {
|
||||
"approval_card": {
|
||||
"btn_approve": "批准執行",
|
||||
"btn_reject": "拒絕",
|
||||
"btn_request_info": "詢問更多",
|
||||
"label_blast_radius": "爆炸半徑",
|
||||
"status_pending": "等待簽核",
|
||||
"status_approved": "已批准"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 字典檔結構
|
||||
|
||||
### 目錄結構
|
||||
|
||||
```
|
||||
apps/web/messages/
|
||||
├── zh-TW.json # 繁體中文 (主要)
|
||||
└── en.json # English
|
||||
```
|
||||
|
||||
### 頂層分類
|
||||
|
||||
```json
|
||||
{
|
||||
"common": {}, // 共用元素 (按鈕、狀態、錯誤)
|
||||
"layout": {}, // 佈局元素 (導航、側邊欄、頁尾)
|
||||
"dashboard": {}, // 戰情室頁面
|
||||
"monitor": {}, // 監控模組
|
||||
"security": {}, // 安全模組
|
||||
"deploy": {}, // 部署模組
|
||||
"tickets": {}, // 工單模組
|
||||
"billing": {}, // 帳單模組
|
||||
"settings": {}, // 設定模組
|
||||
"ai_copilot": {}, // AI 助手
|
||||
"errors": {}, // 錯誤訊息
|
||||
"validation": {} // 表單驗證
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整字典檔範本
|
||||
|
||||
### zh-TW.json
|
||||
|
||||
```json
|
||||
{
|
||||
"common": {
|
||||
"btn": {
|
||||
"save": "儲存",
|
||||
"cancel": "取消",
|
||||
"confirm": "確認",
|
||||
"delete": "刪除",
|
||||
"edit": "編輯",
|
||||
"view": "查看",
|
||||
"back": "返回",
|
||||
"next": "下一步",
|
||||
"previous": "上一步",
|
||||
"submit": "送出",
|
||||
"reset": "重設",
|
||||
"search": "搜尋",
|
||||
"filter": "篩選",
|
||||
"export": "匯出",
|
||||
"import": "匯入",
|
||||
"refresh": "重新整理"
|
||||
},
|
||||
"status": {
|
||||
"loading": "載入中...",
|
||||
"success": "成功",
|
||||
"error": "失敗",
|
||||
"pending": "處理中",
|
||||
"active": "啟用",
|
||||
"inactive": "停用",
|
||||
"healthy": "健康",
|
||||
"warning": "警告",
|
||||
"critical": "嚴重",
|
||||
"unknown": "未知"
|
||||
},
|
||||
"time": {
|
||||
"just_now": "剛剛",
|
||||
"minutes_ago": "{count} 分鐘前",
|
||||
"hours_ago": "{count} 小時前",
|
||||
"days_ago": "{count} 天前",
|
||||
"today": "今天",
|
||||
"yesterday": "昨天"
|
||||
},
|
||||
"pagination": {
|
||||
"page": "第 {current} 頁,共 {total} 頁",
|
||||
"showing": "顯示 {from}-{to},共 {total} 筆",
|
||||
"per_page": "每頁顯示"
|
||||
}
|
||||
},
|
||||
|
||||
"layout": {
|
||||
"nav": {
|
||||
"dashboard": "戰情室",
|
||||
"monitor": "監控",
|
||||
"security": "安全",
|
||||
"deploy": "部署",
|
||||
"tickets": "工單",
|
||||
"billing": "帳單",
|
||||
"settings": "設定"
|
||||
},
|
||||
"sidebar": {
|
||||
"collapse": "收合側邊欄",
|
||||
"expand": "展開側邊欄"
|
||||
},
|
||||
"header": {
|
||||
"search_placeholder": "搜尋... (⌘K)",
|
||||
"notifications": "通知",
|
||||
"profile": "個人檔案",
|
||||
"logout": "登出"
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "© {year} 岑洋國際行銷有限公司",
|
||||
"version": "版本 {version}"
|
||||
}
|
||||
},
|
||||
|
||||
"dashboard": {
|
||||
"page_title": "戰情室",
|
||||
"page_description": "系統健康狀態總覽",
|
||||
|
||||
"host_card": {
|
||||
"title": "主機狀態",
|
||||
"label_ip": "IP 位址",
|
||||
"label_role": "角色",
|
||||
"label_cpu": "CPU",
|
||||
"label_memory": "記憶體",
|
||||
"label_disk": "磁碟",
|
||||
"label_last_seen": "最後更新",
|
||||
"status_online": "上線",
|
||||
"status_offline": "離線"
|
||||
},
|
||||
|
||||
"alert_panel": {
|
||||
"title": "即時告警",
|
||||
"btn_acknowledge": "確認",
|
||||
"btn_view_all": "查看全部",
|
||||
"empty_state": "目前沒有告警",
|
||||
"severity_info": "資訊",
|
||||
"severity_warning": "警告",
|
||||
"severity_critical": "嚴重"
|
||||
},
|
||||
|
||||
"approval_card": {
|
||||
"title": "待簽核項目",
|
||||
"btn_approve": "批准執行",
|
||||
"btn_reject": "拒絕",
|
||||
"btn_request_info": "詢問更多",
|
||||
"label_requester": "申請人",
|
||||
"label_blast_radius": "爆炸半徑",
|
||||
"label_signatures": "簽核進度",
|
||||
"label_expires_in": "剩餘時間",
|
||||
"label_ai_summary": "AI 摘要",
|
||||
"label_confidence": "信心度",
|
||||
"status_pending": "等待簽核",
|
||||
"status_approved": "已批准",
|
||||
"status_rejected": "已拒絕",
|
||||
"status_expired": "已過期",
|
||||
"blast_low": "低",
|
||||
"blast_medium": "中",
|
||||
"blast_high": "高",
|
||||
"blast_critical": "嚴重"
|
||||
},
|
||||
|
||||
"metrics": {
|
||||
"total_hosts": "主機總數",
|
||||
"active_alerts": "活躍告警",
|
||||
"pending_approvals": "待簽核",
|
||||
"deployments_today": "今日部署"
|
||||
}
|
||||
},
|
||||
|
||||
"ai_copilot": {
|
||||
"title": "AI 助手",
|
||||
"placeholder": "輸入問題或指令...",
|
||||
"btn_send": "送出",
|
||||
"btn_stop": "停止",
|
||||
"btn_clear": "清除對話",
|
||||
"thinking": "AI 思考中...",
|
||||
"error_offline": "AI 服務暫時不可用",
|
||||
"error_timeout": "回應超時,請重試",
|
||||
"suggestion_prefix": "建議",
|
||||
"action_prefix": "建議執行",
|
||||
"warning_destructive": "此操作具有破壞性,請謹慎執行"
|
||||
},
|
||||
|
||||
"command_palette": {
|
||||
"placeholder": "輸入指令...",
|
||||
"category_navigation": "導航",
|
||||
"category_action": "操作",
|
||||
"category_ai": "AI 功能",
|
||||
"category_settings": "設定",
|
||||
"no_results": "沒有符合的結果",
|
||||
"hint_shortcut": "快捷鍵"
|
||||
},
|
||||
|
||||
"errors": {
|
||||
"generic": "發生錯誤,請稍後再試",
|
||||
"network": "網路連線失敗",
|
||||
"unauthorized": "您沒有權限執行此操作",
|
||||
"not_found": "找不到請求的資源",
|
||||
"validation": "輸入資料驗證失敗",
|
||||
"server": "伺服器錯誤",
|
||||
"timeout": "請求超時",
|
||||
"rate_limited": "請求過於頻繁,請稍後再試"
|
||||
},
|
||||
|
||||
"validation": {
|
||||
"required": "此欄位為必填",
|
||||
"email": "請輸入有效的電子郵件",
|
||||
"min_length": "至少需要 {min} 個字元",
|
||||
"max_length": "最多 {max} 個字元",
|
||||
"pattern": "格式不正確"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### en.json
|
||||
|
||||
```json
|
||||
{
|
||||
"common": {
|
||||
"btn": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"view": "View",
|
||||
"back": "Back",
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"submit": "Submit",
|
||||
"reset": "Reset",
|
||||
"search": "Search",
|
||||
"filter": "Filter",
|
||||
"export": "Export",
|
||||
"import": "Import",
|
||||
"refresh": "Refresh"
|
||||
},
|
||||
"status": {
|
||||
"loading": "Loading...",
|
||||
"success": "Success",
|
||||
"error": "Failed",
|
||||
"pending": "Processing",
|
||||
"active": "Active",
|
||||
"inactive": "Inactive",
|
||||
"healthy": "Healthy",
|
||||
"warning": "Warning",
|
||||
"critical": "Critical",
|
||||
"unknown": "Unknown"
|
||||
}
|
||||
},
|
||||
|
||||
"dashboard": {
|
||||
"page_title": "War Room",
|
||||
"page_description": "System Health Overview",
|
||||
|
||||
"approval_card": {
|
||||
"title": "Pending Approvals",
|
||||
"btn_approve": "Approve",
|
||||
"btn_reject": "Reject",
|
||||
"btn_request_info": "Request Info",
|
||||
"label_blast_radius": "Blast Radius",
|
||||
"label_signatures": "Signatures",
|
||||
"status_pending": "Pending"
|
||||
}
|
||||
},
|
||||
|
||||
"ai_copilot": {
|
||||
"title": "AI Assistant",
|
||||
"placeholder": "Enter a question or command...",
|
||||
"thinking": "AI is thinking..."
|
||||
},
|
||||
|
||||
"errors": {
|
||||
"generic": "An error occurred. Please try again later.",
|
||||
"network": "Network connection failed",
|
||||
"unauthorized": "You don't have permission to perform this action"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 組件中使用
|
||||
|
||||
```tsx
|
||||
// apps/web/src/components/ApprovalCard.tsx
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export function ApprovalCard({ approval }: Props) {
|
||||
const t = useTranslations('dashboard.approval_card');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{t('title')}</h3>
|
||||
<p>{t('label_blast_radius')}: {t(`blast_${approval.blastRadius}`)}</p>
|
||||
<button>{t('btn_approve')}</button>
|
||||
<button>{t('btn_reject')}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 動態參數
|
||||
|
||||
```tsx
|
||||
// 使用變數
|
||||
const t = useTranslations('common.time');
|
||||
t('minutes_ago', { count: 5 }); // "5 分鐘前"
|
||||
|
||||
const t = useTranslations('common.pagination');
|
||||
t('showing', { from: 1, to: 20, total: 100 }); // "顯示 1-20,共 100 筆"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI 檢查規則
|
||||
|
||||
### 翻譯完整性檢查
|
||||
|
||||
```typescript
|
||||
// scripts/check-i18n.ts
|
||||
import zhTW from '../messages/zh-TW.json';
|
||||
import en from '../messages/en.json';
|
||||
|
||||
function getAllKeys(obj: object, prefix = ''): string[] {
|
||||
return Object.entries(obj).flatMap(([key, value]) => {
|
||||
const fullKey = prefix ? `${prefix}.${key}` : key;
|
||||
return typeof value === 'object'
|
||||
? getAllKeys(value, fullKey)
|
||||
: [fullKey];
|
||||
});
|
||||
}
|
||||
|
||||
const zhKeys = new Set(getAllKeys(zhTW));
|
||||
const enKeys = new Set(getAllKeys(en));
|
||||
|
||||
const missingInEn = [...zhKeys].filter(k => !enKeys.has(k));
|
||||
const missingInZh = [...enKeys].filter(k => !zhKeys.has(k));
|
||||
|
||||
if (missingInEn.length > 0) {
|
||||
console.error('❌ Missing in en.json:', missingInEn);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (missingInZh.length > 0) {
|
||||
console.error('❌ Missing in zh-TW.json:', missingInZh);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ All translations are complete');
|
||||
```
|
||||
|
||||
### PR Checklist
|
||||
|
||||
```markdown
|
||||
## i18n Checklist
|
||||
|
||||
- [ ] 新增的 UI 文字已加入 zh-TW.json
|
||||
- [ ] 新增的 UI 文字已加入 en.json
|
||||
- [ ] Key 命名遵循 `[頁面].[組件].[元素]` 格式
|
||||
- [ ] CI i18n 檢查通過
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 變更記錄
|
||||
|
||||
| 日期 | 版本 | 變更 | 作者 |
|
||||
|------|------|------|------|
|
||||
| 2026-03-20 | v1.0 | 初版建立 | CPO |
|
||||
|
||||
---
|
||||
|
||||
*此文件由 CPO 維護,前端開發者新增 UI 文字時必須遵守。*
|
||||
Reference in New Issue
Block a user