# 技術設計文件 (Technical Design Document) ## 1. 技術概述 ### 1.1 系統架構 PDF Translation Interface 採用現代化的全端技術堆疊,基於 Next.js 15 App Router 架構,提供高效能的 PDF 文件翻譯服務。 ### 1.2 核心技術選擇理由 #### 前端技術選擇 - **Next.js 15 App Router**: 最新的 React 框架,提供更好的 SEO 和效能 - **React 19**: 最新的狀態管理和渲染優化 - **TypeScript**: 強型別系統,提高程式碼品質和維護性 - **Tailwind CSS v4**: 現代化的 CSS 框架,提供快速開發和優秀的自訂性 #### 後端技術選擇 - **Next.js API Routes**: 與前端整合,簡化部署和維護 - **pdf-parse + pdf2json**: 多層備援的 PDF 文字擷取策略 - **Tesseract.js**: 客戶端 OCR,減少伺服器負載 - **PDFKit**: 強大的 PDF 生成能力,支援 Unicode ## 2. 系統架構詳細設計 ### 2.1 前端架構 ```typescript // 主要目錄結構 /app // Next.js 15 App Router ├── globals.css // 全域樣式 ├── layout.tsx // 根佈局 ├── page.tsx // 首頁 └── api/ // API 路由 └── translate/ └── route.ts // 翻譯 API 端點 /components // React 元件 ├── ui/ // 基礎 UI 元件 (shadcn/ui) │ ├── button.tsx │ ├── card.tsx │ ├── select.tsx │ ├── label.tsx │ └── checkbox.tsx └── pdf-translator.tsx // 主要應用程式元件 /lib // 工具函式與邏輯 ├── utils.ts // 通用工具函式 ├── pdf-processor.ts // PDF 處理核心邏輯 ├── pdf-to-image.ts // PDF 轉圖片功能 └── cost-tracker.ts // 費用追蹤系統 ``` ### 2.2 狀態管理架構 ```typescript // PDFTranslator 元件狀態設計 interface PDFTranslatorState { // 檔案處理 file: File | null isDragging: boolean // 語言設定 sourceLanguage: string targetLanguage: string // 翻譯流程 isTranslating: boolean translatedText: string translatedPDFBase64: string generatePDF: boolean // 語音功能 isPlaying: boolean isPaused: boolean speechSupported: boolean selectedVoice: string availableVoices: SpeechSynthesisVoice[] speechRate: number speechVolume: number // 費用追蹤 tokenUsage: TokenUsage | null cost: CostInfo | null model: ModelInfo | null costSummary: CostSummary | null } ``` ## 3. 核心模組技術實作 ### 3.1 PDF 處理模組 (pdf-processor.ts) #### 3.1.1 多層文字擷取策略 ```typescript export async function extractTextFromPDF(buffer: Buffer): Promise { // 第一層:pdf-parse (主要方法) try { const pdfParseModule = await import('pdf-parse') const result = await pdfParseModule(buffer) if (result.text?.trim().length > 10) { return { text: result.text, pageCount: result.numpages, isScanned: false } } } catch (error) { console.log('pdf-parse failed, falling back to pdf2json') } // 第二層:pdf2json (備援方法) try { const PDFParser = require('pdf2json') const pdfParser = new PDFParser() const parseResult = await new Promise((resolve, reject) => { pdfParser.on('pdfParser_dataReady', (pdfData: any) => { let text = '' if (pdfData.Pages) { for (const page of pdfData.Pages) { if (page.Texts) { for (const textItem of page.Texts) { if (textItem.R) { for (const run of textItem.R) { if (run.T) { text += decodeURIComponent(run.T) + ' ' } } } } } text += '\n' } } resolve(text.trim()) }) pdfParser.on('pdfParser_dataError', reject) pdfParser.parseBuffer(buffer) }) if (parseResult && parseResult.length > 10) { return { text: parseResult, pageCount: 1, isScanned: false } } } catch (error) { console.log('pdf2json failed, PDF may be scanned') } // 第三層:標記為掃描文件,需要 OCR return { text: '', pageCount: 1, isScanned: true } } ``` #### 3.1.2 智慧 PDF 生成 ```typescript export async function generateTranslatedPDF( translatedText: string, originalMetadata?: any, targetLanguage?: string ): Promise { const pdfDoc = await PDFDocument.create() pdfDoc.registerFontkit(fontkit) // 中文字元檢測 const hasChinese = /[\u4e00-\u9fff]/.test(translatedText) if (hasChinese) { // 中文內容:使用智慧後備描述系統 return await generateChinesePDF(pdfDoc, translatedText, targetLanguage) } else { // 非中文內容:標準 PDF 生成 return await generateStandardPDF(pdfDoc, translatedText) } } async function generateChinesePDF( pdfDoc: PDFDocument, text: string, targetLanguage?: string ): Promise { const page = pdfDoc.addPage() const { width, height } = page.getSize() const font = await pdfDoc.embedFont(StandardFonts.Helvetica) // 添加重要提示 page.drawText('IMPORTANT: Full Chinese translation is available in the', { x: 50, y: height - 100, size: 11, font, color: rgb(0.8, 0.4, 0.0) }) page.drawText('text output above this PDF download button.', { x: 50, y: height - 115, size: 11, font, color: rgb(0.8, 0.4, 0.0) }) // 處理中文字元並提供有意義的描述 const lines = text.split('\n') let yPosition = height - 150 for (const line of lines) { const processedLine = processChineseText(line) try { page.drawText(processedLine, { x: 50, y: yPosition, size: 12, font, color: rgb(0, 0, 0) }) } catch (error) { // WinAnsi 編碼失敗時的後備處理 const fallbackDescription = generateContentDescription(line) page.drawText(fallbackDescription, { x: 50, y: yPosition, size: 12, font, color: rgb(0.3, 0.3, 0.3) }) } yPosition -= 15 } return await pdfDoc.save() } function generateContentDescription(chineseText: string): string { // 基於內容提供有意義的英文描述 if (chineseText.includes('測試')) return 'Testing PDF processing' if (chineseText.includes('文字提取')) return 'Text extraction functionality' if (chineseText.includes('翻譯')) return 'Translation process' return 'Translated Chinese content' } ``` ### 3.2 費用追蹤模組 (cost-tracker.ts) ```typescript interface CostSession { id: string timestamp: Date provider: string model: string tokenUsage: { prompt: number completion: number total: number } cost: { inputCost: number outputCost: number totalCost: number currency: string } } interface CostSummary { totalSessions: number totalTokens: number totalCost: number currency: string byProvider: Record } class CostTracker { private readonly STORAGE_KEY = 'pdf-translator-costs' addCostSession(session: CostSession): CostSummary { const sessions = this.getCostSessions() sessions.push(session) if (typeof window !== 'undefined') { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(sessions)) } return this.calculateSummary(sessions) } getCostSummary(): CostSummary { const sessions = this.getCostSessions() return this.calculateSummary(sessions) } private calculateSummary(sessions: CostSession[]): CostSummary { return sessions.reduce((summary, session) => ({ totalSessions: summary.totalSessions + 1, totalTokens: summary.totalTokens + session.tokenUsage.total, totalCost: summary.totalCost + session.cost.totalCost, currency: session.cost.currency, byProvider: { ...summary.byProvider, [session.provider]: { sessions: (summary.byProvider[session.provider]?.sessions || 0) + 1, tokens: (summary.byProvider[session.provider]?.tokens || 0) + session.tokenUsage.total, cost: (summary.byProvider[session.provider]?.cost || 0) + session.cost.totalCost } } }), { totalSessions: 0, totalTokens: 0, totalCost: 0, currency: 'USD', byProvider: {} }) } } export const costTracker = new CostTracker() ``` ### 3.3 語音播放模組 ```typescript // 語音功能整合在 PDFTranslator 元件中 const playText = () => { if (!speechSupported || !translatedText) return // 恢復播放 if (isPaused) { speechSynthesis.resume() setIsPaused(false) setIsPlaying(true) return } // 新的播放 speechSynthesis.cancel() const utterance = new SpeechSynthesisUtterance(translatedText) // 語音配置 if (selectedVoice) { const voice = availableVoices.find(v => v.name === selectedVoice) if (voice) utterance.voice = voice } utterance.rate = speechRate utterance.volume = speechVolume utterance.pitch = 1.0 // 事件處理 utterance.onstart = () => setIsPlaying(true) utterance.onend = () => { setIsPlaying(false); setIsPaused(false) } utterance.onerror = (event) => { console.error('Speech synthesis error:', event.error) setIsPlaying(false) setIsPaused(false) } speechSynthesis.speak(utterance) } // 自動語音選擇 const findPreferredVoice = (voices: SpeechSynthesisVoice[], langCode: string) => { const langMap: Record = { 'zh-TW': ['zh-TW', 'zh-HK', 'zh'], 'zh-CN': ['zh-CN', 'zh'], 'en': ['en-US', 'en-GB', 'en'], 'ja': ['ja-JP', 'ja'], 'ko': ['ko-KR', 'ko'], // ... 其他語言映射 } const targetLangs = langMap[langCode] || [langCode] for (const targetLang of targetLangs) { const voice = voices.find(v => v.lang.startsWith(targetLang)) if (voice) return voice } return voices[0] // 後備選項 } ``` ## 4. API 設計與實作 ### 4.1 翻譯 API (app/api/translate/route.ts) ```typescript export async function POST(request: Request) { try { const formData = await request.formData() const file = formData.get('file') as File const sourceLanguage = formData.get('sourceLanguage') as string const targetLanguage = formData.get('targetLanguage') as string const returnPDF = formData.get('returnPDF') === 'true' // 檔案驗證 if (!file || file.type !== 'application/pdf') { return NextResponse.json({ error: '請上傳有效的 PDF 檔案' }, { status: 400 }) } if (file.size > 10 * 1024 * 1024) { // 10MB 限制 return NextResponse.json({ error: '檔案大小不能超過 10MB' }, { status: 413 }) } // PDF 文字擷取 const buffer = Buffer.from(await file.arrayBuffer()) const result = await extractTextFromPDF(buffer) if (!result.text.trim()) { return NextResponse.json({ error: 'PDF 文字提取失敗,可能是掃描檔案或加密文件' }, { status: 400 }) } // AI 翻譯 const { translatedText, tokenUsage, cost, model } = await translateText( result.text, sourceLanguage, targetLanguage ) // 費用追蹤 const costSession = { id: generateId(), timestamp: new Date(), provider: model.provider, model: model.name, tokenUsage, cost } // PDF 生成(可選) let pdfBase64: string | undefined if (returnPDF) { const pdfBytes = await generateTranslatedPDF(translatedText, result.metadata, targetLanguage) pdfBase64 = Buffer.from(pdfBytes).toString('base64') } return NextResponse.json({ translatedText, pdfBase64, tokenUsage: { ...tokenUsage, formattedCounts: { prompt: formatNumber(tokenUsage.prompt), completion: formatNumber(tokenUsage.completion), total: formatNumber(tokenUsage.total) } }, cost: { ...cost, formattedCost: formatCost(cost.totalCost, cost.currency) }, model: { name: model.name, provider: model.provider, displayName: model.displayName }, costSession }) } catch (error) { console.error('Translation error:', error) return NextResponse.json( { error: error instanceof Error ? error.message : '翻譯過程中發生錯誤' }, { status: 500 } ) } } ``` ### 4.2 AI 翻譯整合 ```typescript import { createOpenAI } from '@ai-sdk/openai' import { generateText } from 'ai' const deepseek = createOpenAI({ apiKey: process.env.DEEPSEEK_API_KEY!, baseURL: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com/v1', }) const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY!, }) export async function translateText( text: string, sourceLanguage: string, targetLanguage: string ) { const provider = process.env.AI_PROVIDER || 'deepseek' const model = provider === 'deepseek' ? deepseek(process.env.DEEPSEEK_MODEL || 'deepseek-chat') : openai(process.env.OPENAI_MODEL || 'gpt-4o-mini') const prompt = createTranslationPrompt(text, sourceLanguage, targetLanguage) const result = await generateText({ model, prompt, maxTokens: 4000, temperature: 0.3, }) // Token 使用量計算 const tokenUsage = { prompt: result.usage?.promptTokens || 0, completion: result.usage?.completionTokens || 0, total: result.usage?.totalTokens || 0 } // 費用計算 const cost = calculateCost(tokenUsage, provider, model.modelId) return { translatedText: result.text, tokenUsage, cost, model: { name: model.modelId, provider, displayName: getModelDisplayName(model.modelId, provider) } } } function createTranslationPrompt(text: string, source: string, target: string): string { const targetLang = LANGUAGE_NAMES[target] || target const sourceLang = source === 'auto' ? '自動偵測' : LANGUAGE_NAMES[source] || source return `請將以下${sourceLang}文字翻譯成${targetLang},保持原文的格式和段落結構,確保翻譯準確且自然: ${text} 請直接提供翻譯結果,不需要額外說明。` } ``` ## 5. UI/UX 技術實作 ### 5.1 文清楓風格設計系統 ```typescript // Tailwind CSS 自訂配置 const designTokens = { colors: { // 文清楓主色調 primary: { amber: 'rgb(245 158 11)', // amber-500 green: 'rgb(34 197 94)', // green-500 teal: 'rgb(20 184 166)', // teal-500 }, // 背景漸變 background: { light: 'from-amber-50 via-green-50 to-teal-50', dark: 'dark:from-slate-800 dark:via-slate-900 dark:to-slate-800', }, // 裝飾元素 decoration: { circles: [ 'bg-amber-200 blur-xl', 'bg-green-200 blur-xl', 'bg-teal-200 blur-xl', 'bg-amber-300 blur-xl' ] } }, spacing: { responsive: { mobile: 'p-4 gap-4', tablet: 'sm:p-6 sm:gap-6', desktop: 'lg:p-8 lg:gap-8' } } } ``` ### 5.2 響應式設計實作 ```typescript // 響應式斷點策略 const breakpoints = { sm: '640px', // 手機橫向 md: '768px', // 平板直向 lg: '1024px', // 平板橫向 xl: '1280px', // 桌面 '2xl': '1536px' // 大桌面 } // 響應式元件設計模式 const ResponsiveComponent = () => (
{/* 內容 */}
) // 行動裝置優化 const MobileOptimizedButton = ({ children, fullText, iconText }) => ( ) ``` ### 5.3 拖拽上傳實作 ```typescript const DragDropUpload = () => { const [isDragging, setIsDragging] = useState(false) const handleDragOver = (e: React.DragEvent) => { e.preventDefault() setIsDragging(true) } const handleDragLeave = () => { setIsDragging(false) } const handleDrop = (e: React.DragEvent) => { e.preventDefault() setIsDragging(false) const droppedFile = e.dataTransfer.files[0] if (droppedFile && droppedFile.type === 'application/pdf') { setFile(droppedFile) } else { alert('目前僅支援 PDF 文件') } } return (
{/* 拖拽區域內容 */}
) } ``` ## 6. 效能優化策略 ### 6.1 前端效能優化 ```typescript // 1. 元件懶加載 const PDFTranslator = dynamic(() => import('@/components/pdf-translator'), { loading: () =>
載入中...
, ssr: false // 避免語音 API 的 SSR 問題 }) // 2. 記憶化計算 const memoizedCostSummary = useMemo(() => { return costTracker.getCostSummary() }, [costSummary]) // 3. 防抖動處理 const debouncedSpeechRateChange = useCallback( debounce((rate: number) => setSpeechRate(rate), 300), [] ) // 4. 批次狀態更新 const updateTranslationResult = useCallback((data: TranslationResult) => { // 批次更新避免多次重渲染 setTranslatedText(data.translatedText) setTranslatedPDFBase64(data.pdfBase64 || '') setTokenUsage(data.tokenUsage) setCost(data.cost) setModel(data.model) if (data.costSession) { const updatedSummary = costTracker.addCostSession(data.costSession) setCostSummary(updatedSummary) } }, []) ``` ### 6.2 後端效能優化 ```typescript // 1. 串流處理大檔案 export async function processLargeFile(buffer: Buffer) { const stream = new Readable({ read() { // 分塊處理邏輯 } }) return new Promise((resolve, reject) => { const chunks: Buffer[] = [] stream.on('data', chunk => chunks.push(chunk)) stream.on('end', () => resolve(Buffer.concat(chunks))) stream.on('error', reject) }) } // 2. PDF 處理快取 const pdfCache = new Map() export async function cachedExtractTextFromPDF(buffer: Buffer): Promise { const hash = createHash('md5').update(buffer).digest('hex') if (pdfCache.has(hash)) { return pdfCache.get(hash)! } const result = await extractTextFromPDF(buffer) pdfCache.set(hash, result) // 限制快取大小 if (pdfCache.size > 100) { const firstKey = pdfCache.keys().next().value pdfCache.delete(firstKey) } return result } // 3. AI API 請求優化 const rateLimiter = new Map() export async function rateLimitedTranslation( text: string, source: string, target: string ) { const key = `${source}-${target}` const lastRequest = rateLimiter.get(key) || 0 const now = Date.now() if (now - lastRequest < 1000) { // 1秒限制 await new Promise(resolve => setTimeout(resolve, 1000 - (now - lastRequest))) } rateLimiter.set(key, Date.now()) return await translateText(text, source, target) } ``` ## 7. 錯誤處理策略 ### 7.1 分層錯誤處理 ```typescript // 1. API 層錯誤處理 export class TranslationError extends Error { constructor( message: string, public code: string, public statusCode: number = 500 ) { super(message) this.name = 'TranslationError' } } export class PDFProcessingError extends TranslationError { constructor(message: string) { super(message, 'PDF_PROCESSING_ERROR', 400) } } export class AIServiceError extends TranslationError { constructor(message: string, provider: string) { super(`${provider} 服務錯誤: ${message}`, 'AI_SERVICE_ERROR', 502) } } // 2. 全域錯誤處理中間件 export async function errorHandler( error: Error, request: Request ): Promise { console.error('API Error:', error) if (error instanceof TranslationError) { return NextResponse.json( { error: error.message, code: error.code }, { status: error.statusCode } ) } // 未知錯誤 return NextResponse.json( { error: '內部伺服器錯誤', code: 'INTERNAL_ERROR' }, { status: 500 } ) } // 3. 前端錯誤邊界 export function ErrorBoundary({ children }: { children: React.ReactNode }) { return ( (

