Translation on Firefox Translations models; new logo
- Email translation moves from LibreTranslate to our own translator
service (translator/): Mozilla's Firefox Translations models on the
Bergamot engine, fastText language detection (176 languages). Polish,
English, German, French, Spanish and Italian on first start (~350 MB
instead of ~10 GB); Admin panel > Email translation installs and
removes languages. Built and pushed as the `translator` image.
- Every message is translated paragraph by paragraph as plain text;
names travel with their sentence and are kept only where the model
changed them; a sentence start the model copied ("Enclosed") is tried
again in lower case; list markers are not sent
- The Translate button shows only for installed languages
- New logo: favicon, app/maskable/iPhone icons, notification badge,
email logo and calendar copy regenerated; BIMI logo rebuilt in SVG
Tiny PS with its gradients; masks render in browsers
- Launch screen background is the app's dark grey
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,10 +15,12 @@ import re
|
||||
import uuid
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, Path, Query
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app import translation
|
||||
from app.api.errors import raise_api_error
|
||||
from app.config import get_settings
|
||||
from app.db import get_session
|
||||
@@ -332,3 +334,61 @@ async def remove_user(user_id: uuid.UUID, admin: CurrentAdmin, db: DbSession) ->
|
||||
except MailServerError as exc:
|
||||
raise_api_error(502, "mail_server_unavailable", f"the mail server did not answer: {exc}")
|
||||
return {"status": "deleted"}
|
||||
|
||||
|
||||
# --- email translation languages (translator service) ------------------------------
|
||||
|
||||
|
||||
class InstallProgressOut(BaseModel):
|
||||
done: int
|
||||
total: int
|
||||
|
||||
|
||||
class AdminTranslationOut(BaseModel):
|
||||
enabled: bool # TRANSLATOR_URL is set
|
||||
reachable: bool
|
||||
installed: list[str] = []
|
||||
installing: dict[str, InstallProgressOut] = {}
|
||||
available: list[str] = [] # what Mozilla has models for (empty: offline)
|
||||
errors: dict[str, str] = {} # why a language's last install failed
|
||||
|
||||
|
||||
_LANG = Path(pattern=r"^[a-z]{2,3}(-[A-Za-z]{2,4})?$")
|
||||
|
||||
|
||||
@router.get("/translation", response_model=AdminTranslationOut)
|
||||
async def translation_status(admin: CurrentAdmin) -> AdminTranslationOut:
|
||||
if not translation.enabled():
|
||||
return AdminTranslationOut(enabled=False, reachable=False)
|
||||
try:
|
||||
data = await translation.status()
|
||||
except translation.TranslationError:
|
||||
return AdminTranslationOut(enabled=True, reachable=False)
|
||||
return AdminTranslationOut(enabled=True, reachable=True, **data)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/translation/languages/{code}",
|
||||
response_model=AdminTranslationOut,
|
||||
dependencies=[Depends(require_csrf)],
|
||||
)
|
||||
async def install_language(admin: CurrentAdmin, code: str = _LANG) -> AdminTranslationOut:
|
||||
"""Download a language's models (to and from English) in the background."""
|
||||
try:
|
||||
await translation.install(code)
|
||||
except translation.TranslationError as exc:
|
||||
raise_api_error(502, "translator_failed", str(exc))
|
||||
return await translation_status(admin)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/translation/languages/{code}",
|
||||
response_model=AdminTranslationOut,
|
||||
dependencies=[Depends(require_csrf)],
|
||||
)
|
||||
async def remove_language(admin: CurrentAdmin, code: str = _LANG) -> AdminTranslationOut:
|
||||
try:
|
||||
await translation.remove(code)
|
||||
except translation.TranslationError as exc:
|
||||
raise_api_error(502, "translator_failed", str(exc))
|
||||
return await translation_status(admin)
|
||||
|
||||
Reference in New Issue
Block a user