Releases: kroryan/book-translator
# 🚀 Windows Desktop App with Complete v2.0 Refactoring
🎯 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
- ✅ Factory Pattern:
create_app()for Flask initialization - ✅ Singleton Pattern: Config, Database, Loggers
- ✅ Repository Pattern: TranslationRepository for data access
- ✅ Strategy Pattern: Validators, Text Processing
- ✅ Observer Pattern: Log Buffer
- ✅ 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/currentendpoint 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_PROGRESS→PROCESSINGto 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:
PathConfignow returnsPathobjects 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:
sourceLanguage→source_lang - ✅ Fixed:
targetLanguage→target_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:
- Phase 1: Remove
<think>,<thinking>,<reasoning>tags (including unclosed ones) - Phase 2: Remove instruction echoes (
IMPORTANT:,REQUIREMENTS:,NOTE:, etc.) - *...
🖥️ Add Windows Desktop App with System Tray Support
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
pystrayto 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
- book_translator_onefile.spec - Creates single portable
- Build Scripts:
build_onefile.bat- One-click build for single-file exebuild.bat- One-click build for folder distribution
Code Improvements
translator.py
- Removed duplicate
import restatements - Added
debug_print()function for frontend log visibility - Added
LogBufferclass for in-memory log storage - Added
/logsand/logs/streamendpoints for real-time log access
requirements.txt
- Added
pystray>=0.19.0for system tray support - Added
Pillow>=10.0.0for 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
-
Build the executable:
pip install pyinstaller pystray Pillow pyinstaller book_translator_onefile.spec
-
Run
dist/BookTranslator.exe -
Verify:
- ✅ Browser opens automatically
- ✅ System tray icon appears
- ✅ Close browser → server keeps running
- ✅ Click tray icon → browser reopens
- ✅ Right-click → Quit Completely → app closes