feat(ui,api): wave 3 — editorial pricing, real cancel, server history, DeepL purge
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m36s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m36s
Pricing: full editorial redesign — serif card headers with accent pills
replace the colored font-black blocks, tone sweep across toggle/metrics/
features/CTAs, PLAN_COLORS removed; one design system app-wide.
Translate: decorative titles one step down (CTA hierarchy restored);
glossary and image-translation blocks hidden entirely for free users
(progressive disclosure — three controls for free).
Reviews: XLIFF hint line explains the exchange format; backend errors
routed through a friendly mapper (session/not-found/rate-limit/server).
Landing: fabricated hero UI cards (fake 'Context Engine' overlay)
removed — the photo no longer promises screens that don't exist.
Nav: single DashboardNavLinks component shared by sidebar and mobile
drawer (was duplicated markup).
API: GET /api/v1/translations (user job history, paginated; completed
jobs retained 24h) and POST /api/v1/translations/{id}/cancel —
cooperative cancellation with worker checkpoints before dispatch and
before finalisation, reserved quota released immediately. Translate
monitor now offers a real 'Cancel translation' next to 'Back to start';
recent-jobs list reads server history first, localStorage fallback.
DeepL purge (backend): provider module, registry registration, config
attrs/defaults, dispatch branch, admin settings schema + test branch,
legacy availability block, validation rules, plan provider lists,
error-code mappings, MCP enums, translator prompt mention, related
tests updated/removed. Fallback resolver skips unknown providers, so
stale chains containing 'deepl' degrade gracefully.
Verified: backend 110 tests passed; frontend build exit 0, vitest 9/9,
0 missing i18n keys, eslint 63 errors (vs 64 at HEAD).
This commit is contained in:
@@ -7,7 +7,7 @@ Optimized for high performance with parallel processing and caching
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, List, Dict, Tuple
|
||||
import requests
|
||||
from deep_translator import GoogleTranslator, DeeplTranslator, LibreTranslator
|
||||
from deep_translator import GoogleTranslator, LibreTranslator
|
||||
from config import config
|
||||
import concurrent.futures
|
||||
import threading
|
||||
@@ -386,72 +386,6 @@ class GoogleTranslationProvider(TranslationProvider):
|
||||
return results
|
||||
|
||||
|
||||
class DeepLTranslationProvider(TranslationProvider):
|
||||
"""DeepL Translate implementation with batch support"""
|
||||
|
||||
def __init__(self, api_key: str):
|
||||
self.api_key = api_key
|
||||
self._translator_cache = {}
|
||||
|
||||
def _get_translator(
|
||||
self, source_language: str, target_language: str
|
||||
) -> DeeplTranslator:
|
||||
key = f"{source_language}_{target_language}"
|
||||
if key not in self._translator_cache:
|
||||
self._translator_cache[key] = DeeplTranslator(
|
||||
api_key=self.api_key, source=source_language, target=target_language
|
||||
)
|
||||
return self._translator_cache[key]
|
||||
|
||||
def translate(
|
||||
self, text: str, target_language: str, source_language: str = "auto"
|
||||
) -> str:
|
||||
if not text or not text.strip():
|
||||
return text
|
||||
|
||||
try:
|
||||
translator = self._get_translator(source_language, target_language)
|
||||
return translator.translate(text)
|
||||
except Exception as e:
|
||||
logger.warning("translation_error", error_type=type(e).__name__)
|
||||
return text
|
||||
|
||||
def translate_batch(
|
||||
self, texts: List[str], target_language: str, source_language: str = "auto"
|
||||
) -> List[str]:
|
||||
"""Batch translate using DeepL"""
|
||||
if not texts:
|
||||
return []
|
||||
|
||||
results = [""] * len(texts)
|
||||
non_empty = [(i, t) for i, t in enumerate(texts) if t and t.strip()]
|
||||
|
||||
if not non_empty:
|
||||
return [t if t else "" for t in texts]
|
||||
|
||||
try:
|
||||
translator = self._get_translator(source_language, target_language)
|
||||
non_empty_texts = [t for _, t in non_empty]
|
||||
|
||||
if hasattr(translator, "translate_batch"):
|
||||
translated = translator.translate_batch(non_empty_texts)
|
||||
else:
|
||||
translated = [translator.translate(t) for t in non_empty_texts]
|
||||
|
||||
for (idx, _), trans in zip(non_empty, translated):
|
||||
results[idx] = trans if trans else texts[idx]
|
||||
|
||||
# Fill empty positions
|
||||
for i, text in enumerate(texts):
|
||||
if not text or not text.strip():
|
||||
results[i] = text if text else ""
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
logger.warning("deepl_batch_error", error_type=type(e).__name__)
|
||||
return [self.translate(t, target_language, source_language) for t in texts]
|
||||
|
||||
|
||||
class LibreTranslationProvider(TranslationProvider):
|
||||
"""LibreTranslate implementation with batch support"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user