@@ -572,7 +572,7 @@ export default function TaskDetailPage() {
{/* Existing Translations */}
{translationList && translationList.translations.length > 0 && (
-
平均置信度
+
{t('taskDetail.stats.avgConfidence')}
{processingMetadata?.average_confidence
? `${(processingMetadata.average_confidence * 100).toFixed(0)}%`
@@ -755,7 +755,7 @@ export default function TaskDetailPage() {
{/* Result Preview */}
{isCompleted && (
diff --git a/frontend/src/pages/TaskHistoryPage.tsx b/frontend/src/pages/TaskHistoryPage.tsx
index aef1e92..069f455 100644
--- a/frontend/src/pages/TaskHistoryPage.tsx
+++ b/frontend/src/pages/TaskHistoryPage.tsx
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
+import { useTranslation } from 'react-i18next'
import { apiClientV2 } from '@/services/apiV2'
import type { Task, TaskStats, TaskStatus } from '@/types/apiV2'
import {
@@ -33,6 +34,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
export default function TaskHistoryPage() {
const navigate = useNavigate()
+ const { t, i18n } = useTranslation()
const [tasks, setTasks] = useState([])
const [stats, setStats] = useState(null)
const [loading, setLoading] = useState(true)
@@ -69,7 +71,7 @@ export default function TaskHistoryPage() {
setTotal(response.total)
setHasMore(response.has_more)
} catch (err: any) {
- setError(err.response?.data?.detail || '載入任務失敗')
+ setError(err.response?.data?.detail || t('taskHistory.loadFailed'))
} finally {
setLoading(false)
}
@@ -101,25 +103,25 @@ export default function TaskHistoryPage() {
// Delete task
const handleDelete = async (taskId: string) => {
- if (!confirm('確定要刪除此任務嗎?')) return
+ if (!confirm(t('taskHistory.deleteConfirm'))) return
try {
await apiClientV2.deleteTask(taskId)
fetchTasks()
fetchStats()
} catch (err: any) {
- alert(err.response?.data?.detail || '刪除任務失敗')
+ alert(err.response?.data?.detail || t('taskHistory.deleteFailed'))
}
}
// Delete all tasks
const handleDeleteAll = async () => {
if (tasks.length === 0) {
- alert('沒有可刪除的任務')
+ alert(t('taskHistory.noTasksToDelete'))
return
}
- if (!confirm(`確定要刪除所有 ${total} 個任務嗎?此操作無法復原!`)) return
+ if (!confirm(t('taskHistory.deleteAllConfirm', { count: total }))) return
try {
setLoading(true)
@@ -139,9 +141,9 @@ export default function TaskHistoryPage() {
}
fetchTasks()
fetchStats()
- alert('所有任務已刪除')
+ alert(t('taskHistory.allTasksDeleted'))
} catch (err: any) {
- alert(err.response?.data?.detail || '刪除任務失敗')
+ alert(err.response?.data?.detail || t('taskHistory.deleteFailed'))
fetchTasks()
fetchStats()
} finally {
@@ -159,7 +161,7 @@ export default function TaskHistoryPage() {
try {
await apiClientV2.downloadPDF(taskId, format)
} catch (err: any) {
- alert(err.response?.data?.detail || `下載 PDF 檔案失敗`)
+ alert(err.response?.data?.detail || t('taskHistory.downloadPdfFailed'))
}
}
@@ -169,18 +171,18 @@ export default function TaskHistoryPage() {
await apiClientV2.startTask(taskId)
fetchTasks()
} catch (err: any) {
- alert(err.response?.data?.detail || '啟動任務失敗')
+ alert(err.response?.data?.detail || t('taskHistory.startTaskFailed'))
}
}
const handleCancelTask = async (taskId: string) => {
- if (!confirm('確定要取消此任務嗎?')) return
+ if (!confirm(t('taskHistory.cancelConfirm'))) return
try {
await apiClientV2.cancelTask(taskId)
fetchTasks()
fetchStats()
} catch (err: any) {
- alert(err.response?.data?.detail || '取消任務失敗')
+ alert(err.response?.data?.detail || t('taskHistory.cancelFailed'))
}
}
@@ -190,14 +192,14 @@ export default function TaskHistoryPage() {
fetchTasks()
fetchStats()
} catch (err: any) {
- alert(err.response?.data?.detail || '重試任務失敗')
+ alert(err.response?.data?.detail || t('taskHistory.retryFailed'))
}
}
// Format date
const formatDate = (dateStr: string) => {
const date = new Date(dateStr)
- return date.toLocaleString('zh-TW')
+ return date.toLocaleString(i18n.language)
}
// Format processing time
@@ -213,22 +215,22 @@ export default function TaskHistoryPage() {
pending: {
variant: 'secondary',
icon: Clock,
- label: '待處理',
+ label: t('taskHistory.status.pending'),
},
processing: {
variant: 'default',
icon: Loader2,
- label: '處理中',
+ label: t('taskHistory.status.processing'),
},
completed: {
variant: 'default',
icon: CheckCircle2,
- label: '已完成',
+ label: t('taskHistory.status.completed'),
},
failed: {
variant: 'destructive',
icon: XCircle,
- label: '失敗',
+ label: t('taskHistory.status.failed'),
},
}
@@ -248,17 +250,17 @@ export default function TaskHistoryPage() {
{/* Header */}
-
任務歷史
-
查看和管理您的 OCR 任務
+
{t('taskHistory.title')}
+
{t('taskHistory.subtitle')}
@@ -268,7 +270,7 @@ export default function TaskHistoryPage() {
- 總計
+ {t('common.total')}
{stats.total}
@@ -277,7 +279,7 @@ export default function TaskHistoryPage() {
- 待處理
+ {t('taskHistory.status.pending')}
{stats.pending}
@@ -286,7 +288,7 @@ export default function TaskHistoryPage() {
- 處理中
+ {t('taskHistory.status.processing')}
{stats.processing}
@@ -295,7 +297,7 @@ export default function TaskHistoryPage() {
- 已完成
+ {t('taskHistory.status.completed')}
{stats.completed}
@@ -304,7 +306,7 @@ export default function TaskHistoryPage() {
- 失敗
+ {t('taskHistory.status.failed')}
{stats.failed}
@@ -318,13 +320,13 @@ export default function TaskHistoryPage() {
- 篩選條件
+ {t('taskHistory.filterConditions')}
-
+
{
@@ -332,17 +334,17 @@ export default function TaskHistoryPage() {
handleFilterChange()
}}
options={[
- { value: 'all', label: '全部' },
- { value: 'pending', label: '待處理' },
- { value: 'processing', label: '處理中' },
- { value: 'completed', label: '已完成' },
- { value: 'failed', label: '失敗' },
+ { value: 'all', label: t('taskHistory.status.all') },
+ { value: 'pending', label: t('taskHistory.status.pending') },
+ { value: 'processing', label: t('taskHistory.status.processing') },
+ { value: 'completed', label: t('taskHistory.status.completed') },
+ { value: 'failed', label: t('taskHistory.status.failed') },
]}
/>
-
+
-
+
-
+
- 清除篩選
+ {t('taskHistory.clearFilter')}
)}
@@ -413,9 +415,9 @@ export default function TaskHistoryPage() {
{/* Task List */}
- 任務列表
+ {t('taskHistory.taskList')}
- 共 {total} 個任務 {hasMore && `(顯示第 ${page} 頁)`}
+ {t('common.total')} {total} {hasMore && `(${t('taskHistory.pagination.showing', { start: (page - 1) * pageSize + 1, end: Math.min(page * pageSize, total), total })})`}
@@ -426,26 +428,26 @@ export default function TaskHistoryPage() {
) : tasks.length === 0 ? (
-
暫無任務
+
{t('taskHistory.noTasks')}
) : (
<>
- 檔案名稱
- 狀態
- 建立時間
- 完成時間
- 處理時間
- 操作
+ {t('taskHistory.filenameFilter')}
+ {t('taskHistory.statusFilter')}
+ {t('taskHistory.table.createdAt')}
+ {t('taskHistory.table.completedAt')}
+ {t('taskHistory.table.processingTime')}
+ {t('taskHistory.table.actions')}
{tasks.map((task) => (
- {task.filename || '未命名檔案'}
+ {task.filename || t('taskHistory.unnamed')}
{getStatusBadge(task.status)}
@@ -466,7 +468,7 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleStartTask(task.task_id)}
- title="開始處理"
+ title={t('taskHistory.actions.startProcessing')}
>
@@ -474,7 +476,7 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleCancelTask(task.task_id)}
- title="取消"
+ title={t('taskHistory.actions.cancel')}
>
@@ -485,7 +487,7 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleCancelTask(task.task_id)}
- title="取消"
+ title={t('taskHistory.actions.cancel')}
>
@@ -495,7 +497,7 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleRetryTask(task.task_id)}
- title="重試"
+ title={t('taskHistory.actions.retry')}
>
@@ -507,25 +509,25 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleDownloadPDF(task.task_id, 'layout')}
- title="下載版面 PDF"
+ title={t('taskHistory.actions.downloadLayoutPdf')}
>
- 版面
+ {t('taskHistory.actions.layoutPdf')}
@@ -536,7 +538,7 @@ export default function TaskHistoryPage() {
variant="outline"
size="sm"
onClick={() => handleDelete(task.task_id)}
- title="刪除"
+ title={t('taskHistory.actions.delete')}
>
@@ -550,8 +552,7 @@ export default function TaskHistoryPage() {
{/* Pagination */}
- 顯示 {(page - 1) * pageSize + 1} - {Math.min(page * pageSize, total)} / 共{' '}
- {total} 個
+ {t('taskHistory.pagination.showing', { start: (page - 1) * pageSize + 1, end: Math.min(page * pageSize, total), total })}
diff --git a/frontend/src/pages/UploadPage.tsx b/frontend/src/pages/UploadPage.tsx
index 94684c8..6f7c867 100644
--- a/frontend/src/pages/UploadPage.tsx
+++ b/frontend/src/pages/UploadPage.tsx
@@ -82,7 +82,7 @@ export default function UploadPage() {
if (selectedFiles.length === 0) {
toast({
title: t('errors.validationError'),
- description: '請選擇至少一個檔案',
+ description: t('upload.selectAtLeastOne'),
variant: 'destructive',
})
return
@@ -122,7 +122,7 @@ export default function UploadPage() {
{t('upload.title')}
- 選擇要進行 OCR 處理的檔案,支援圖片、PDF 和 Office 文件
+ {t('upload.subtitle')}
@@ -135,8 +135,8 @@ export default function UploadPage() {
{selectedFiles.length === 0 ? '1' : }
-
選擇檔案
-
上傳要處理的文件
+
{t('upload.steps.selectFiles')}
+
{t('upload.steps.selectFilesDesc')}
@@ -151,8 +151,8 @@ export default function UploadPage() {
0 ? 'text-foreground' : 'text-muted-foreground'
- }`}>確認並上傳
-
檢查並開始處理
+ }`}>{t('upload.steps.confirmUpload')}
+ {t('upload.steps.confirmUploadDesc')}
@@ -163,8 +163,8 @@ export default function UploadPage() {
3
-
處理完成
-
查看結果並導出
+
{t('upload.steps.processingComplete')}
+
{t('upload.steps.processingCompleteDesc')}
@@ -193,7 +193,7 @@ export default function UploadPage() {
{t('upload.selectedFiles')}
- 已選擇 {selectedFiles.length} 個檔案,總大小 {formatFileSize(totalSize)}
+ {t('upload.fileList.summary', { count: selectedFiles.length, size: formatFileSize(totalSize) })}
@@ -205,7 +205,7 @@ export default function UploadPage() {
className="gap-2"
>
- 清空全部
+ {t('upload.clearAll')}
@@ -228,13 +228,13 @@ export default function UploadPage() {
{file.name}
- {formatFileSize(file.size)} · {file.type || '未知類型'}
+ {formatFileSize(file.size)} · {file.type || t('upload.fileList.unknownType')}
{/* Status badge */}
- 準備就緒
+ {t('upload.fileList.ready')}
{/* Remove button */}
@@ -242,7 +242,7 @@ export default function UploadPage() {
onClick={() => handleRemoveFile(index)}
disabled={uploadMutation.isPending}
className="p-2 rounded-lg text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- title="移除檔案"
+ title={t('upload.fileList.removeFile')}
>
@@ -255,7 +255,7 @@ export default function UploadPage() {
{/* Action Bar */}
- 請確認檔案無誤後點擊上傳按鈕開始處理
+ {t('upload.fileList.confirmPrompt')}