From fca05aef7ab3b52110d5966697e3e73360eb04a7 Mon Sep 17 00:00:00 2001 From: Giorgio Gilestro Date: Fri, 29 May 2026 12:01:28 +0200 Subject: [PATCH] i18n: live-swap chat sidebar labels on language toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The strategic log content already refreshes via HTMX on lang-changed (server-side translation lookup), but the chat sidebar's static labels — title, hint, helper lede, textarea placeholder, Send button — were baked into the HTML by Jinja at page render and only updated after a full reload. Add a tiny client-side i18n dictionary (CASSANDRA_I18N) plus applyI18n(lang) in base.html. cassandraSetLang() now calls applyI18n(newLang) right after the language PATCH succeeds and before firing the HTMX triggers, so labels swap in step with the AI content. Convention: sets textContent; sets .placeholder. Initial render still goes through the existing {% if user_lang == 'it' %} Jinja blocks so there's no flash of English on page load for IT users — applyI18n is a no-op until the toggle is clicked. Only the chat sidebar has bindings today. Adding more labels later is a matter of dropping a key into the dict and tagging the element. Co-Authored-By: Claude Opus 4.7 --- app/templates/base.html | 36 ++++++++++++++++++++++++++++++++++++ app/templates/log.html | 20 +++++--------------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/app/templates/base.html b/app/templates/base.html index 97028ab..9bbb46f 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -154,6 +154,40 @@ try { localStorage.setItem('cassandra.theme', newTheme); } catch (e) {} }; + // Static-label i18n dictionary. AI-generated content is re-fetched via + // HTMX (server-side translation), but plain UI labels are baked into + // the HTML at render time. This dict + applyI18n() below let the + // language toggle swap labels live without a page refresh. + // Convention: … (sets textContent), + // (sets .placeholder). + // First-render correctness is handled by the template's + // {% if user_lang == 'it' %} block — applyI18n only kicks in on + // subsequent toggle events. + window.CASSANDRA_I18N = { + 'chat.title': { en: 'Ask Cassandra', + it: 'Chiedi a Cassandra' }, + 'chat.hint': { en: 'grounded on the latest log + live data', + it: "basato sull'ultimo log + dati in tempo reale" }, + 'chat.lede': { en: "Ask about today's analysis. The model sees the latest strategic log, live market readings across all groups, and the last 24h of thesis-filtered headlines. Refresh wipes this conversation.", + it: "Fai domande sull'analisi di oggi. Il modello vede l'ultimo log strategico, le quotazioni di mercato in tempo reale per tutti i gruppi e le ultime 24h di titoli filtrati per tesi. Un refresh della pagina cancella questa conversazione." }, + 'chat.placeholder': { en: 'e.g. why is the defence sleeve flat through Hormuz?', + it: 'es. perché il comparto difesa è piatto nonostante Hormuz?' }, + 'chat.send': { en: 'Send', + it: 'Invia' }, + }; + window.cassandraApplyI18n = function (lang) { + document.querySelectorAll('[data-i18n]').forEach(function (el) { + var key = el.getAttribute('data-i18n'); + var entry = window.CASSANDRA_I18N[key]; + if (entry && entry[lang] != null) el.textContent = entry[lang]; + }); + document.querySelectorAll('[data-i18n-placeholder]').forEach(function (el) { + var key = el.getAttribute('data-i18n-placeholder'); + var entry = window.CASSANDRA_I18N[key]; + if (entry && entry[lang] != null) el.placeholder = entry[lang]; + }); + }; + window.cassandraSetLang = async function (newLang) { var pill = document.getElementById('lang-toggle'); if (!pill) return; @@ -170,6 +204,8 @@ body: JSON.stringify({lang: newLang}), }); if (!r.ok) throw new Error('HTTP ' + r.status); + // Swap any static UI labels that have i18n bindings. + window.cassandraApplyI18n(newLang); // Trigger HTMX-driven panels to re-fetch in the new language. // Same shape as cassandraSetTone — every panel that listens to // tone-changed also listens to lang-changed. diff --git a/app/templates/log.html b/app/templates/log.html index 8370050..cadc3f1 100644 --- a/app/templates/log.html +++ b/app/templates/log.html @@ -33,28 +33,18 @@ {% if paid %} {% else %}