Skip to content

Releases: kroryan/book-translator

# 🚀 Windows Desktop App with Complete v2.0 Refactoring

18 Jan 04:12

Choose a tag to compare

🎯 Overview

The app runs as a system tray application, allowing users to keep the translation server running in the background while closing the browser window. Additionally, this release includes comprehensive language detection improvements, critical Windows compatibility fixes, a complete refactoring from a 2,295-line monolith to a modular 3,000+ line architecture, major fixes to translation output cleaning and formatting preservation, and critical bug fixes affecting core functionality.


🏗️ MAJOR: Complete v2.0 Refactoring

Executive Summary

  • Refactored: 2,295 lines → modular architecture of 3,000+ lines
  • Tests: 35/35 passing (100%)
  • Coverage: Config, Models, Services, Database, API, Utils
  • Quality: Clean, testable, maintainable code
  • Critical Bugs: All major issues resolved

New Modular Architecture

book_translator/
├── __init__.py              # Package entry point
├── app.py                   # Flask application factory (175 lines)
├── config/
│   ├── __init__.py
│   ├── settings.py          # Centralized config (221 lines) - FIXED Path objects
│   └── constants.py         # Enums and markers (204 lines) - FIXED status names
├── models/
│   ├── __init__.py
│   ├── translation.py       # Data models (120 lines)
│   └── schemas.py           # API schemas (78 lines)
├── services/
│   ├── __init__.py
│   ├── ollama_client.py     # Ollama API client (219 lines)
│   ├── cache_service.py     # Translation cache (223 lines) - ENHANCED logging
│   ├── terminology.py       # Terminology manager (98 lines)
│   └── translator.py        # Translation logic (308 lines) - ENHANCED logging
├── database/
│   ├── __init__.py
│   ├── connection.py        # DB manager (165 lines)
│   └── repositories.py      # Data access layer (198 lines)
├── api/
│   ├── __init__.py
│   ├── routes.py            # Flask blueprints (231 lines) - FIXED unreachable route
│   └── middleware.py        # Rate limit & auth (162 lines)
└── utils/
    ├── __init__.py
    ├── language_detection.py (160 lines)
    ├── text_processing.py   (176 lines) - ENHANCED logging
    ├── validators.py        (121 lines)
    └── logging.py           (144 lines)

Implemented Design Patterns

  1. Factory Pattern: create_app() for Flask initialization
  2. Singleton Pattern: Config, Database, Loggers
  3. Repository Pattern: TranslationRepository for data access
  4. Strategy Pattern: Validators, Text Processing
  5. Observer Pattern: Log Buffer
  6. Dependency Injection: Service layer decoupling

Architecture Improvements

  • Separation of Concerns (Single Responsibility Principle)
  • Dependency Injection for testability
  • Application Factory Pattern for Flask
  • Repository Pattern for database operations
  • Centralized Configuration with type safety
  • Independent Services with clear interfaces

🐛 CRITICAL: Bug Fixes

Backend Critical Fixes

1. routes.py - Unreachable Route Fixed

# BEFORE (BUG): /models/current was after return statement
@api_bp.route('/translate', methods=['POST'])
def translate():
    # ... code ...
    return jsonify(response)
    
@api_bp.route('/models/current')  # UNREACHABLE!
def get_current_model():
    pass

# AFTER (FIXED): Moved before return
@api_bp.route('/models/current')
def get_current_model():
    pass

@api_bp.route('/translate', methods=['POST'])
def translate():
    # ... code ...
    return jsonify(response)
  • Fixed: /models/current endpoint was unreachable due to placement
  • Impact: Model selection now works correctly

2. constants.py - Status Enum Mismatch

