i18n: bilingual landing page (EN / IT) with auto-detect routing
Public landing page is now served in English and Italian via path-
prefixed URLs (/en/ and /it/), with the bare / detecting the
visitor's language and 302-ing to the right one.
Routing
-------
* GET / : if authed → dashboard (unchanged). Otherwise the visitor's
language is resolved from (in priority order) the rtm.lang cookie,
the Accept-Language header, the cf-ipcountry geolocation header,
and finally DEFAULT_LANG, then a 302 redirects to /<lang>/.
* GET /en/ + /it/ : render the localised landing template, set the
rtm.lang cookie (1-year, SameSite=Lax) so the next /-visit goes
straight to the same translation. Logged-in users with user.lang
set bypass detection for / (same priority chain — user.lang wins
on every public surface they touch).
Storage
-------
* app/locales/<lang>.yaml — flat-ish nested copy files. YAML chosen
so a future translator can edit without touching Python. Strings
containing inline HTML (<strong>, <em>, <a>) are rendered with the
Jinja `safe` filter in the template.
* app/services/locales.py — loads at startup, exposes get_locale()
and detect_public_lang(). Wraps each YAML tree in a small _Dotted
view so templates can write {{ t.hero.subhead }} (deliberately NOT
a dict subclass — dict's built-in method names would shadow YAML
keys like `items`).
Template + chrome
-----------------
* landing.html ported in full to {{ t.<key> }} references.
* public_base.html gets <html lang="…"> + hreflang link tags (en/it/
x-default) when a route opts in via lang_switch=true. A tiny
EN | IT link group lands in the public header, only visible on
surfaces that opt in.
* public.css picks up the small lang-switch widget styles.
Scope (intentionally narrow)
----------------------------
* Only the landing page is localised. Pricing, terms, privacy,
disclaimer, login, verify all stay English-only for now; their
header chrome stays English too because localising labels there
while the linked content is still EN would be a worse mismatch.
* When other public pages get translated, lang_switch=true on those
routes will surface the same widget there with no template changes.
Tests
-----
* tests/test_locales.py covers YAML load parity (every active
language has a file), dotted access through the tree, the
detection precedence chain, and the unknown-locale fallback.
Deps
----
* pyyaml was already in requirements.lock as a transitive but not
declared. Added to pyproject so it stays pinned as an explicit
direct dependency.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d98a90f35e
commit
ee8384f1ba
10 changed files with 721 additions and 93 deletions
121
app/locales/en.yaml
Normal file
121
app/locales/en.yaml
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# English copy for the public landing page.
|
||||
#
|
||||
# Strings that contain inline HTML are rendered with the `safe` filter
|
||||
# in the template. Keep markup minimal — <strong>, <em>, and the
|
||||
# brand placeholders only.
|
||||
|
||||
hero:
|
||||
tagline: "Understand markets. Don't gamble on them."
|
||||
subhead: >-
|
||||
Built for investors who want to <strong>act rationally</strong> and
|
||||
tune out the high-frequency noise that comes from treating markets
|
||||
like a casino. We aggregate cross-asset news and macro signals,
|
||||
then write a plain-English read of what the underlying fundamentals
|
||||
justify versus what the crowd is doing. Refreshed through the
|
||||
trading day. A media service, not a financial one.
|
||||
cta_dashboard: "Open dashboard"
|
||||
cta_pricing: "See pricing"
|
||||
cta_signup: "Sign up free"
|
||||
|
||||
shot_dashboard:
|
||||
alt: "Dashboard preview"
|
||||
zoom_hint: "Click to enlarge"
|
||||
caption: >-
|
||||
The dashboard. An aggregate cross-asset read at the top,
|
||||
hand-picked indicator groups underneath. Reading level toggle
|
||||
(Novice / Pro) flips every AI-generated panel between
|
||||
plain-English and terse-pro framing.
|
||||
|
||||
features:
|
||||
news:
|
||||
tag: "News, aggregated"
|
||||
title: "Headlines from across the macro universe"
|
||||
body: >-
|
||||
RSS and per-ticker feeds covering equities, rates, credit, FX,
|
||||
commodities, and geopolitics. Every headline is auto-tagged by
|
||||
theme so the noise stays as noise and the fundamentals-relevant
|
||||
stuff is easy to find. Ingestion follows the trading calendar —
|
||||
off-hours stay quiet.
|
||||
shot_alt: "News feed with auto-tagged headlines"
|
||||
shot_caption: >-
|
||||
The news feed. Each headline carries one or more theme tags
|
||||
(rates, AI, energy, geopolitics, …) so you can keep the threads
|
||||
you care about and mute the ones you don't. Click a tag to
|
||||
include; shift-click to exclude.
|
||||
indicators:
|
||||
tag: "Macro signals"
|
||||
title: "A curated cross-asset tape"
|
||||
body: >-
|
||||
A hand-picked set of indicators across every asset class,
|
||||
refreshed hourly during market hours. Each group gets a short
|
||||
read that explains what the move <em>means</em>, not what it
|
||||
was. Anchored in earnings, policy, valuation — not chart
|
||||
patterns.
|
||||
shot_alt: "Indicators panel with AI commentary"
|
||||
shot_caption: >-
|
||||
The indicators panel. Tabs across asset classes (equity, rates,
|
||||
commodities, FX, bonds, …); each tab carries a one-paragraph
|
||||
'read' written by the model on top of the live prices. The
|
||||
numbers anchor the prose so the commentary is checkable, not
|
||||
floating.
|
||||
strategic:
|
||||
tag: "The strategic read"
|
||||
title: "Rational vs irrational, every paragraph"
|
||||
body: >-
|
||||
We tie the day's headlines and the cross-asset signals into a
|
||||
single short interpretation. Each paragraph separates
|
||||
<strong>rational drivers</strong> (earnings, policy, valuation)
|
||||
from <strong>irrational ones</strong> (positioning, narrative,
|
||||
flows) and names the gap. Two reading levels: Novice and Pro.
|
||||
This is editorial commentary on public data — not a forecast
|
||||
and not advice on any investment decision.
|
||||
shot_alt: "Strategic log — the editorial AI read"
|
||||
shot_caption: >-
|
||||
The strategic log. The model writes a fresh interpretation
|
||||
through the trading day, taking the previous draft as context
|
||||
so it updates rather than starts over. Paid users get a refresh
|
||||
every hour; free users get one every six.
|
||||
|
||||
multilang_callout: >-
|
||||
Every AI-generated surface — strategic log, indicator reads,
|
||||
portfolio analysis, chat, daily digest — is available in
|
||||
<strong>English</strong> and <strong>Italian</strong>. Toggle the
|
||||
language pill in the header and the panels refresh in place;
|
||||
ticker symbols, currency codes and numbers stay verbatim across
|
||||
languages.
|
||||
|
||||
more_views:
|
||||
head: "More views"
|
||||
chat:
|
||||
alt: "Ask follow-up questions against any past log"
|
||||
caption: >-
|
||||
Ask follow-up questions against any past log. The chat panel
|
||||
inherits the log's full context, so you can pull on a thread
|
||||
without re-pasting headlines or re-explaining the setup.
|
||||
caption_strong: "Ask anything about a log"
|
||||
caption_span: "Conversational follow-ups with the day's context loaded."
|
||||
|
||||
portfolio_blurb: >-
|
||||
Paid users can also drop a portfolio CSV from their broker for an
|
||||
AI sense-check on concentration, regime fit, and currency
|
||||
exposure. Holdings stay in your browser by default; opt in to
|
||||
encrypted cloud sync to restore on another device.
|
||||
|
||||
not_strip:
|
||||
head: "What this isn't."
|
||||
items:
|
||||
- "Not investment advice."
|
||||
- "Not trading signals."
|
||||
- "Not a day-trading tool."
|
||||
- "No buy/sell calls, ever."
|
||||
- "No chart-pattern predictions."
|
||||
- "Not a regulated service."
|
||||
|
||||
footer:
|
||||
legal: >-
|
||||
By signing up you agree to our <a href="/terms">Terms</a> and
|
||||
<a href="/privacy">Privacy notice</a>, and confirm you've read
|
||||
the <a href="/disclaimer">financial disclaimer</a>.
|
||||
|
||||
meta:
|
||||
description: "Understand markets. Don't gamble on them."
|
||||
128
app/locales/it.yaml
Normal file
128
app/locales/it.yaml
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Italian copy for the public landing page.
|
||||
#
|
||||
# Note: brand names ("Read the Markets") and proper nouns
|
||||
# (tickers, currency codes) stay verbatim — they aren't translated.
|
||||
|
||||
hero:
|
||||
tagline: "Capisci i mercati. Non scommetterci sopra."
|
||||
subhead: >-
|
||||
Pensato per investitori che vogliono <strong>ragionare con
|
||||
lucidità</strong> e tagliare fuori il rumore ad alta frequenza
|
||||
che nasce dal trattare i mercati come un casinò. Aggreghiamo
|
||||
notizie cross-asset e segnali macro, e ne scriviamo una lettura
|
||||
in prosa chiara: cosa giustificano i fondamentali sottostanti,
|
||||
rispetto a cosa sta facendo la folla. Aggiornato durante la
|
||||
giornata di trading. Un servizio editoriale, non finanziario.
|
||||
cta_dashboard: "Apri dashboard"
|
||||
cta_pricing: "Vedi i piani"
|
||||
cta_signup: "Iscriviti gratis"
|
||||
|
||||
shot_dashboard:
|
||||
alt: "Anteprima dashboard"
|
||||
zoom_hint: "Clicca per ingrandire"
|
||||
caption: >-
|
||||
La dashboard. In alto una lettura cross-asset aggregata, sotto
|
||||
gruppi di indicatori selezionati a mano. Il toggle del livello
|
||||
di lettura (Novice / Pro) cambia il taglio di ogni pannello
|
||||
generato dall'IA, da prosa accessibile a registro tecnico
|
||||
sintetico.
|
||||
|
||||
features:
|
||||
news:
|
||||
tag: "Notizie, aggregate"
|
||||
title: "Titoli dall'intero universo macro"
|
||||
body: >-
|
||||
Feed RSS e per-ticker che coprono azioni, tassi, credito, FX,
|
||||
materie prime e geopolitica. Ogni titolo è etichettato
|
||||
automaticamente per tema, così il rumore resta rumore e il
|
||||
materiale rilevante per i fondamentali è facile da trovare.
|
||||
L'ingestione segue il calendario di trading — fuori orario
|
||||
tutto resta in pausa.
|
||||
shot_alt: "Feed notizie con tag automatici"
|
||||
shot_caption: >-
|
||||
Il feed delle notizie. Ogni titolo porta uno o più tag tematici
|
||||
(tassi, AI, energia, geopolitica, …) per tenere i fili che ti
|
||||
interessano e silenziare gli altri. Click su un tag per
|
||||
includerlo; shift-click per escluderlo.
|
||||
indicators:
|
||||
tag: "Segnali macro"
|
||||
title: "Una tape cross-asset curata"
|
||||
body: >-
|
||||
Un set di indicatori selezionato a mano su ogni classe di
|
||||
attivo, aggiornato ogni ora durante le sessioni di mercato.
|
||||
Ogni gruppo ha una lettura breve che spiega cosa il movimento
|
||||
<em>significa</em>, non cosa è stato. Ancorata a utili,
|
||||
politica monetaria, valutazione — non a pattern grafici.
|
||||
shot_alt: "Pannello indicatori con commento IA"
|
||||
shot_caption: >-
|
||||
Il pannello indicatori. Tab per classe di attivo (azionario,
|
||||
tassi, materie prime, FX, obbligazioni, …); ogni tab porta una
|
||||
'lettura' di un paragrafo scritta dal modello sopra i prezzi
|
||||
live. I numeri ancorano la prosa, così il commento è
|
||||
verificabile, non sospeso nel vuoto.
|
||||
strategic:
|
||||
tag: "Lettura strategica"
|
||||
title: "Razionale vs irrazionale, paragrafo per paragrafo"
|
||||
body: >-
|
||||
Leghiamo i titoli della giornata e i segnali cross-asset in
|
||||
un'unica interpretazione breve. Ogni paragrafo distingue i
|
||||
<strong>driver razionali</strong> (utili, politica, valutazione)
|
||||
dagli <strong>irrazionali</strong> (posizionamento, narrativa,
|
||||
flussi) e dà un nome al divario. Due livelli di lettura: Novice
|
||||
e Pro. È commento editoriale su dati pubblici — non una
|
||||
previsione e non un consiglio su nessuna decisione di
|
||||
investimento.
|
||||
shot_alt: "Log strategico — la lettura editoriale IA"
|
||||
shot_caption: >-
|
||||
Il log strategico. Il modello scrive un'interpretazione
|
||||
aggiornata durante la giornata, partendo dalla versione
|
||||
precedente come contesto, in modo da aggiornare invece di
|
||||
ricominciare. Gli utenti paganti ricevono un refresh ogni ora;
|
||||
gli utenti gratuiti uno ogni sei.
|
||||
|
||||
multilang_callout: >-
|
||||
Ogni superficie generata dall'IA — log strategico, letture degli
|
||||
indicatori, analisi di portafoglio, chat, digest giornaliero — è
|
||||
disponibile in <strong>Inglese</strong> e <strong>Italiano</strong>.
|
||||
Usa il selettore di lingua nell'header e i pannelli si aggiornano
|
||||
in tempo reale; simboli ticker, codici valuta e numeri restano
|
||||
invariati tra le lingue.
|
||||
|
||||
more_views:
|
||||
head: "Altre viste"
|
||||
chat:
|
||||
alt: "Fai domande di approfondimento su qualunque log passato"
|
||||
caption: >-
|
||||
Fai domande di approfondimento su qualunque log passato. Il
|
||||
pannello chat eredita tutto il contesto del log, così puoi
|
||||
tirare un filo senza re-incollare titoli o re-spiegare la
|
||||
situazione.
|
||||
caption_strong: "Chiedi qualsiasi cosa su un log"
|
||||
caption_span: "Domande conversazionali con il contesto della giornata già caricato."
|
||||
|
||||
portfolio_blurb: >-
|
||||
Gli utenti paganti possono anche caricare un CSV di portafoglio
|
||||
dal loro broker per un sense-check IA su concentrazione,
|
||||
coerenza con il regime di mercato ed esposizione valutaria. Le
|
||||
posizioni restano nel browser per impostazione predefinita;
|
||||
l'attivazione opzionale del cloud sync (crittografato)
|
||||
permette di ritrovarle su un altro dispositivo.
|
||||
|
||||
not_strip:
|
||||
head: "Cosa questo NON è."
|
||||
items:
|
||||
- "Non è consulenza finanziaria."
|
||||
- "Non sono segnali di trading."
|
||||
- "Non è uno strumento per day-trading."
|
||||
- "Nessuna chiamata buy/sell, mai."
|
||||
- "Nessuna previsione basata su pattern grafici."
|
||||
- "Non è un servizio regolamentato."
|
||||
|
||||
footer:
|
||||
legal: >-
|
||||
Iscrivendoti accetti i nostri <a href="/terms">Termini</a> e
|
||||
l'<a href="/privacy">Informativa privacy</a>, e confermi di aver
|
||||
letto il <a href="/disclaimer">disclaimer finanziario</a>.
|
||||
|
||||
meta:
|
||||
description: "Capisci i mercati. Non scommetterci sopra."
|
||||
Loading…
Add table
Add a link
Reference in a new issue