From ddec64fc798bf6357e398f59326acb064424eae4 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 13 Apr 2026 10:09:29 +0200 Subject: [PATCH] feat(ocr): extract translateOcrProgress with ANALYZING_PAGE and DONE:skipped support Move translateOcrProgress from page.svelte to a testable module. Return structured result with currentPage/totalPages/skippedPages for the progress bar. Add ANALYZING_PAGE and DONE with skipped pages parsing. Add i18n keys for de/en/es. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/messages/de.json | 2 + frontend/messages/en.json | 2 + frontend/messages/es.json | 2 + .../src/lib/ocr/translateOcrProgress.spec.ts | 78 +++++++++++++++++++ frontend/src/lib/ocr/translateOcrProgress.ts | 56 +++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 frontend/src/lib/ocr/translateOcrProgress.spec.ts create mode 100644 frontend/src/lib/ocr/translateOcrProgress.ts diff --git a/frontend/messages/de.json b/frontend/messages/de.json index a4221f78..683c1b3a 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -526,6 +526,8 @@ "ocr_status_analyzing": "OCR-Analyse läuft — dies kann einige Minuten dauern…", "ocr_status_creating_blocks": "{count} Textblöcke erkannt — erstelle Transkription…", "ocr_status_done_blocks": "{count} Blöcke erstellt", + "ocr_status_analyzing_page": "Seite {current} von {total} wird analysiert…", + "ocr_status_done_skipped": "{count} Blöcke erstellt, {skipped} Seite(n) übersprungen", "ocr_status_error": "OCR fehlgeschlagen", "transcription_block_review": "Als geprüft markieren", "transcription_block_unreview": "Markierung aufheben", diff --git a/frontend/messages/en.json b/frontend/messages/en.json index e9546eae..c61bc728 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -526,6 +526,8 @@ "ocr_status_analyzing": "OCR analysis running — this may take a few minutes…", "ocr_status_creating_blocks": "{count} text blocks detected — creating transcription…", "ocr_status_done_blocks": "{count} blocks created", + "ocr_status_analyzing_page": "Analyzing page {current} of {total}…", + "ocr_status_done_skipped": "{count} blocks created, {skipped} page(s) skipped", "ocr_status_error": "OCR failed", "transcription_block_review": "Mark as reviewed", "transcription_block_unreview": "Unmark as reviewed", diff --git a/frontend/messages/es.json b/frontend/messages/es.json index ce03d8eb..6dcf5a25 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -526,6 +526,8 @@ "ocr_status_analyzing": "Análisis OCR en curso — esto puede tardar unos minutos…", "ocr_status_creating_blocks": "{count} bloques de texto detectados — creando transcripción…", "ocr_status_done_blocks": "{count} bloques creados", + "ocr_status_analyzing_page": "Analizando página {current} de {total}…", + "ocr_status_done_skipped": "{count} bloques creados, {skipped} página(s) omitida(s)", "ocr_status_error": "OCR fallido", "transcription_block_review": "Marcar como revisado", "transcription_block_unreview": "Desmarcar como revisado", diff --git a/frontend/src/lib/ocr/translateOcrProgress.spec.ts b/frontend/src/lib/ocr/translateOcrProgress.spec.ts new file mode 100644 index 00000000..62df0dd6 --- /dev/null +++ b/frontend/src/lib/ocr/translateOcrProgress.spec.ts @@ -0,0 +1,78 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('$lib/paraglide/messages.js', () => ({ + m: { + ocr_progress_heading: () => 'OCR-Analyse', + ocr_status_preparing: () => 'Dokument wird vorbereitet…', + ocr_status_loading: () => 'Lade Modell und Dokument…', + ocr_status_analyzing: () => 'OCR-Analyse läuft…', + ocr_status_creating_blocks: ({ count }: { count: string }) => `${count} Textblöcke erkannt`, + ocr_status_done_blocks: ({ count }: { count: string }) => `${count} Blöcke erstellt`, + ocr_status_done_skipped: ({ count, skipped }: { count: string; skipped: string }) => + `${count} Blöcke erstellt, ${skipped} Seite(n) übersprungen`, + ocr_status_analyzing_page: ({ current, total }: { current: string; total: string }) => + `Seite ${current} von ${total} wird analysiert…`, + ocr_status_error: () => 'OCR fehlgeschlagen' + } +})); + +import { translateOcrProgress } from './translateOcrProgress'; + +describe('translateOcrProgress', () => { + it('returns heading for empty code', () => { + const result = translateOcrProgress(''); + expect(result.message).toBe('OCR-Analyse'); + }); + + it('translates PREPARING', () => { + const result = translateOcrProgress('PREPARING'); + expect(result.message).toBe('Dokument wird vorbereitet…'); + expect(result.currentPage).toBeUndefined(); + expect(result.totalPages).toBeUndefined(); + }); + + it('translates LOADING', () => { + expect(translateOcrProgress('LOADING').message).toBe('Lade Modell und Dokument…'); + }); + + it('translates ANALYZING', () => { + expect(translateOcrProgress('ANALYZING').message).toBe('OCR-Analyse läuft…'); + }); + + it('translates CREATING_BLOCKS with count', () => { + expect(translateOcrProgress('CREATING_BLOCKS:42').message).toBe('42 Textblöcke erkannt'); + }); + + it('translates DONE without skipped pages', () => { + const result = translateOcrProgress('DONE:15'); + expect(result.message).toBe('15 Blöcke erstellt'); + expect(result.skippedPages).toBeUndefined(); + }); + + it('translates DONE with zero skipped', () => { + const result = translateOcrProgress('DONE:15:0'); + expect(result.message).toBe('15 Blöcke erstellt'); + expect(result.skippedPages).toBeUndefined(); + }); + + it('translates DONE with skipped pages', () => { + const result = translateOcrProgress('DONE:12:2'); + expect(result.message).toBe('12 Blöcke erstellt, 2 Seite(n) übersprungen'); + expect(result.skippedPages).toBe(2); + }); + + it('translates ANALYZING_PAGE with current, total, and blocks', () => { + const result = translateOcrProgress('ANALYZING_PAGE:2:5:10'); + expect(result.message).toBe('Seite 2 von 5 wird analysiert…'); + expect(result.currentPage).toBe(2); + expect(result.totalPages).toBe(5); + }); + + it('translates ERROR', () => { + expect(translateOcrProgress('ERROR').message).toBe('OCR fehlgeschlagen'); + }); + + it('returns raw code for unknown codes', () => { + expect(translateOcrProgress('UNKNOWN_CODE').message).toBe('UNKNOWN_CODE'); + }); +}); diff --git a/frontend/src/lib/ocr/translateOcrProgress.ts b/frontend/src/lib/ocr/translateOcrProgress.ts new file mode 100644 index 00000000..370bb9c8 --- /dev/null +++ b/frontend/src/lib/ocr/translateOcrProgress.ts @@ -0,0 +1,56 @@ +import { m } from '$lib/paraglide/messages.js'; + +export interface OcrProgressResult { + message: string; + currentPage?: number; + totalPages?: number; + skippedPages?: number; +} + +export function translateOcrProgress(code: string): OcrProgressResult { + if (!code) return { message: m.ocr_progress_heading() }; + + const parts = code.split(':'); + const key = parts[0]; + + switch (key) { + case 'PREPARING': + return { message: m.ocr_status_preparing() }; + case 'LOADING': + return { message: m.ocr_status_loading() }; + case 'ANALYZING': + return { message: m.ocr_status_analyzing() }; + case 'CREATING_BLOCKS': + return { message: m.ocr_status_creating_blocks({ count: parts[1] ?? '0' }) }; + case 'DONE': { + const count = parts[1] ?? '0'; + const skipped = parts[2] ? parseInt(parts[2], 10) : 0; + if (skipped > 0) { + return { + message: m.ocr_status_done_skipped({ + count, + skipped: String(skipped) + }), + skippedPages: skipped + }; + } + return { message: m.ocr_status_done_blocks({ count }) }; + } + case 'ANALYZING_PAGE': { + const current = parseInt(parts[1] ?? '0', 10); + const total = parseInt(parts[2] ?? '0', 10); + return { + message: m.ocr_status_analyzing_page({ + current: String(current), + total: String(total) + }), + currentPage: current, + totalPages: total + }; + } + case 'ERROR': + return { message: m.ocr_status_error() }; + default: + return { message: code }; + } +}