944d2beb87
The widget needed inline-script CSP we refuse to grant, so: a /api/public/translate endpoint feeds misses through the chatbot's OpenRouter model and caches every string forever on the volume (translations.json); the client walks text nodes, swaps them in place, and a MutationObserver keeps late-rendered panels covered. CSP goes back to fully tight. 21 languages; choice sticks in localStorage; level names and POL stay untranslated.