@@ -16,10 +16,12 @@ import {
1616} from '@phosphor-icons/react'
1717import { BatchItem , useBatch } from '../hooks/useBatch'
1818import StatsPanel from './StatsPanel'
19+ import { useI18n } from '../i18n'
1920
2021type BatchHook = ReturnType < typeof useBatch >
2122
2223export default function BatchMode ( { batchHook } : { batchHook : BatchHook } ) {
24+ const { t } = useI18n ( )
2325 const {
2426 items,
2527 scanning,
@@ -77,20 +79,20 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
7779 < div className = "flex items-center justify-between px-6 py-4 border-b border-white/[0.06] flex-shrink-0" >
7880 < div >
7981 < h1 className = "text-base text-white leading-none font-display tracking-[0.02em]" >
80- 批量检测
82+ { t ( 'batch.title' ) }
8183 </ h1 >
82- < p className = "text-xs text-zinc-300 mt-1" > 同时检测多个文件或多段文本 </ p >
84+ < p className = "text-xs text-zinc-300 mt-1" > { t ( 'batch.subtitle' ) } </ p >
8385 </ div >
8486
8587 { items . length > 0 && (
8688 < div className = "flex items-center gap-2 flex-wrap justify-end" >
8789 { doneCount > 0 && (
8890 < div className = "flex items-center gap-3 px-3 py-1.5 bg-white/[0.04] rounded-lg border border-white/[0.07]" >
8991 < span className = "text-xs text-zinc-200" >
90- < span className = "text-white font-mono font-semibold" > { doneCount } </ span > / { items . length } 已扫描
92+ { t ( 'batch.scanned' , { done : doneCount , total : items . length } ) }
9193 </ span >
9294 < span className = "text-xs text-zinc-200" >
93- 平均风险 { ' ' }
95+ { t ( 'batch.avgRisk' ) } { ' ' }
9496 < span
9597 className = "font-mono font-semibold"
9698 style = { {
@@ -108,7 +110,7 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
108110 className = "flex items-center gap-1.5 text-xs text-white hover:text-white bg-white/[0.04] hover:bg-white/[0.07] border border-white/[0.07] px-3 py-1.5 rounded-lg transition-all cursor-pointer no-drag"
109111 >
110112 < Broom size = { 13 } weight = "regular" />
111- 全部清理
113+ { t ( 'batch.cleanAll' ) }
112114 </ button >
113115 ) }
114116 { /* Scan selected / scan all */ }
@@ -119,7 +121,7 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
119121 className = "flex items-center gap-1.5 text-xs text-white bg-[#3b7cd4] hover:bg-[#4d8de0] disabled:opacity-40 disabled:cursor-not-allowed px-3 py-1.5 rounded-lg transition-all cursor-pointer no-drag font-medium"
120122 >
121123 < Play size = { 13 } weight = "fill" />
122- { scanning ? '扫描中…' : `扫描已选 ( ${ selectedCount } )` }
124+ { scanning ? t ( 'common.scanning' ) : t ( 'batch.scanSelected' , { count : selectedCount } ) }
123125 </ button >
124126 ) : (
125127 < button
@@ -128,15 +130,15 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
128130 className = "flex items-center gap-1.5 text-xs text-white bg-[#3b7cd4] hover:bg-[#4d8de0] disabled:opacity-40 disabled:cursor-not-allowed px-3 py-1.5 rounded-lg transition-all cursor-pointer no-drag font-medium"
129131 >
130132 < Play size = { 13 } weight = "fill" />
131- { scanning ? '扫描中…' : '扫描全部' }
133+ { scanning ? t ( 'common.scanning' ) : t ( 'batch.scanAll' ) }
132134 </ button >
133135 ) }
134136 < button
135137 onClick = { clearAll }
136138 className = "flex items-center gap-1.5 text-xs text-zinc-300 hover:text-red-400 transition-colors cursor-pointer no-drag"
137139 >
138140 < Trash size = { 13 } weight = "regular" />
139- 清空
141+ { t ( 'common.clear' ) }
140142 </ button >
141143 </ div >
142144 ) }
@@ -148,8 +150,8 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
148150 { /* Mode toggle */ }
149151 < div className = "flex items-center gap-1 bg-white/[0.04] rounded-lg p-0.5" >
150152 { ( [
151- { id : 'file' as const , label : '文件' , icon : < FileText size = { 12 } /> } ,
152- { id : 'text' as const , label : '文本' , icon : < TextT size = { 12 } /> } ,
153+ { id : 'file' as const , label : t ( 'batch.file' ) , icon : < FileText size = { 12 } /> } ,
154+ { id : 'text' as const , label : t ( 'batch.text' ) , icon : < TextT size = { 12 } /> } ,
153155 ] ) . map ( ( { id, label, icon } ) => (
154156 < button
155157 key = { id }
@@ -183,8 +185,8 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
183185 < UploadSimple size = { 22 } weight = "regular" className = "text-zinc-200" />
184186 </ div >
185187 < div className = "text-center" >
186- < p className = "text-sm text-white font-medium" > 拖入多个文件 </ p >
187- < p className = "text-xs text-zinc-300 mt-1" > 点击或拖放 · .txt .md .docx .pdf </ p >
188+ < p className = "text-sm text-white font-medium" > { t ( 'batch.dropFiles' ) } </ p >
189+ < p className = "text-xs text-zinc-300 mt-1" > { t ( 'batch.dropHint' ) } </ p >
188190 </ div >
189191 < input
190192 ref = { fileInputRef }
@@ -201,7 +203,7 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
201203 < textarea
202204 value = { segmentText }
203205 onChange = { ( e ) => setSegmentText ( e . target . value ) }
204- placeholder = { `粘贴多段文本,用\n---\n分隔各段` }
206+ placeholder = { t ( 'batch.segmentPlaceholder' ) }
205207 className = "flex-1 bg-white/[0.03] rounded-xl border border-white/[0.07] text-sm text-zinc-200 leading-relaxed font-['Geist_Mono',monospace] resize-none px-4 py-3 focus:outline-none focus:border-[#3b7cd4]/40 placeholder:text-zinc-600"
206208 spellCheck = { false }
207209 />
@@ -210,7 +212,7 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
210212 disabled = { ! segmentText . trim ( ) }
211213 className = "flex items-center justify-center gap-1.5 text-xs text-zinc-200 bg-white/[0.06] hover:bg-white/[0.09] disabled:opacity-40 disabled:cursor-not-allowed border border-white/[0.08] px-3 py-2 rounded-lg transition-all cursor-pointer no-drag"
212214 >
213- 添加文本段落
215+ { t ( 'batch.addSegments' ) }
214216 </ button >
215217 </ div >
216218 ) }
@@ -223,7 +225,7 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
223225 < div className = "w-16 h-16 rounded-2xl border border-white/[0.07] bg-white/[0.02] flex items-center justify-center" >
224226 < UploadSimple size = { 28 } weight = "regular" className = "text-zinc-600" />
225227 </ div >
226- < p className = "text-sm text-zinc-200" > 拖入文件或粘贴文本段落开始批量检测 </ p >
228+ < p className = "text-sm text-zinc-200" > { t ( 'batch.empty' ) } </ p >
227229 </ div >
228230 ) : (
229231 < div className = "p-5 flex flex-col gap-2" >
@@ -240,10 +242,10 @@ export default function BatchMode({ batchHook }: { batchHook: BatchHook }) {
240242 ) : (
241243 < Square size = { 15 } weight = "regular" className = "text-zinc-600" />
242244 ) }
243- { allSelected ? '取消全选' : '全选' }
245+ { allSelected ? t ( 'batch.unselectAll' ) : t ( 'batch.selectAll' ) }
244246 </ button >
245247 { selectedCount > 0 && (
246- < span className = "text-xs text-zinc-300" > 已选 { selectedCount } 项 </ span >
248+ < span className = "text-xs text-zinc-300" > { t ( 'batch.selected' , { count : selectedCount } ) } </ span >
247249 ) }
248250 </ div >
249251
@@ -283,10 +285,18 @@ function BatchItemCard({
283285 onRemove : ( ) => void
284286 onSelect : ( ) => void
285287} ) {
288+ const { t } = useI18n ( )
286289 const score = item . result ?. riskScore ?? 0
287290 const scoreColor = score < 30 ? '#10b981' : score < 65 ? '#f59e0b' : '#ef4444'
288- const riskLabel = score < 30 ? '低风险' : score < 65 ? '中等' : '高风险'
289- const actionLabel = score === 0 ? '无异常' : score < 30 ? '可忽略' : score < 65 ? '建议清理' : '建议重写'
291+ const riskLabel = score < 30 ? t ( 'stats.low' ) : score < 65 ? t ( 'stats.medium' ) : t ( 'stats.high' )
292+ const actionLabel =
293+ score === 0
294+ ? t ( 'risk.none' )
295+ : score < 30
296+ ? t ( 'stats.action.ignore' )
297+ : score < 65
298+ ? t ( 'stats.action.clean' )
299+ : t ( 'stats.action.rewrite' )
290300
291301 return (
292302 < motion . div
@@ -340,7 +350,10 @@ function BatchItemCard({
340350 < p className = "text-sm text-white truncate font-medium" > { item . name } </ p >
341351 { item . status === 'done' && item . result && (
342352 < p className = "text-xs text-zinc-300 mt-0.5" >
343- { item . result . totalChars . toLocaleString ( ) } 字符 · 发现 { item . result . suspiciousCount } 处
353+ { t ( 'batch.itemSummary' , {
354+ chars : item . result . totalChars . toLocaleString ( ) ,
355+ count : item . result . suspiciousCount ,
356+ } ) }
344357 </ p >
345358 ) }
346359 { item . status === 'error' && (
@@ -381,15 +394,15 @@ function BatchItemCard({
381394 < button
382395 onClick = { ( e ) => { e . stopPropagation ( ) ; onClean ( ) } }
383396 className = "p-1.5 text-zinc-300 hover:text-zinc-200 hover:bg-white/[0.06] rounded transition-colors cursor-pointer"
384- title = "清理"
397+ title = { t ( 'common.clean' ) }
385398 >
386399 < Broom size = { 13 } weight = "regular" />
387400 </ button >
388401 ) }
389402 < button
390403 onClick = { ( e ) => { e . stopPropagation ( ) ; onRemove ( ) } }
391404 className = "p-1.5 text-zinc-300 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors cursor-pointer"
392- title = "移除"
405+ title = { t ( 'common.remove' ) }
393406 >
394407 < X size = { 13 } weight = "regular" />
395408 </ button >
@@ -410,7 +423,7 @@ function BatchItemCard({
410423 < StatsPanel result = { item . result } />
411424 { item . cleanedText && (
412425 < div className = "mt-4" >
413- < p className = "text-xs text-zinc-200 mb-2 uppercase tracking-wider" > 净化结果 </ p >
426+ < p className = "text-xs text-zinc-200 mb-2 uppercase tracking-wider" > { t ( 'batch.cleanResult' ) } </ p >
414427 < textarea
415428 value = { item . cleanedText }
416429 readOnly
0 commit comments