A 階段第四個 milestone、完整 Frontend FW UI(badge / modal / 8 種 reason 復原)+ backend WS hot-fix(補對稱於 flash 的 firmware WS endpoint)。 Frontend(13 修改 / 7 新檔): - 新 firmware/ component group (badge / upgrade-button / upgrade-dialog 4-phase / progress-view / error-view 8-reason / index) - Zustand store (firmware-store.ts) + WS hook (use-firmware-progress.ts) 對齊既有 useFlashProgress pattern - DeviceCard 整合 FirmwareBadge + FirmwareUpgradeButton - i18n: settings.firmware.* namespace (對齊 Design Spec §9 SoT) + devices.card.fwBadge.* (zh-TW + en, 57 leaf keys × 2 lang = 114 strings) - toast.ts ToastOptions interface (duration param) - types/device.ts: FW 衍生欄位 + FirmwareStage/Reason/ProgressEvent/ActiveTask types Backend WS hot-fix (3 檔): - ws/firmware_ws.go (50 行、純對稱 flash_ws.go) - ws/firmware_ws_test.go (165 行、2 smoke tests: broadcast + room isolation) - router.go: GET /ws/devices/:id/firmware-progress 關鍵設計: - R-FW-11 緩解: upgrading phase modal 不可關 (onInteractOutside/onEscapeKeyDown preventDefault + 隱藏 X) - 多裝置隔離 defense in depth: store handleEvent activeDeviceId mismatch 直接 return - 8 種 reason → 4 種 UX (recoverable/destructive/brick 警告/contactSupport) - ContactSupport mailto handler (RFC 6068 + encodeURIComponent) Reviewer 兩輪審查: - Round 1: 0 Critical / 3 Major / 8 Minor / 5 Suggestion - Round 2: 0 Critical / 0 Major / 0 Minor / 2 Suggestion(接受方案 A、不需 frontend 第 3 輪) - MJ1 i18n namespace 採方案 A (settings.firmware.*)、Design SoT 優先、Reviewer 同意 測試: - pnpm test --run: 60 tests pass (32 firmware: 22 store + 10 badge + 新 9 error-view + 19 既有) - npx tsc --noEmit: 0 error - pnpm build: production build 成功 - go test ./internal/api/ws/... -race: 1.964s 全綠 - pnpm lint firmware/: 0 hit (17 既有 lint 問題不屬 M9-4、follow-up) 未做(範圍外): - Settings 韌體面板 (M9-12 B 階段) - 手動降版 UI (M9-12) - 版本切換 dropdown (B 階段) - Wails 控制台 force-quit modal (M9-4.5) A 階段 MVP 後端 + 前端開發全部完成、剩 M9-4.5 (SIGTERM + Wails OnBeforeClose) + M9-5 (三平台實機驗證) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
506 lines
18 KiB
TypeScript
506 lines
18 KiB
TypeScript
import type { TranslationDict } from './types';
|
|
|
|
export const en: TranslationDict = {
|
|
common: {
|
|
save: 'Save',
|
|
cancel: 'Cancel',
|
|
delete: 'Delete',
|
|
back: 'Back',
|
|
close: 'Close',
|
|
done: 'Done',
|
|
retry: 'Retry',
|
|
connect: 'Connect',
|
|
disconnect: 'Disconnect',
|
|
manage: 'Manage',
|
|
view: 'View',
|
|
upload: 'Upload',
|
|
uploading: 'Uploading...',
|
|
loading: 'Loading...',
|
|
search: 'Search',
|
|
reset: 'Reset',
|
|
start: 'Start',
|
|
stop: 'Stop',
|
|
na: 'N/A',
|
|
custom: 'Custom',
|
|
},
|
|
nav: {
|
|
dashboard: 'Dashboard',
|
|
modelLibrary: 'Model Library',
|
|
devices: 'Devices',
|
|
workspace: 'Workspace',
|
|
settings: 'Settings',
|
|
appName: 'visionA Local',
|
|
version: 'visionA Local v0.1.0',
|
|
platformTitle: 'visionA Local — Edge AI Workspace',
|
|
serverConnected: 'Server Connected',
|
|
serverDisconnected: 'Server Disconnected',
|
|
},
|
|
workspace: {
|
|
title: 'Workspace',
|
|
subtitle: 'Select a device to begin',
|
|
noConnectedDevice: 'No connected device',
|
|
noConnectedDeviceDesc: 'Connect a device to start inference.',
|
|
goToDevices: 'Go to Devices',
|
|
},
|
|
dashboard: {
|
|
title: 'Dashboard',
|
|
subtitle: 'Overview of your visionA workspace',
|
|
models: 'Models',
|
|
devices: 'Devices',
|
|
connected: 'Connected',
|
|
flashes: 'Flashes',
|
|
quickActions: 'Quick Actions',
|
|
browseModels: 'Browse Models',
|
|
manageDevices: 'Manage Devices',
|
|
uploadModel: 'Upload Model',
|
|
recentActivity: 'Recent Activity',
|
|
noActivity: 'No activity recorded yet.',
|
|
connectedDevices: 'Connected Devices',
|
|
noConnectedDevices: 'No connected devices.',
|
|
justNow: 'Just now',
|
|
minutesAgo: '{n}m ago',
|
|
hoursAgo: '{n}h ago',
|
|
daysAgo: '{n}d ago',
|
|
},
|
|
devices: {
|
|
title: 'Devices',
|
|
subtitle: 'Manage your edge AI devices',
|
|
scan: 'Scan Devices',
|
|
scanning: 'Scanning...',
|
|
noDevices: 'No Kneron devices detected. Please connect a KL520 / KL720 device and click "Scan".',
|
|
type: 'Type',
|
|
firmware: 'Firmware',
|
|
flashedModel: 'Flashed Model',
|
|
status: {
|
|
detected: 'Detected',
|
|
connecting: 'Connecting...',
|
|
connected: 'Connected',
|
|
disconnecting: 'Disconnecting...',
|
|
flashing: 'Flashing',
|
|
inferencing: 'Inferring',
|
|
error: 'Error',
|
|
disconnected: 'Disconnected',
|
|
},
|
|
health: {
|
|
title: 'Device Health',
|
|
status: 'Status',
|
|
firmwareVersion: 'Firmware Version',
|
|
uptime: 'Uptime',
|
|
lastSeen: 'Last Seen',
|
|
},
|
|
connectionLog: {
|
|
title: 'Connection History',
|
|
noEvents: 'No connection events recorded.',
|
|
connected: 'Connected',
|
|
disconnected: 'Disconnected',
|
|
},
|
|
settings: {
|
|
title: 'Device Settings',
|
|
alias: 'Custom Name',
|
|
aliasPlaceholder: 'e.g., Lab Device #1',
|
|
notes: 'Notes',
|
|
notesPlaceholder: 'Add notes about this device...',
|
|
saveSettings: 'Save Settings',
|
|
settingsSaved: 'Device settings saved',
|
|
},
|
|
detail: {
|
|
deviceInfo: 'Device Info',
|
|
id: 'ID',
|
|
type: 'Type',
|
|
firmware: 'Firmware',
|
|
port: 'Port',
|
|
modelStatus: 'Model Status',
|
|
readyForInference: 'Device is ready for inference. Open the workspace to start.',
|
|
noModelFlashed: 'No model has been flashed to this device yet. Use the Flash Model button to deploy a model.',
|
|
openWorkspace: 'Open Workspace',
|
|
},
|
|
flash: {
|
|
flashModel: 'Flash Model',
|
|
flashToDevice: 'Flash Model to Device',
|
|
selectModel: 'Select a model',
|
|
hardwareIncompatible: 'Hardware Incompatible',
|
|
incompatibleDesc: 'This model does not support {device}.',
|
|
incompatibleCannotFlash: 'Incompatible \u2014 Cannot Flash',
|
|
startFlash: 'Start Flash',
|
|
flashFailed: 'Flash Failed',
|
|
preparingFlash: 'Preparing flash...',
|
|
flashComplete: 'Flash complete!',
|
|
},
|
|
card: {
|
|
fwBadge: {
|
|
tooltipCurrent: 'Firmware is up to date ({version}).',
|
|
tooltipOlder: 'Firmware is older. Upgrade recommended.',
|
|
tooltipLegacy: 'Legacy KDP1 firmware. Upgrade to KDP2 for full feature support.',
|
|
tooltipUnknown: 'Firmware status unknown. Please rescan the device.',
|
|
deepLinkA11y: 'Firmware status \u2014 {deviceName}',
|
|
},
|
|
},
|
|
},
|
|
models: {
|
|
title: 'Model Library',
|
|
subtitle: 'Browse and select AI models for deployment',
|
|
noModelsFound: 'No models found matching your criteria.',
|
|
compareModels: 'Compare Models',
|
|
exitCompare: 'Exit Compare',
|
|
uploadModel: 'Upload Model',
|
|
accuracy: 'Accuracy',
|
|
fps: 'FPS',
|
|
size: 'Size',
|
|
hardware: 'Hardware',
|
|
latency: 'Latency',
|
|
framework: 'Framework',
|
|
inputSize: 'Input Size',
|
|
quantization: 'Quantization',
|
|
version: 'Version',
|
|
author: 'Author',
|
|
license: 'License',
|
|
labels: 'Labels',
|
|
performance: 'Performance',
|
|
specifications: 'Specifications',
|
|
hardwareCompatibility: 'Hardware Compatibility',
|
|
metadata: 'Metadata',
|
|
deployToDevice: 'Deploy to Device',
|
|
deleteCustomModel: 'Delete Custom Model',
|
|
deleteConfirm: 'Are you sure you want to delete "{name}"? This action cannot be undone.',
|
|
comparison: {
|
|
title: 'Model Comparison',
|
|
metric: 'Metric',
|
|
selected: '{n} models selected',
|
|
compare: 'Compare',
|
|
clear: 'Clear',
|
|
taskType: 'Task Type',
|
|
modelSize: 'Model Size',
|
|
},
|
|
filters: {
|
|
searchPlaceholder: 'Search models...',
|
|
taskType: 'Task Type',
|
|
allTypes: 'All Types',
|
|
allHardware: 'All Hardware',
|
|
},
|
|
upload: {
|
|
title: 'Upload Custom Model',
|
|
modelFile: 'Model File (.nef) *',
|
|
clickToSelect: 'Click to select .nef file',
|
|
modelName: 'Model Name *',
|
|
modelNamePlaceholder: 'e.g., My Custom YOLOv5',
|
|
description: 'Description',
|
|
descriptionPlaceholder: 'Optional description',
|
|
taskType: 'Task Type *',
|
|
taskTypePlaceholder: 'Select task type',
|
|
objectDetection: 'Object Detection',
|
|
classification: 'Classification',
|
|
segmentation: 'Segmentation',
|
|
poseEstimation: 'Pose Estimation',
|
|
labels: 'Labels * (comma separated)',
|
|
labelsPlaceholder: 'e.g., person, car, dog, cat',
|
|
inputWidth: 'Input Width',
|
|
inputHeight: 'Input Height',
|
|
quantization: 'Quantization',
|
|
errors: {
|
|
noFile: 'Please select a .nef file',
|
|
noName: 'Model name is required',
|
|
noTaskType: 'Task type is required',
|
|
noLabels: 'At least one label is required',
|
|
uploadFailed: 'Upload failed. Please try again.',
|
|
},
|
|
},
|
|
},
|
|
camera: {
|
|
camera: 'Camera',
|
|
image: 'Image',
|
|
video: 'Video',
|
|
startCamera: 'Start Camera',
|
|
stopCamera: 'Stop Camera',
|
|
stopImage: 'Stop Image',
|
|
stopVideo: 'Stop Video',
|
|
stopBatch: 'Stop Batch',
|
|
selectImage: 'Select Image',
|
|
selectImages: 'Select Images',
|
|
selectVideo: 'Select Video',
|
|
jpgPng: 'JPG, PNG',
|
|
jpgPngMultiple: 'JPG, PNG (multiple)',
|
|
mp4AviMov: 'MP4 / AVI / MOV / MPEG / MPG',
|
|
noInputSource: 'No input source',
|
|
selectSourceHint: 'Select a camera, image, or video above',
|
|
uploadedImage: 'Uploaded Image',
|
|
videoPlayback: 'Video Playback',
|
|
cameraFeed: 'Camera Feed',
|
|
dropImagesHere: 'Drop images here',
|
|
batchProcessing: 'Processing',
|
|
batchImages: 'Batch Images',
|
|
noCameraDetected: 'No camera detected. Use image or video for inference.',
|
|
},
|
|
inference: {
|
|
confidenceFilter: 'Confidence Filter',
|
|
confidenceThreshold: 'Confidence Threshold',
|
|
classificationResults: 'Classification Results',
|
|
noResultsAboveThreshold: 'No results above threshold',
|
|
details: 'Details',
|
|
model: 'Model',
|
|
task: 'Task',
|
|
latency: 'Latency',
|
|
fps: 'FPS',
|
|
workspace: 'Workspace',
|
|
startInference: 'Start Inference',
|
|
stopInference: 'Stop Inference',
|
|
batchProgress: 'Batch Progress',
|
|
processed: 'Processed',
|
|
currentImage: 'Viewing',
|
|
totalImages: 'Total',
|
|
videoProgress: 'Video Progress',
|
|
frames: 'frames',
|
|
framesProcessed: 'Frames Processed',
|
|
},
|
|
settings: {
|
|
title: 'Settings',
|
|
subtitle: 'Application settings',
|
|
tabs: {
|
|
general: 'General',
|
|
hardware: 'Hardware',
|
|
models: 'Models',
|
|
advanced: 'Advanced',
|
|
},
|
|
general: {
|
|
title: 'General',
|
|
language: 'Language',
|
|
languageFollowSystem: 'Follow system',
|
|
languageFollowSystemLabel: 'Follow system ({detected})',
|
|
languageRestartHint: 'Language changes apply immediately in the app, but native OS menus may need a restart.',
|
|
theme: 'Theme',
|
|
themeFollowSystem: 'Follow system',
|
|
themeFollowSystemHint: 'Dark mode follows your system setting.',
|
|
themeCurrentLight: 'Currently: Light',
|
|
themeCurrentDark: 'Currently: Dark',
|
|
dataDirectory: 'Data Directory',
|
|
dataDirectoryHint: 'Logs, settings and custom models are stored here.',
|
|
},
|
|
hardware: {
|
|
title: 'Hardware',
|
|
pythonMode: 'Python Runtime',
|
|
pythonModeAuto: 'Auto (prefer bundled)',
|
|
pythonModeBundled: 'Bundled (recommended)',
|
|
pythonModeSystem: 'System Python',
|
|
pythonModeHint: 'Read-only in this milestone. Switching will be available in a later release.',
|
|
serverPort: 'Server Port',
|
|
serverPortHint: 'Actual port the backend is listening on.',
|
|
},
|
|
models: {
|
|
title: 'Models',
|
|
presetModels: 'Preset Models',
|
|
presetModelsEmpty: 'No preset models available.',
|
|
presetModelsLoadFailed: 'Could not load model list.',
|
|
uploadPath: 'Upload Path',
|
|
uploadPathHint: 'Custom models you upload are stored here.',
|
|
},
|
|
advanced: {
|
|
title: 'Advanced',
|
|
viewLogs: 'View Server Logs',
|
|
viewLogsHint: 'Inspect recent server log entries.',
|
|
restartServer: 'Restart Server',
|
|
restartServerHint: 'All active inference sessions will stop. The UI reconnects automatically.',
|
|
about: 'About',
|
|
resetAll: 'Reset All Settings',
|
|
resetAllHint: 'Restore all preferences to their defaults. This does not delete data.',
|
|
},
|
|
firmware: {
|
|
badge: {
|
|
current: 'Latest',
|
|
older: 'Upgrade available',
|
|
legacy: 'KDP1',
|
|
unknown: 'Unknown',
|
|
},
|
|
button: {
|
|
upgrade: 'Upgrade firmware',
|
|
upgradeTo: 'Upgrade to {version}',
|
|
},
|
|
upgradeModal: {
|
|
title: 'Upgrade Firmware',
|
|
heading: 'Upgrade firmware on {deviceName}',
|
|
from: 'Current version',
|
|
to: 'Upgrading to',
|
|
warning: 'Upgrade takes ~{duration}. Do not unplug the device or close the application. The device will be re-detected automatically when done.',
|
|
actionStart: 'Start upgrade',
|
|
actionCancel: 'Cancel',
|
|
},
|
|
progress: {
|
|
upgradeTitle: 'Upgrading Firmware',
|
|
deviceHeading: 'Upgrading firmware on {deviceName}',
|
|
stage: {
|
|
preparing: 'Stage {n} / {total}: Preparing (detect + connect)',
|
|
loading: 'Stage {n} / {total}: Loading bootloader',
|
|
flashing: 'Stage {n} / {total}: Flashing firmware',
|
|
verifying: 'Stage {n} / {total}: Verifying',
|
|
done: 'Upgrade complete',
|
|
},
|
|
warningUpgrade: '⚠ Do not unplug the device',
|
|
estimatedRemaining: '~{seconds}s remaining',
|
|
elapsed: 'Elapsed {seconds}s',
|
|
},
|
|
success: {
|
|
upgradeToast: '{deviceName} upgrade succeeded',
|
|
upgradeDetail: 'From {from} to {to} (took {duration})',
|
|
partialSuccess: 'Firmware may have been written but verification failed. Please unplug, reconnect, and rescan.',
|
|
},
|
|
error: {
|
|
modalTitle: 'Firmware operation failed',
|
|
message: {
|
|
scanNotFound: 'Device not found, possibly disconnected. Please unplug and retry.',
|
|
connectFailed: 'Cannot connect to device. Please check the USB connection and retry.',
|
|
loaderWriteFailed: 'Bootloader load failed. Please unplug, reconnect, and retry.',
|
|
upgradeMidFailed: 'Firmware write failed. Possible hardware issue, please contact technical support.',
|
|
disconnect: 'Device disconnected during operation. State unknown; the device may be damaged. Please contact technical support.',
|
|
timeout: 'Operation timed out. The operation may or may not have completed. Please unplug, reconnect, and rescan.',
|
|
verifyMismatch: 'Firmware written but verification failed. The device may be damaged. Please contact technical support.',
|
|
verifyNotFound: 'Device not found after upgrade. The device may be damaged. Please contact technical support.',
|
|
},
|
|
errorCode: 'Error code: {code}',
|
|
technicalInfo: 'Technical info',
|
|
copyError: 'Copy error info',
|
|
copied: 'Copied ✓',
|
|
action: {
|
|
close: 'Close',
|
|
retry: 'Retry',
|
|
replugRetry: 'Unplug and retry',
|
|
rescan: 'Unplug and rescan',
|
|
contactSupport: 'Contact technical support',
|
|
},
|
|
contactSupportTooltip: 'Please contact technical support',
|
|
brickWarning: '⚠ The device may be damaged. Please contact technical support.',
|
|
},
|
|
},
|
|
serverConfig: 'Server Configuration',
|
|
backendUrl: 'Backend URL',
|
|
backendUrlPlaceholder: 'e.g., http://192.168.1.100:3721',
|
|
backendUrlHint: 'Leave empty for same-origin mode (frontend embedded in Go binary).',
|
|
backendUrlSaved: 'Backend URL updated',
|
|
saveBackendUrl: 'Save',
|
|
apiUrl: 'API URL',
|
|
wsUrl: 'WebSocket URL',
|
|
serverNote: 'Server addresses are configured at build time.',
|
|
appearance: 'Appearance',
|
|
theme: 'Theme',
|
|
themeLight: 'Light',
|
|
themeDark: 'Dark',
|
|
language: 'Language',
|
|
displayLanguage: 'Display Language',
|
|
languageZhTW: 'Traditional Chinese',
|
|
languageEn: 'English',
|
|
about: 'About',
|
|
versionLabel: 'Version',
|
|
platform: 'visionA Local — Edge AI Workspace',
|
|
resetToDefaults: 'Reset to Defaults',
|
|
resetSuccess: 'Settings restored to defaults',
|
|
serverLogs: {
|
|
title: 'Server Logs',
|
|
clear: 'Clear',
|
|
filter: 'Filter',
|
|
autoScroll: 'Auto-scroll',
|
|
noLogs: 'No log entries yet. Logs will appear as the server produces them.',
|
|
entries: 'entries',
|
|
},
|
|
serverStatus: {
|
|
title: 'Server Status',
|
|
uptimeLabel: 'Uptime',
|
|
goroutinesLabel: 'Goroutines',
|
|
heapAllocLabel: 'Heap Alloc',
|
|
sysMemLabel: 'System Mem',
|
|
gcCyclesLabel: 'GC Cycles',
|
|
nextGcLabel: 'Next GC',
|
|
versionLabel: 'Version',
|
|
buildTimeLabel: 'Build Time',
|
|
platformLabel: 'Platform',
|
|
goVersionLabel: 'Go Version',
|
|
depsTitle: 'Dependencies',
|
|
depAvailable: 'Available',
|
|
depNotFound: 'Not Found',
|
|
restart: 'Restart Server',
|
|
restarting: 'Restarting...',
|
|
waitingForServer: 'Waiting for server...',
|
|
serverBack: 'Server back online',
|
|
restartConfirmTitle: 'Restart Server?',
|
|
restartConfirmDesc: 'The server will restart. All active inference sessions will be stopped. The UI will reconnect automatically.',
|
|
restartConfirm: 'Restart',
|
|
offline: 'Server Offline',
|
|
offlineDesc: 'Cannot connect to the backend server. Make sure the server is running.',
|
|
backendAddress: 'Backend address',
|
|
reconnect: 'Reconnect',
|
|
reconnecting: 'Reconnecting...',
|
|
},
|
|
},
|
|
onboarding: {
|
|
welcome: 'Welcome to visionA Local!',
|
|
step1Title: 'Connect Your Device',
|
|
step1Desc: 'Plug in your Kneron USB Dongle and scan for devices. The platform will automatically detect supported hardware.',
|
|
step2Title: 'Choose an AI Model',
|
|
step2Desc: 'Browse the model library and flash a pre-trained model to your device. Models are optimized for edge inference.',
|
|
step3Title: 'Start Inference!',
|
|
step3Desc: 'Open the workspace, select a camera, image, or video, and run real-time AI inference on your edge device.',
|
|
next: 'Next',
|
|
previous: 'Previous',
|
|
getStarted: 'Get Started',
|
|
skipTour: 'Skip Tour',
|
|
},
|
|
emptyState: {
|
|
devicesTitle: 'No Devices Detected',
|
|
devicesDesc: 'Plug in your Kneron USB Dongle and click Scan to detect devices.',
|
|
devicesScan: 'Scan Devices',
|
|
modelsTitle: 'Explore AI Models',
|
|
modelsDesc: 'Browse pre-trained models or upload your own custom model.',
|
|
modelsBrowse: 'Browse Models',
|
|
modelsUpload: 'Upload Model',
|
|
},
|
|
tour: {
|
|
step1Title: 'Scan for Devices',
|
|
step1Desc: 'Click this button to scan for connected Kneron USB devices.',
|
|
step1Pending: 'Please click "Scan Devices" first. No devices detected yet.',
|
|
step2Title: 'Connect a Device',
|
|
step2Desc: 'Click Connect to establish a connection with the detected device.',
|
|
step2Pending: 'Please click "Connect" on a device first.',
|
|
step3Title: 'Manage Device',
|
|
step3Desc: 'Click Manage to view device details, flash models, and open the workspace.',
|
|
step3Pending: 'Please connect a device first to see the Manage button.',
|
|
step4Title: 'Flash a Model',
|
|
step4Desc: 'Select an AI model from the library and flash it to your device.',
|
|
step4Pending: 'Please flash a model to the device first.',
|
|
step5Title: 'Open Workspace',
|
|
step5Desc: 'Open the workspace to configure input sources and run inference.',
|
|
step5Pending: 'Please flash a model first — the workspace button will appear after.',
|
|
step6Title: 'Start Inference',
|
|
step6Desc: 'Select a camera or upload an image/video, then start real-time AI inference!',
|
|
step6Pending: 'Please start inference to complete the tour.',
|
|
next: 'Next',
|
|
prev: 'Previous',
|
|
done: 'Done',
|
|
exit: 'Exit Tour',
|
|
of: 'of',
|
|
helpTooltip: 'Start guided tour',
|
|
},
|
|
errors: {
|
|
serverDisconnected: 'Server connection lost',
|
|
serverReconnected: 'Server reconnected',
|
|
failedToLoadDevices: 'Failed to load device list',
|
|
deviceConnected: 'Device connected',
|
|
deviceDisconnected: 'Device disconnected',
|
|
modelUploadSuccess: 'Model uploaded successfully',
|
|
modelUploadFailed: 'Upload failed, please check network connection',
|
|
modelDeleted: 'Model deleted',
|
|
modelDeleteFailed: 'Delete failed',
|
|
cannotStopStream: 'Cannot stop stream',
|
|
imageUploadFailed: 'Image upload failed',
|
|
videoUploadFailed: 'Video upload failed',
|
|
batchUploadFailed: 'Batch image upload failed',
|
|
unexpectedError: 'An unexpected error occurred',
|
|
},
|
|
offline: {
|
|
title: 'Local Server Offline',
|
|
subtitle: {
|
|
quit: 'visionA Local has stopped. Please relaunch the application.',
|
|
restart: 'Server is restarting, please wait…',
|
|
healthcheck: 'The server is not responding. Reconnecting…',
|
|
},
|
|
retryButton: 'Retry connection',
|
|
retrying: 'Retrying…',
|
|
helpText: 'To leave this page, simply close this browser tab.',
|
|
},
|
|
};
|