依 R5 五輪決策把 visionA-local 從「Wails 內嵌 Next.js」重構為「Wails
本機伺服器控制台 + 瀏覽器 Web UI」模式(類比 Docker Desktop / Ollama)。
程式碼變動
- M8-1 砍 yt-dlp 全套(後端 resolver / URL handler / 前端 URL tab /
Makefile vendor / installer / bootstrap / CI workflow,-555 行)
- M8-2 砍 Mock 模式全套(driver/mock、mock_camera、Settings runtimeMode、
VISIONA_MOCK 環境變數,-528 行)
- M8-3 ffmpeg 從 GPL 切換到 LGPL 混合方案:Windows/Linux 用 BtbN 現成
LGPL binary,macOS 自 build minimal decoder-only 進 git
(vendor/ffmpeg/macos/ffmpeg 5.7MB + ffprobe 5.6MB,比 GPL 版省 85% 空間)
- M8-4 Wails Server Controller:state machine、log ring buffer 2000 行、
preferences.json atomic write、boot-id、Gin SkipPaths、shutdown 7+1 秒、
notify_*.go 三平台 OS 通知、watchServer 改 Error state 不 os.Exit
- M8-4b 啟動階段管線 R5-E:6 階段進度 event、20s soft / 60s hard timeout、
stage 5/6 skip 規則、sentinel file、RestartStartupSequence 5 步驟
- M8-5 Wails 控制台 vanilla HTML/JS/CSS(9 檔 ~2012 行)取代 M7-B splash:
state 視覺、log panel、startup progress panel、Stage 6 manual CTA
pulse、shutdown modal、Settings、Dark Mode、i18n 中英雙語
- M8-6 上傳影片副檔名擴充(mp4/avi/mov/mpeg/mpg)
- M8-7 Web UI Server Offline Overlay(role=alertdialog + focus trap +
wsEverConnected 容錯 + Page Visibility)
- M8-8 CORS middleware(127.0.0.1/localhost only + suffix attack 防護)+
ws/origin.go 獨立 WebSocket CheckOrigin 避 package cycle
- MAJ-4 server:shutdown-imminent WebSocket broadcast 機制
(/ws/system endpoint + notifyShutdownImminent helper)
- M8-9 Boot-ID + 瀏覽器 tab 自動重連(sessionStorage loop guard)
品質
- ~105+ 新 unit test + race detector (-count=2) 全綠
- 10 個 milestone 全部通過 Reviewer 審查
- 三方 v2 + v2.1 文件(PRD / Design Spec / TDD)+ 交叉互審紀錄
收錄在 .autoflow/
交付前待處理(M8-10)
- 重跑 make payload-macos 把舊 GPL 77MB binary 換成新 LGPL
- 三平台 end-to-end build 驗證
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
431 lines
15 KiB
TypeScript
431 lines
15 KiB
TypeScript
import type { TranslationDict } from './types';
|
||
|
||
export const zhTW: TranslationDict = {
|
||
common: {
|
||
save: '儲存',
|
||
cancel: '取消',
|
||
delete: '刪除',
|
||
back: '返回',
|
||
close: '關閉',
|
||
done: '完成',
|
||
retry: '重試',
|
||
connect: '連線',
|
||
disconnect: '中斷連線',
|
||
manage: '管理',
|
||
view: '檢視',
|
||
upload: '上傳',
|
||
uploading: '上傳中...',
|
||
loading: '載入中...',
|
||
search: '搜尋',
|
||
reset: '重設',
|
||
start: '開始',
|
||
stop: '停止',
|
||
na: 'N/A',
|
||
custom: '自訂',
|
||
},
|
||
nav: {
|
||
dashboard: '儀表板',
|
||
modelLibrary: '模型庫',
|
||
devices: '裝置',
|
||
workspace: '工作區',
|
||
settings: '設定',
|
||
appName: 'visionA Local',
|
||
version: 'visionA Local v0.1.0',
|
||
platformTitle: 'visionA Local — 邊緣 AI 工作站',
|
||
serverConnected: '伺服器已連線',
|
||
serverDisconnected: '伺服器未連線',
|
||
},
|
||
workspace: {
|
||
title: '工作區',
|
||
subtitle: '選擇裝置以開始',
|
||
noConnectedDevice: '沒有已連線的裝置',
|
||
noConnectedDeviceDesc: '請先連線一台裝置,再開始推論。',
|
||
goToDevices: '前往裝置頁',
|
||
},
|
||
dashboard: {
|
||
title: '儀表板',
|
||
subtitle: 'visionA 工作站總覽',
|
||
models: '模型',
|
||
devices: '裝置',
|
||
connected: '已連線',
|
||
flashes: '燒錄次數',
|
||
quickActions: '快速操作',
|
||
browseModels: '瀏覽模型',
|
||
manageDevices: '管理裝置',
|
||
uploadModel: '上傳模型',
|
||
recentActivity: '近期活動',
|
||
noActivity: '尚無活動紀錄。',
|
||
connectedDevices: '已連線裝置',
|
||
noConnectedDevices: '目前沒有已連線的裝置。',
|
||
justNow: '剛剛',
|
||
minutesAgo: '{n} 分鐘前',
|
||
hoursAgo: '{n} 小時前',
|
||
daysAgo: '{n} 天前',
|
||
},
|
||
devices: {
|
||
title: '裝置',
|
||
subtitle: '管理你的 Edge AI 裝置',
|
||
scan: '掃描裝置',
|
||
scanning: '掃描中...',
|
||
noDevices: '未偵測到 Kneron 裝置。請連接 KL520 / KL720 後按「掃描」。',
|
||
type: '類型',
|
||
firmware: '韌體',
|
||
flashedModel: '已燒錄模型',
|
||
status: {
|
||
detected: '已偵測',
|
||
connecting: '連線中...',
|
||
connected: '已連線',
|
||
disconnecting: '斷線中...',
|
||
flashing: '燒錄中',
|
||
inferencing: '推論中',
|
||
error: '錯誤',
|
||
disconnected: '已中斷',
|
||
},
|
||
health: {
|
||
title: '裝置健康狀態',
|
||
status: '狀態',
|
||
firmwareVersion: '韌體版本',
|
||
uptime: '運行時間',
|
||
lastSeen: '最後活動',
|
||
},
|
||
connectionLog: {
|
||
title: '連線歷史',
|
||
noEvents: '尚無連線事件紀錄。',
|
||
connected: '已連線',
|
||
disconnected: '已中斷',
|
||
},
|
||
settings: {
|
||
title: '裝置設定',
|
||
alias: '自訂名稱',
|
||
aliasPlaceholder: '例如:實驗室裝置 #1',
|
||
notes: '備註',
|
||
notesPlaceholder: '新增關於此裝置的備註...',
|
||
saveSettings: '儲存設定',
|
||
settingsSaved: '裝置設定已儲存',
|
||
},
|
||
detail: {
|
||
deviceInfo: '裝置資訊',
|
||
id: 'ID',
|
||
type: '類型',
|
||
firmware: '韌體',
|
||
port: '連接埠',
|
||
modelStatus: '模型狀態',
|
||
readyForInference: '裝置已準備好進行推論。開啟工作區即可開始。',
|
||
noModelFlashed: '此裝置尚未燒錄模型。請使用「燒錄模型」按鈕部署模型。',
|
||
openWorkspace: '開啟工作區',
|
||
},
|
||
flash: {
|
||
flashModel: '燒錄模型',
|
||
flashToDevice: '燒錄模型至裝置',
|
||
selectModel: '選擇模型',
|
||
hardwareIncompatible: '硬體不相容',
|
||
incompatibleDesc: '此模型不支援 {device}。',
|
||
incompatibleCannotFlash: '不相容 — 無法燒錄',
|
||
startFlash: '開始燒錄',
|
||
flashFailed: '燒錄失敗',
|
||
preparingFlash: '準備燒錄中...',
|
||
flashComplete: '燒錄完成!',
|
||
},
|
||
},
|
||
models: {
|
||
title: '模型庫',
|
||
subtitle: '瀏覽並選擇 AI 模型進行部署',
|
||
noModelsFound: '未找到符合條件的模型。',
|
||
compareModels: '比較模型',
|
||
exitCompare: '離開比較',
|
||
uploadModel: '上傳模型',
|
||
accuracy: '準確率',
|
||
fps: 'FPS',
|
||
size: '大小',
|
||
hardware: '硬體',
|
||
latency: '延遲',
|
||
framework: '框架',
|
||
inputSize: '輸入大小',
|
||
quantization: '量化',
|
||
version: '版本',
|
||
author: '作者',
|
||
license: '授權',
|
||
labels: '標籤',
|
||
performance: '效能',
|
||
specifications: '規格',
|
||
hardwareCompatibility: '硬體相容性',
|
||
metadata: '後設資料',
|
||
deployToDevice: '部署至裝置',
|
||
deleteCustomModel: '刪除自訂模型',
|
||
deleteConfirm: '確定要刪除「{name}」嗎?此操作無法復原。',
|
||
comparison: {
|
||
title: '模型比較',
|
||
metric: '指標',
|
||
selected: '已選取 {n} 個模型',
|
||
compare: '比較',
|
||
clear: '清除',
|
||
taskType: '任務類型',
|
||
modelSize: '模型大小',
|
||
},
|
||
filters: {
|
||
searchPlaceholder: '搜尋模型...',
|
||
taskType: '任務類型',
|
||
allTypes: '所有類型',
|
||
allHardware: '所有硬體',
|
||
},
|
||
upload: {
|
||
title: '上傳自訂模型',
|
||
modelFile: '模型檔案 (.nef) *',
|
||
clickToSelect: '點擊選擇 .nef 檔案',
|
||
modelName: '模型名稱 *',
|
||
modelNamePlaceholder: '例如:My Custom YOLOv5',
|
||
description: '描述',
|
||
descriptionPlaceholder: '選填描述',
|
||
taskType: '任務類型 *',
|
||
taskTypePlaceholder: '選擇任務類型',
|
||
objectDetection: '物件偵測',
|
||
classification: '分類',
|
||
segmentation: '語意分割',
|
||
poseEstimation: '姿態估計',
|
||
labels: '標籤 *(以逗號分隔)',
|
||
labelsPlaceholder: '例如:person, car, dog, cat',
|
||
inputWidth: '輸入寬度',
|
||
inputHeight: '輸入高度',
|
||
quantization: '量化',
|
||
errors: {
|
||
noFile: '請選擇 .nef 檔案',
|
||
noName: '請輸入模型名稱',
|
||
noTaskType: '請選擇任務類型',
|
||
noLabels: '至少需要一個標籤',
|
||
uploadFailed: '上傳失敗,請重試。',
|
||
},
|
||
},
|
||
},
|
||
camera: {
|
||
camera: '攝影機',
|
||
image: '圖片',
|
||
video: '影片',
|
||
startCamera: '啟動攝影機',
|
||
stopCamera: '停止攝影機',
|
||
stopImage: '停止圖片',
|
||
stopVideo: '停止影片',
|
||
stopBatch: '停止批次',
|
||
selectImage: '選擇圖片',
|
||
selectImages: '選擇圖片',
|
||
selectVideo: '選擇影片',
|
||
jpgPng: 'JPG, PNG',
|
||
jpgPngMultiple: 'JPG, PNG(支援多選)',
|
||
mp4AviMov: 'MP4 / AVI / MOV / MPEG / MPG',
|
||
noInputSource: '無輸入來源',
|
||
selectSourceHint: '請在上方選擇攝影機、圖片或影片',
|
||
uploadedImage: '已上傳圖片',
|
||
videoPlayback: '影片播放',
|
||
cameraFeed: '攝影機畫面',
|
||
dropImagesHere: '將圖片拖放至此',
|
||
batchProcessing: '處理中',
|
||
batchImages: '批次圖片',
|
||
noCameraDetected: '未偵測到攝影機,請使用圖片或影片進行推論。',
|
||
},
|
||
inference: {
|
||
confidenceFilter: '信心度篩選',
|
||
confidenceThreshold: '信心度門檻',
|
||
classificationResults: '分類結果',
|
||
noResultsAboveThreshold: '沒有超過門檻的結果',
|
||
details: '詳細資訊',
|
||
model: '模型',
|
||
task: '任務',
|
||
latency: '延遲',
|
||
fps: 'FPS',
|
||
workspace: '工作區',
|
||
startInference: '開始推論',
|
||
stopInference: '停止推論',
|
||
batchProgress: '批次進度',
|
||
processed: '已處理',
|
||
currentImage: '檢視中',
|
||
totalImages: '總共',
|
||
videoProgress: '影片進度',
|
||
frames: '幀',
|
||
framesProcessed: '已處理幀數',
|
||
},
|
||
settings: {
|
||
title: '設定',
|
||
subtitle: '應用程式設定',
|
||
tabs: {
|
||
general: '一般',
|
||
hardware: '硬體',
|
||
models: '模型',
|
||
advanced: '進階',
|
||
},
|
||
general: {
|
||
title: '一般',
|
||
language: '語言',
|
||
languageFollowSystem: '跟隨系統',
|
||
languageFollowSystemLabel: '跟隨系統(目前:{detected})',
|
||
languageRestartHint: '語言切換會立即生效,但原生系統選單可能需重啟 App 才會更新。',
|
||
theme: '主題',
|
||
themeFollowSystem: '跟隨系統',
|
||
themeFollowSystemHint: '深色模式會自動跟隨系統設定。',
|
||
themeCurrentLight: '目前:淺色',
|
||
themeCurrentDark: '目前:深色',
|
||
dataDirectory: '資料目錄',
|
||
dataDirectoryHint: '日誌、設定與自訂模型皆存放於此目錄。',
|
||
},
|
||
hardware: {
|
||
title: '硬體',
|
||
pythonMode: 'Python 執行模式',
|
||
pythonModeAuto: '自動(優先內嵌)',
|
||
pythonModeBundled: '內嵌(推薦)',
|
||
pythonModeSystem: '系統 Python',
|
||
pythonModeHint: '此階段為唯讀顯示,後續版本會開放切換。',
|
||
serverPort: 'Server Port',
|
||
serverPortHint: '後端實際監聽的連接埠。',
|
||
},
|
||
models: {
|
||
title: '模型',
|
||
presetModels: '預置模型',
|
||
presetModelsEmpty: '目前沒有預置模型。',
|
||
presetModelsLoadFailed: '無法載入模型清單。',
|
||
uploadPath: '上傳路徑',
|
||
uploadPathHint: '你上傳的自訂模型會存放於此。',
|
||
},
|
||
advanced: {
|
||
title: '進階',
|
||
viewLogs: '查看伺服器日誌',
|
||
viewLogsHint: '檢視近期的伺服器日誌紀錄。',
|
||
restartServer: '重啟伺服器',
|
||
restartServerHint: '所有進行中的推論會停止,介面會自動重新連線。',
|
||
about: '關於',
|
||
resetAll: '重置所有設定',
|
||
resetAllHint: '將所有偏好恢復成預設值,不會刪除資料。',
|
||
},
|
||
serverConfig: '伺服器設定',
|
||
backendUrl: '後端 URL',
|
||
backendUrlPlaceholder: '例如:http://192.168.1.100:3721',
|
||
backendUrlHint: '留空表示使用同源模式(前端嵌入 Go binary 時)。',
|
||
backendUrlSaved: '後端 URL 已更新',
|
||
saveBackendUrl: '儲存',
|
||
apiUrl: 'API URL',
|
||
wsUrl: 'WebSocket URL',
|
||
serverNote: '伺服器位址於建置時設定。',
|
||
appearance: '外觀',
|
||
theme: '主題',
|
||
themeLight: '淺色',
|
||
themeDark: '深色',
|
||
language: '語言',
|
||
displayLanguage: '顯示語言',
|
||
languageZhTW: '繁體中文',
|
||
languageEn: 'English',
|
||
about: '關於',
|
||
versionLabel: '版本',
|
||
platform: 'visionA Local — 邊緣 AI 工作站',
|
||
resetToDefaults: '恢復預設值',
|
||
resetSuccess: '設定已恢復預設值',
|
||
serverLogs: {
|
||
title: '伺服器日誌',
|
||
clear: '清除',
|
||
filter: '篩選',
|
||
autoScroll: '自動捲動',
|
||
noLogs: '尚無日誌紀錄。伺服器產生日誌後將即時顯示。',
|
||
entries: '筆紀錄',
|
||
},
|
||
serverStatus: {
|
||
title: '伺服器狀態',
|
||
uptimeLabel: '運行時間',
|
||
goroutinesLabel: 'Goroutine 數',
|
||
heapAllocLabel: '堆積記憶體',
|
||
sysMemLabel: '系統記憶體',
|
||
gcCyclesLabel: 'GC 次數',
|
||
nextGcLabel: '下次 GC',
|
||
versionLabel: '版本',
|
||
buildTimeLabel: '建置時間',
|
||
platformLabel: '平台',
|
||
goVersionLabel: 'Go 版本',
|
||
depsTitle: '相依套件',
|
||
depAvailable: '可用',
|
||
depNotFound: '未安裝',
|
||
restart: '重啟伺服器',
|
||
restarting: '重啟中...',
|
||
waitingForServer: '等待伺服器...',
|
||
serverBack: '伺服器已恢復',
|
||
restartConfirmTitle: '確定重啟伺服器?',
|
||
restartConfirmDesc: '伺服器將重新啟動。所有進行中的推論工作階段將被停止,介面將自動重新連線。',
|
||
restartConfirm: '重啟',
|
||
offline: '伺服器未連線',
|
||
offlineDesc: '無法連線到後端伺服器。請確認伺服器是否正在運行。',
|
||
backendAddress: '後端位址',
|
||
reconnect: '重新連線',
|
||
reconnecting: '重新連線中...',
|
||
},
|
||
},
|
||
onboarding: {
|
||
welcome: '歡迎使用 visionA Local!',
|
||
step1Title: '連接你的裝置',
|
||
step1Desc: '插入 Kneron USB Dongle 並掃描裝置。平台將自動偵測支援的硬體。',
|
||
step2Title: '選擇 AI 模型',
|
||
step2Desc: '瀏覽模型庫並將預訓練模型燒錄到你的裝置。模型已針對邊緣推論進行最佳化。',
|
||
step3Title: '開始推論!',
|
||
step3Desc: '開啟工作區,選擇攝影機、圖片或影片,在你的邊緣裝置上執行即時 AI 推論。',
|
||
next: '下一步',
|
||
previous: '上一步',
|
||
getStarted: '開始使用',
|
||
skipTour: '跳過導覽',
|
||
},
|
||
emptyState: {
|
||
devicesTitle: '未偵測到裝置',
|
||
devicesDesc: '請插入 Kneron USB Dongle 並點擊掃描來偵測裝置。',
|
||
devicesScan: '掃描裝置',
|
||
modelsTitle: '探索 AI 模型',
|
||
modelsDesc: '瀏覽預訓練模型庫或上傳你自己的自訂模型。',
|
||
modelsBrowse: '瀏覽模型',
|
||
modelsUpload: '上傳模型',
|
||
},
|
||
tour: {
|
||
step1Title: '掃描裝置',
|
||
step1Desc: '點擊此按鈕掃描已連接的 Kneron USB 裝置。',
|
||
step1Pending: '請先點擊「掃描裝置」,目前尚未偵測到任何裝置。',
|
||
step2Title: '連線裝置',
|
||
step2Desc: '點擊「連線」與偵測到的裝置建立連線。',
|
||
step2Pending: '請先點擊裝置上的「連線」按鈕。',
|
||
step3Title: '管理裝置',
|
||
step3Desc: '點擊「管理」檢視裝置詳情、燒錄模型及開啟工作區。',
|
||
step3Pending: '請先連線裝置,才會顯示「管理」按鈕。',
|
||
step4Title: '燒錄模型',
|
||
step4Desc: '從模型庫選擇 AI 模型並燒錄至你的裝置。',
|
||
step4Pending: '請先將模型燒錄至裝置。',
|
||
step5Title: '開啟工作區',
|
||
step5Desc: '開啟工作區設定輸入來源並執行推論。',
|
||
step5Pending: '請先燒錄模型,完成後會出現「開啟工作區」按鈕。',
|
||
step6Title: '開始推論',
|
||
step6Desc: '選擇攝影機或上傳圖片/影片,開始即時 AI 推論!',
|
||
step6Pending: '請開始推論以完成導覽。',
|
||
next: '下一步',
|
||
prev: '上一步',
|
||
done: '完成',
|
||
exit: '離開導覽',
|
||
of: '/',
|
||
helpTooltip: '開始導覽',
|
||
},
|
||
errors: {
|
||
serverDisconnected: '伺服器連線中斷',
|
||
serverReconnected: '伺服器已重新連線',
|
||
failedToLoadDevices: '無法載入裝置列表',
|
||
deviceConnected: '裝置已連線',
|
||
deviceDisconnected: '裝置已中斷連線',
|
||
modelUploadSuccess: '模型上傳成功',
|
||
modelUploadFailed: '上傳失敗,請檢查網路連線',
|
||
modelDeleted: '模型已刪除',
|
||
modelDeleteFailed: '刪除失敗',
|
||
cannotStopStream: '無法停止串流',
|
||
imageUploadFailed: '圖片上傳失敗',
|
||
videoUploadFailed: '影片上傳失敗',
|
||
batchUploadFailed: '批次圖片上傳失敗',
|
||
unexpectedError: '發生未預期的錯誤',
|
||
},
|
||
offline: {
|
||
title: 'Local Server 已離線',
|
||
subtitle: {
|
||
quit: 'visionA Local 已結束,請重新開啟應用程式。',
|
||
restart: 'Server 正在重新啟動,請稍候…',
|
||
healthcheck: '偵測到 Server 無回應,正在重試連線…',
|
||
},
|
||
retryButton: '重試連線',
|
||
retrying: '正在重試中…',
|
||
helpText: '如要離開本頁,請直接關閉此分頁。',
|
||
},
|
||
};
|