系統發生錯誤

{error.message}

)} > {children}
) } ``` ### 7.2 使用者友善的錯誤提示 ```typescript const errorMessages = { PDF_TOO_LARGE: '檔案大小超過 10MB 限制,請選擇較小的檔案', PDF_CORRUPTED: 'PDF 檔案損壞或加密,無法處理', PDF_NO_TEXT: 'PDF 中未找到可擷取的文字,可能為純圖片檔案', AI_QUOTA_EXCEEDED: 'AI 服務配額已達上限,請稍後再試', AI_RATE_LIMITED: '請求過於頻繁,請稍等片刻後再試', NETWORK_ERROR: '網路連線失敗,請檢查網路狀況', UNSUPPORTED_LANGUAGE: '不支援的語言組合' } export function getErrorMessage(error: unknown): string { if (error instanceof Error) { return errorMessages[error.message] || error.message } return '發生未知錯誤,請重新嘗試' } ``` ## 8. 測試策略 ### 8.1 單元測試 ```typescript // PDF 處理測試 describe('PDF Processing', () => { test('should extract text from text-based PDF', async () => { const mockBuffer = Buffer.from('mock-pdf-content') const result = await extractTextFromPDF(mockBuffer) expect(result.text).toBeDefined() expect(result.pageCount).toBeGreaterThan(0) expect(result.isScanned).toBe(false) }) test('should handle corrupted PDF gracefully', async () => { const corruptedBuffer = Buffer.from('invalid-pdf') await expect(extractTextFromPDF(corruptedBuffer)) .rejects.toThrow(PDFProcessingError) }) }) // 費用追蹤測試 describe('Cost Tracking', () => { beforeEach(() => { localStorage.clear() }) test('should calculate cost summary correctly', () => { const session: CostSession = { id: 'test-1', timestamp: new Date(), provider: 'deepseek', model: 'deepseek-chat', tokenUsage: { prompt: 100, completion: 50, total: 150 }, cost: { inputCost: 0.001, outputCost: 0.002, totalCost: 0.003, currency: 'USD' } } const summary = costTracker.addCostSession(session) expect(summary.totalSessions).toBe(1) expect(summary.totalTokens).toBe(150) expect(summary.totalCost).toBe(0.003) }) }) ``` ### 8.2 整合測試 ```typescript // API 端點測試 describe('/api/translate', () => { test('should translate PDF successfully', async () => { const formData = new FormData() formData.append('file', new File(['mock-pdf'], 'test.pdf', { type: 'application/pdf' })) formData.append('sourceLanguage', 'zh-TW') formData.append('targetLanguage', 'en') formData.append('returnPDF', 'true') const response = await fetch('/api/translate', { method: 'POST', body: formData }) expect(response.status).toBe(200) const data = await response.json() expect(data.translatedText).toBeDefined() expect(data.tokenUsage).toBeDefined() expect(data.cost).toBeDefined() }) }) ``` ### 8.3 端對端測試 ```typescript // Playwright E2E 測試 test('complete translation workflow', async ({ page }) => { await page.goto('/') // 上傳檔案 await page.setInputFiles('input[type="file"]', 'test-files/sample.pdf') // 選擇語言 await page.selectOption('[data-testid="source-language"]', 'zh-TW') await page.selectOption('[data-testid="target-language"]', 'en') // 開始翻譯 await page.click('[data-testid="translate-button"]') // 等待結果 await page.waitForSelector('[data-testid="translation-result"]') // 驗證結果 const result = await page.textContent('[data-testid="translation-result"]') expect(result).toBeTruthy() // 測試語音播放 await page.click('[data-testid="play-button"]') await page.waitForTimeout(1000) await page.click('[data-testid="pause-button"]') }) ``` ## 9. 部署與監控 ### 9.1 Vercel 部署配置 ```json { "buildCommand": "npm run build", "outputDirectory": ".next", "installCommand": "npm install", "framework": "nextjs", "functions": { "app/api/translate/route.ts": { "maxDuration": 30 } }, "env": { "NODE_ENV": "production" } } ``` ### 9.2 效能監控 ```typescript // 自訂效能監控 export class PerformanceMonitor { static measureApiCall(name: string) { const start = performance.now() return { end: (status: 'success' | 'error') => { const duration = performance.now() - start // 發送監控數據 if (typeof window !== 'undefined') { navigator.sendBeacon('/api/metrics', JSON.stringify({ name, duration, status, timestamp: Date.now() })) } } } } } // 使用範例 export async function monitoredTranslation(params: TranslationParams) { const monitor = PerformanceMonitor.measureApiCall('translation') try { const result = await translateText(params) monitor.end('success') return result } catch (error) { monitor.end('error') throw error } } ``` ## 10. 安全性實作 ### 10.1 輸入驗證 ```typescript import { z } from 'zod' const TranslationRequestSchema = z.object({ file: z.instanceof(File) .refine(file => file.type === 'application/pdf', '僅支援 PDF 檔案') .refine(file => file.size <= 10 * 1024 * 1024, '檔案大小不能超過 10MB'), sourceLanguage: z.enum(SUPPORTED_LANGUAGES), targetLanguage: z.enum(SUPPORTED_LANGUAGES), returnPDF: z.boolean().optional() }) export async function validateRequest(formData: FormData) { const data = { file: formData.get('file'), sourceLanguage: formData.get('sourceLanguage'), targetLanguage: formData.get('targetLanguage'), returnPDF: formData.get('returnPDF') === 'true' } return TranslationRequestSchema.parse(data) } ``` ### 10.2 API 金鑰管理 ```typescript // 環境變數驗證 const envSchema = z.object({ DEEPSEEK_API_KEY: z.string().min(1, 'DeepSeek API key is required'), OPENAI_API_KEY: z.string().optional(), AI_PROVIDER: z.enum(['deepseek', 'openai']).default('deepseek'), NODE_ENV: z.enum(['development', 'production', 'test']).default('development') }) export const env = envSchema.parse(process.env) // API 金鑰輪換 class APIKeyManager { private keys: string[] private currentIndex = 0 constructor(keys: string[]) { this.keys = keys.filter(Boolean) if (this.keys.length === 0) { throw new Error('No valid API keys provided') } } getKey(): string { const key = this.keys[this.currentIndex] this.currentIndex = (this.currentIndex + 1) % this.keys.length return key } } ``` ## 11. 未來技術升級計畫 ### 11.1 短期升級 (1-3 個月) - [ ] 實作 Redis 快取系統 - [ ] 添加請求速率限制 - [ ] 整合 Sentry 錯誤監控 - [ ] 實作批次檔案處理 ### 11.2 中期升級 (3-6 個月) - [ ] 實作 WebSocket 即時進度更新 - [ ] 整合更多 AI 模型 (Claude, Gemini) - [ ] 實作用戶認證系統 - [ ] 添加翻譯歷史功能 ### 11.3 長期升級 (6-12 個月) - [ ] 實作分散式處理系統 - [ ] 整合專業 CAT 工具 - [ ] 實作協作翻譯功能 - [ ] 添加 API 開放平台 ### 11.4 技術債務管理 - [ ] 重構 PDF 處理模組為微服務 - [ ] 實作完整的測試覆蓋率 (目標 >90%) - [ ] 優化 TypeScript 型別定義 - [ ] 實作自動化效能測試 --- *本技術設計文件將隨著系統演進持續更新,確保技術架構的可維護性和可擴展性。*