# BEFORE (BUG): Enum didn't match DB constraint
class TranslationStatus(str, Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"  # DB has "processing"
    COMPLETED = "completed"
    FAILED = "failed"

# AFTER (FIXED): Now matches database
class TranslationStatus(str, Enum):
    PENDING = "pending"
    PROCESSING = "processing"  # Matches DB constraint
    COMPLETED = "completed"
    FAILED = "failed"
  • Fixed: IN_PROGRESSPROCESSING to match DB constraint
  • Impact: No more database integrity errors on status updates

3. settings.py - Path Objects vs Strings

# BEFORE (BUG): Returned strings instead of Path objects
@dataclass
class PathConfig:
    def upload_folder(self) -> str:
        return str(self.base_dir / "uploads")

# AFTER (FIXED): Returns Path objects
@dataclass
class PathConfig:
    def upload_folder(self) -> Path:
        return self.base_dir / "uploads"
  • Fixed: PathConfig now returns Path objects for all folder properties
  • Impact: Consistent path handling, no string/Path mixing issues

Frontend Critical Fixes

4. Form Field Name Mismatches

// BEFORE (BUG): Wrong field names
const formData = new FormData();
formData.append('sourceLanguage', sourceLang);  // Backend expects 'source_lang'
formData.append('targetLanguage', targetLang);  // Backend expects 'target_lang'

// AFTER (FIXED): Correct field names
const formData = new FormData();
formData.append('source_lang', sourceLang);
formData.append('target_lang', targetLang);
  • Fixed: sourceLanguagesource_lang
  • Fixed: targetLanguagetarget_lang
  • Impact: Form submissions now work correctly

5. SSE Connection Race Condition

// BEFORE (BUG): Connected to SSE before getting translation ID
const eventSource = new EventSource(`/api/stream/${translationId}`);
// translationId might be undefined!

// AFTER (FIXED): Get ID first, then connect
const response = await fetch('/api/translate', { method: 'POST', body: formData });
const data = await response.json();
const translationId = data.translation_id;
const eventSource = new EventSource(`/api/stream/${translationId}`);
  • Fixed: Now gets translation ID before connecting to SSE
  • Impact: Real-time progress updates work reliably

6. History Display Field Names

// BEFORE (BUG): Wrong field names
<td>${item.filename}</td>
<td>${item.source_lang}</td>
<td>${item.target_lang}</td>

// AFTER (FIXED): Correct field names
<td>${item.original_filename}</td>
<td>${item.source_language}</td>
<td>${item.target_language}</td>
  • Fixed: Field names now match API response
  • Impact: Translation history displays correctly

7. Download URL Path

// BEFORE (BUG): Wrong path
window.location.href = `/download/${translationId}`;

// AFTER (FIXED): Correct API path
window.location.href = `/api/download/${translationId}`;
  • Fixed: Download URL now includes /api/ prefix
  • Impact: Downloads work correctly

8. Status Check for Retry

// BEFORE (BUG): Wrong status value
if (translation.status === 'error') {
    showRetryButton();
}

// AFTER (FIXED): Correct status value
if (translation.status === 'failed') {
    showRetryButton();
}
  • Fixed: 'error''failed' to match backend enum
  • Impact: Retry button appears when translations fail

📊 ENHANCED: Comprehensive Debug Logging

translator.py - Detailed Translation Logging

# NEW: Chunk-level logging
logger.debug(f"Processing chunk {idx+1}/{len(chunks)}")
logger.debug(f"Chunk size: {len(chunk)} characters")

# NEW: Prompt logging
logger.debug(f"Prompt length: {len(prompt)} characters")
logger.debug(f"Prompt preview: {prompt[:200]}...")

# NEW: LLM request/response logging
logger.debug(f"Sending request to LLM: {model}")
logger.debug(f"Response received in {elapsed:.2f}s")
logger.debug(f"Response length: {len(response)} characters")

# NEW: Validation logging
logger.debug(f"Validating translation quality")
logger.debug(f"Validation result: {is_valid}")

# NEW: Timing logging
logger.debug(f"Translation completed in {total_time:.2f}s")
logger.debug(f"Average time per chunk: {avg_time:.2f}s")

cache_service.py - Cache Operation Logging

# NEW: Cache lookup logging
logger.debug(f"Cache lookup: source={source_lang}, target={target_lang}")
logger.debug(f"Cache key: {cache_key}")

# NEW: Cache hit/miss logging
logger.debug(f"Cache HIT for {cache_key}")
logger.debug(f"Cache MISS for {cache_key}")

# NEW: Cache store logging
logger.debug(f"Storing in cache: {cache_key}")
logger.debug(f"Cache entry size: {len(translation)} characters")

text_processing.py - Processing Logging

# NEW: Chunking logging
logger.debug(f"Splitting text into chunks")
logger.debug(f"Total chunks created: {len(chunks)}")
logger.debug(f"Average chunk size: {avg_size} characters")

# NEW: Cleaning logging
logger.debug(f"Cleaning translation response")
logger.debug(f"Phase 1: Removed {n} thinking tags")
logger.debug(f"Phase 2: Removed {n} instruction echoes")
logger.debug(f"Phase 3: Removed {n} context leaks")
logger.debug(f"Phase 4: Removed quote wrapping")
logger.debug(f"Phase 5: Removed {n} duplicates")

Impact

  • 🔍 Easier Debugging: Detailed logs help identify issues quickly
  • 📈 Performance Monitoring: Track timing and resource usage
  • 🐛 Issue Diagnosis: See exactly what's happening at each step
  • 📊 Cache Analysis: Monitor cache effectiveness

✨ New Features

🎨 CRITICAL: Translation Output Quality Improvements

Enhanced Output Cleaning (5-Phase Process)

NEW: clean_translation_response() with comprehensive cleaning:

  1. Phase 1: Remove <think>, <thinking>, <reasoning> tags (including unclosed ones)
  2. Phase 2: Remove instruction echoes (IMPORTANT:, REQUIREMENTS:, NOTE:, etc.)
  3. *...
Read more

🖥️ Add Windows Desktop App with System Tray Support

17 Jan 08:49

Choose a tag to compare

This PR adds a native Windows desktop application experience for Book Translator. The app runs as a system tray application, allowing users to keep the translation server running in the background while closing the browser window.

New Features

Desktop Application (app_desktop.py)

  • System Tray Integration: Uses pystray to create a system tray icon
  • Background Operation: Server keeps running even when browser is closed
  • Auto Browser Launch: Automatically opens the default browser on startup
  • Tray Menu Options:
    • 📖 Open Book Translator - Reopens the app in browser
    • 🌐 Server status indicator
    • ❌ Quit Completely - Shuts down server and exits

Build System

  • PyInstaller Spec Files:
    • book_translator_onefile.spec - Creates single portable .exe (~50MB)
    • book_translator.spec - Creates folder-based distribution
  • Build Scripts:
    • build_onefile.bat - One-click build for single-file exe
    • build.bat - One-click build for folder distribution

Code Improvements

translator.py

  • Removed duplicate import re statements
  • Added debug_print() function for frontend log visibility
  • Added LogBuffer class for in-memory log storage
  • Added /logs and /logs/stream endpoints for real-time log access

requirements.txt

  • Added pystray>=0.19.0 for system tray support
  • Added Pillow>=10.0.0 for icon generation
  • Updated version constraints to use >= for flexibility

README.md

  • Added documentation for Windows desktop app installation
  • Added build instructions for creating the .exe
  • Updated startup options to include desktop app as Option 1

Files Changed

File Change
app_desktop.py New - Desktop wrapper with tray support
book_translator_onefile.spec New - PyInstaller single-file config
book_translator.spec New - PyInstaller folder config
build.bat New - Build script (folder)
build_onefile.bat New - Build script (single-file)
app_icon.ico New - Application icon
translator.py Modified - Bug fixes, log buffer
requirements.txt Modified - Added pystray, Pillow
README.md Modified - Desktop app docs
static/index.html Modified - Frontend improvements
.gitignore Modified - Exclude build artifacts

How to Test

  1. Build the executable:

    pip install pyinstaller pystray Pillow
    pyinstaller book_translator_onefile.spec
  2. Run dist/BookTranslator.exe

  3. Verify:

    • ✅ Browser opens automatically
    • ✅ System tray icon appears
    • ✅ Close browser → server keeps running
    • ✅ Click tray icon → browser reopens
    • ✅ Right-click → Quit Completely → app closes