From 7dfad0ee07c2ab23efd94dcf82d6db6e7cc44884 Mon Sep 17 00:00:00 2001 From: corsacca Date: Mon, 7 Sep 2026 16:30:48 +0100 Subject: [PATCH] replace deepl with llm translation via openrouter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - translateText/translateTexts call OpenRouter chat-completions; glossary terms are prompt-injected from config/glossaries.ts, generated from the translation repo's TSVs by scripts/build-glossaries.ts - model resolution: per-language override in config/languages.ts → translation.model site setting (new admin field) → TRANSLATION_MODEL env → anthropic/claude-opus-5, at low reasoning effort - blank fragments are held back from the model, which drops them and breaks the one-translation-per-fragment count the caller maps results back on - OpenRouter failures map to a typed error with a human-readable message; an unrecoverable one (no credits, bad key, rejected model) stops the run instead of repeating the same call for every remaining locale - machine translations record source 'auto-translate'; 'deepl' stays readable for snapshots written before the switch --- .env.example | 10 +- app/components/admin/RichTextEditor.vue | 2 +- app/pages/admin/pages/[id].vue | 11 +- app/pages/admin/settings.vue | 71 ++ config/glossaries.ts | 882 ++++++++++++++++++ config/languages.ts | 39 +- nuxt.config.ts | 6 +- scripts/build-glossaries.ts | 47 + server/api/admin/pages/[id]/translate.post.ts | 12 +- server/database/schema.ts | 4 +- server/mcp/tools/translate.ts | 21 +- server/services/cmsTranslate.ts | 58 +- server/services/cmsTranslations.ts | 2 +- server/utils/deepl.ts | 134 --- server/utils/openrouter.ts | 199 ++++ server/utils/site-settings.ts | 10 + server/utils/translate.ts | 108 +++ 17 files changed, 1400 insertions(+), 216 deletions(-) create mode 100644 config/glossaries.ts create mode 100644 scripts/build-glossaries.ts delete mode 100644 server/utils/deepl.ts create mode 100644 server/utils/openrouter.ts create mode 100644 server/utils/translate.ts diff --git a/.env.example b/.env.example index b749caef..4c1c9cce 100644 --- a/.env.example +++ b/.env.example @@ -47,10 +47,12 @@ NUXT_PUBLIC_STATINATOR_ENABLED=true # Set to empty in dev to make the cookie host-only. NUXT_PUBLIC_STATINATOR_COOKIE_DOMAIN=.doxa.life -# DeepL (used by the CMS "Translate from English" workflow on /admin/pages/:id) -# Pro endpoint: https://api.deepl.com | Free-tier endpoint: https://api-free.deepl.com -DEEPL_API_KEY=your-deepl-api-key -DEEPL_API_URL=https://api.deepl.com +# OpenRouter (used by the CMS "Auto-translate" workflow on /admin/pages/:id +# and the translate_text / translate_page MCP tools) +OPENROUTER_API_KEY=your-openrouter-api-key +# Optional model override. Admins can also set it at /admin/settings, which wins +# over this. Defaults to anthropic/claude-opus-5 when both are unset. +TRANSLATION_MODEL= # Cloudflare cache purge on deploy (server/plugins/cloudflare-purge.ts). # On container boot the edge cache is purged so stale prerendered HTML can't diff --git a/app/components/admin/RichTextEditor.vue b/app/components/admin/RichTextEditor.vue index 8d11392f..a6d29932 100644 --- a/app/components/admin/RichTextEditor.vue +++ b/app/components/admin/RichTextEditor.vue @@ -89,7 +89,7 @@ const editor = useEditor({ } }) -// External updates (DeepL translation refresh, locale tab switch) — +// External updates (auto-translation refresh, locale tab switch) — // replace editor content when the prop drifts from the editor state. watch( () => props.modelValue, diff --git a/app/pages/admin/pages/[id].vue b/app/pages/admin/pages/[id].vue index 34046688..a81d988b 100644 --- a/app/pages/admin/pages/[id].vue +++ b/app/pages/admin/pages/[id].vue @@ -1,8 +1,8 @@