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).
149 lines
4.2 KiB
Python
149 lines
4.2 KiB
Python
"""
|
|
Provider Registry - Singleton pattern for managing translation providers.
|
|
|
|
Provides a central registry for all translation providers with:
|
|
- Registration and retrieval by name
|
|
- Listing available providers
|
|
- Fallback chain support
|
|
"""
|
|
|
|
from typing import Dict, List, Optional
|
|
import threading
|
|
|
|
from .base import TranslationProvider
|
|
|
|
|
|
class ProviderRegistry:
|
|
"""
|
|
Singleton registry for translation providers.
|
|
|
|
Thread-safe implementation for managing multiple translation providers
|
|
with support for fallback chains.
|
|
"""
|
|
|
|
_instance: Optional["ProviderRegistry"] = None
|
|
_lock: threading.Lock = threading.Lock()
|
|
|
|
def __new__(cls) -> "ProviderRegistry":
|
|
"""Create or return the singleton instance."""
|
|
if cls._instance is None:
|
|
with cls._lock:
|
|
if cls._instance is None:
|
|
cls._instance = super().__new__(cls)
|
|
cls._instance._providers: Dict[str, TranslationProvider] = {}
|
|
cls._instance._providers_lock = threading.RLock()
|
|
return cls._instance
|
|
|
|
def register(self, name: str, provider: TranslationProvider) -> None:
|
|
"""
|
|
Register a translation provider.
|
|
|
|
Args:
|
|
name: Unique name for the provider (e.g., "google", "openai")
|
|
provider: TranslationProvider instance
|
|
"""
|
|
with self._providers_lock:
|
|
self._providers[name] = provider
|
|
|
|
def unregister(self, name: str) -> bool:
|
|
"""
|
|
Unregister a translation provider.
|
|
|
|
Args:
|
|
name: Name of the provider to remove
|
|
|
|
Returns:
|
|
True if provider was removed, False if not found
|
|
"""
|
|
with self._providers_lock:
|
|
if name in self._providers:
|
|
del self._providers[name]
|
|
return True
|
|
return False
|
|
|
|
def get(self, name: str) -> Optional[TranslationProvider]:
|
|
"""
|
|
Get a registered provider by name.
|
|
|
|
Args:
|
|
name: Provider name
|
|
|
|
Returns:
|
|
TranslationProvider instance or None if not found
|
|
"""
|
|
with self._providers_lock:
|
|
return self._providers.get(name)
|
|
|
|
def list_all(self) -> List[str]:
|
|
"""
|
|
List all registered provider names.
|
|
|
|
Returns:
|
|
List of provider names
|
|
"""
|
|
with self._providers_lock:
|
|
return list(self._providers.keys())
|
|
|
|
def list_available(self) -> List[str]:
|
|
"""
|
|
List names of all available (reachable) providers.
|
|
|
|
Returns:
|
|
List of provider names that are currently available
|
|
"""
|
|
with self._providers_lock:
|
|
return [
|
|
name
|
|
for name, provider in self._providers.items()
|
|
if provider.is_available()
|
|
]
|
|
|
|
def get_first_available(self, names: List[str]) -> Optional[TranslationProvider]:
|
|
"""
|
|
Get the first available provider from a list of names (fallback chain).
|
|
|
|
Iterates through the list in order and returns the first provider
|
|
that is available. This enables graceful degradation when providers
|
|
are unavailable.
|
|
|
|
Args:
|
|
names: List of provider names in priority order
|
|
|
|
Returns:
|
|
First available TranslationProvider or None if all are unavailable
|
|
"""
|
|
for name in names:
|
|
provider = self.get(name)
|
|
if provider is not None and provider.is_available():
|
|
return provider
|
|
return None
|
|
|
|
def clear(self) -> None:
|
|
"""Remove all registered providers."""
|
|
with self._providers_lock:
|
|
self._providers.clear()
|
|
|
|
def __len__(self) -> int:
|
|
"""Return the number of registered providers."""
|
|
with self._providers_lock:
|
|
return len(self._providers)
|
|
|
|
def __contains__(self, name: str) -> bool:
|
|
"""Check if a provider is registered."""
|
|
with self._providers_lock:
|
|
return name in self._providers
|
|
|
|
|
|
def get_registry() -> ProviderRegistry:
|
|
"""
|
|
Get the global provider registry instance.
|
|
|
|
Returns:
|
|
The singleton ProviderRegistry instance
|
|
"""
|
|
return ProviderRegistry()
|
|
|
|
|
|
# Global registry instance
|
|
registry = ProviderRegistry()
|