feat: complete i18n support for all frontend pages and components
Add comprehensive bilingual (zh-TW/en-US) support across the entire frontend: Pages updated: - AdminDashboardPage: All 63+ strings translated - TaskHistoryPage: All 80+ strings translated - TaskDetailPage: All 90+ strings translated - AuditLogsPage: All audit log UI translated - ResultsPage/ProcessingPage: Fixed i18n integration - UploadPage: Step indicators and file list UI translated Components updated: - TaskNotFound: Task deletion messages - FileUpload: Prompts and file size limits - ProcessingTrackSelector: Processing mode options with analysis info - Layout: Navigation descriptions - ProtectedRoute: Loading and access denied messages - PDFViewer: Page navigation and error messages Locale files: Added ~200 new translation keys to both zh-TW.json and en-US.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,8 +124,8 @@ function SingleTaskProcessing() {
|
||||
updateTaskStatus(taskId, 'processing', forceTrack || undefined)
|
||||
}
|
||||
toast({
|
||||
title: '開始處理',
|
||||
description: 'OCR 處理已開始',
|
||||
title: t('processing.startProcessing'),
|
||||
description: t('processing.ocrStarted'),
|
||||
variant: 'success',
|
||||
})
|
||||
},
|
||||
@@ -200,7 +200,7 @@ function SingleTaskProcessing() {
|
||||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center">
|
||||
<Loader2 className="w-12 h-12 animate-spin text-primary mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">載入任務資訊...</p>
|
||||
<p className="text-muted-foreground">{t('processing.loadingTask')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -226,13 +226,13 @@ function SingleTaskProcessing() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
{t('processing.noBatchMessage', { defaultValue: '尚未選擇任何任務。請先上傳檔案以建立任務。' })}
|
||||
{t('processing.noBatchMessage')}
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => navigate('/upload')}
|
||||
size="lg"
|
||||
>
|
||||
{t('processing.goToUpload', { defaultValue: '前往上傳頁面' })}
|
||||
{t('processing.goToUpload')}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -252,7 +252,7 @@ function SingleTaskProcessing() {
|
||||
<div>
|
||||
<h1 className="page-title">{t('processing.title')}</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
任務 ID: <span className="font-mono text-primary">{taskId}</span>
|
||||
{t('taskDetail.taskId', { id: taskId })}
|
||||
{taskDetail?.filename && ` · ${taskDetail.filename}`}
|
||||
</p>
|
||||
</div>
|
||||
@@ -260,13 +260,13 @@ function SingleTaskProcessing() {
|
||||
{isCompleted && (
|
||||
<div className="flex items-center gap-2 text-success">
|
||||
<CheckCircle className="w-6 h-6" />
|
||||
<span className="font-semibold">處理完成</span>
|
||||
<span className="font-semibold">{t('processing.completed')}</span>
|
||||
</div>
|
||||
)}
|
||||
{isProcessing && (
|
||||
<div className="flex items-center gap-2 text-primary">
|
||||
<Loader2 className="w-6 h-6 animate-spin" />
|
||||
<span className="font-semibold">處理中</span>
|
||||
<span className="font-semibold">{t('processing.processing')}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -311,9 +311,9 @@ function SingleTaskProcessing() {
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-0.5">檔案名稱</p>
|
||||
<p className="text-xs text-muted-foreground mb-0.5">{t('taskDetail.filename')}</p>
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
{taskDetail.filename || '未知檔案'}
|
||||
{taskDetail.filename || t('common.unknownFile')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,7 +325,7 @@ function SingleTaskProcessing() {
|
||||
<Clock className="w-5 h-5 text-success" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-0.5">處理時間</p>
|
||||
<p className="text-xs text-muted-foreground mb-0.5">{t('taskDetail.processingTime')}</p>
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
{(taskDetail.processing_time_ms / 1000).toFixed(2)}s
|
||||
</p>
|
||||
@@ -342,7 +342,7 @@ function SingleTaskProcessing() {
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-destructive flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-destructive mb-1">處理失敗</p>
|
||||
<p className="text-sm font-medium text-destructive mb-1">{t('processing.failed')}</p>
|
||||
<p className="text-sm text-destructive/80">{taskDetail.error_message}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -380,7 +380,7 @@ function SingleTaskProcessing() {
|
||||
size="lg"
|
||||
>
|
||||
<CheckCircle className="w-4 h-4" />
|
||||
查看任務歷史
|
||||
{t('results.viewTaskHistory')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -396,24 +396,24 @@ function SingleTaskProcessing() {
|
||||
<div className="p-2 bg-primary/10 rounded-lg">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<CardTitle>任務詳情</CardTitle>
|
||||
<CardTitle>{t('taskDetail.title')}</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between py-2 border-b border-border">
|
||||
<span className="text-sm text-muted-foreground">任務狀態</span>
|
||||
<span className="text-sm text-muted-foreground">{t('taskDetail.taskStatus')}</span>
|
||||
{getStatusBadge(taskDetail.status)}
|
||||
</div>
|
||||
<div className="flex justify-between py-2 border-b border-border">
|
||||
<span className="text-sm text-muted-foreground">建立時間</span>
|
||||
<span className="text-sm text-muted-foreground">{t('taskDetail.createdAt')}</span>
|
||||
<span className="text-sm font-medium">
|
||||
{new Date(taskDetail.created_at).toLocaleString('zh-TW')}
|
||||
</span>
|
||||
</div>
|
||||
{taskDetail.updated_at && (
|
||||
<div className="flex justify-between py-2 border-b border-border">
|
||||
<span className="text-sm text-muted-foreground">更新時間</span>
|
||||
<span className="text-sm text-muted-foreground">{t('taskDetail.lastUpdated')}</span>
|
||||
<span className="text-sm font-medium">
|
||||
{new Date(taskDetail.updated_at).toLocaleString('zh-TW')}
|
||||
</span>
|
||||
@@ -421,7 +421,7 @@ function SingleTaskProcessing() {
|
||||
)}
|
||||
{taskDetail.completed_at && (
|
||||
<div className="flex justify-between py-2 border-b border-border">
|
||||
<span className="text-sm text-muted-foreground">完成時間</span>
|
||||
<span className="text-sm text-muted-foreground">{t('taskDetail.completedAt')}</span>
|
||||
<span className="text-sm font-medium">
|
||||
{new Date(taskDetail.completed_at).toLocaleString('zh-TW')}
|
||||
</span>
|
||||
@@ -439,7 +439,7 @@ function SingleTaskProcessing() {
|
||||
{isAnalyzing && (
|
||||
<div className="flex items-center gap-2 p-4 bg-muted/30 rounded-lg border">
|
||||
<Loader2 className="w-4 h-4 animate-spin text-primary" />
|
||||
<span className="text-sm text-muted-foreground">分析文件類型中...</span>
|
||||
<span className="text-sm text-muted-foreground">{t('processing.analyzingDocument')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user