log: serve localized content from the HTMX endpoints
The /log page renders its content asynchronously by hitting
/api/log/latest?as=html and /api/log/by-date/{day}?as=html via HTMX.
Both endpoints returned StrategicLog.content (English) verbatim,
ignoring the new StrategicLogTranslation table entirely. The
_resolve_log_content helper I added to pages.py earlier was wired
into the page handlers themselves but never reached for HTMX swaps,
so Italian users only ever saw English content despite their
lang='it' preference being persisted and translations being
generated correctly.
Fix: add a _localized_content helper in api.py that looks up the
matching translation row for the requesting principal's lang.
_log_partial_payload gains a content_override arg; both HTMX
endpoints (log_latest, log_by_date) compute the override and pass
it through. JSON paths (?as= other than html) remain English to
avoid changing the public API contract.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
eb31d09782
commit
7acd191051
1 changed files with 35 additions and 6 deletions
|
|
@ -39,6 +39,7 @@ from app.models import (
|
||||||
JobRun,
|
JobRun,
|
||||||
Quote,
|
Quote,
|
||||||
StrategicLog,
|
StrategicLog,
|
||||||
|
StrategicLogTranslation,
|
||||||
User,
|
User,
|
||||||
)
|
)
|
||||||
from app.schemas import (
|
from app.schemas import (
|
||||||
|
|
@ -297,11 +298,15 @@ async def news_list(
|
||||||
# --- Strategic log -----------------------------------------------------------
|
# --- Strategic log -----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def _log_partial_payload(row: StrategicLog | None) -> dict | None:
|
def _log_partial_payload(
|
||||||
|
row: StrategicLog | None,
|
||||||
|
content_override: str | None = None,
|
||||||
|
) -> dict | None:
|
||||||
if row is None:
|
if row is None:
|
||||||
return None
|
return None
|
||||||
|
content = content_override if content_override is not None else row.content
|
||||||
return {
|
return {
|
||||||
"content_html": _md_to_html(row.content),
|
"content_html": _md_to_html(content),
|
||||||
"generated_at": row.generated_at,
|
"generated_at": row.generated_at,
|
||||||
"model": row.model,
|
"model": row.model,
|
||||||
"tone": row.tone,
|
"tone": row.tone,
|
||||||
|
|
@ -313,6 +318,28 @@ def _log_partial_payload(row: StrategicLog | None) -> dict | None:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def _localized_content(
|
||||||
|
session: AsyncSession,
|
||||||
|
row: StrategicLog | None,
|
||||||
|
principal: CurrentUser | None,
|
||||||
|
) -> str | None:
|
||||||
|
"""Return the translated content_md for ``row`` when the principal has
|
||||||
|
a non-English lang preference and a matching translation row exists.
|
||||||
|
Returns None to signal 'use row.content as-is' (the default English
|
||||||
|
path)."""
|
||||||
|
if row is None or principal is None or principal.user is None:
|
||||||
|
return None
|
||||||
|
lang = (principal.user.lang or "en")
|
||||||
|
if lang == "en":
|
||||||
|
return None
|
||||||
|
t = (await session.execute(
|
||||||
|
select(StrategicLogTranslation)
|
||||||
|
.where(StrategicLogTranslation.log_id == row.id)
|
||||||
|
.where(StrategicLogTranslation.lang == lang)
|
||||||
|
)).scalar_one_or_none()
|
||||||
|
return t.content_md if t is not None else None
|
||||||
|
|
||||||
|
|
||||||
def _resolve_tone_param(tone: str | None) -> str:
|
def _resolve_tone_param(tone: str | None) -> str:
|
||||||
"""Normalise a query-param tone to one of the two valid values.
|
"""Normalise a query-param tone to one of the two valid values.
|
||||||
PRO is silently mapped to INTERMEDIATE (see openrouter.PROMPT_VERSION 6)."""
|
PRO is silently mapped to INTERMEDIATE (see openrouter.PROMPT_VERSION 6)."""
|
||||||
|
|
@ -368,10 +395,11 @@ async def log_latest(
|
||||||
row = (await session.execute(fallback)).scalar_one_or_none()
|
row = (await session.execute(fallback)).scalar_one_or_none()
|
||||||
|
|
||||||
if as_ == "html":
|
if as_ == "html":
|
||||||
|
content_override = await _localized_content(session, row, principal)
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, "partials/log.html",
|
request, "partials/log.html",
|
||||||
{"log": _log_partial_payload(row), "tone": wanted_tone,
|
{"log": _log_partial_payload(row, content_override=content_override),
|
||||||
"paid": not free_only},
|
"tone": wanted_tone, "paid": not free_only},
|
||||||
)
|
)
|
||||||
|
|
||||||
if row is None:
|
if row is None:
|
||||||
|
|
@ -422,10 +450,11 @@ async def log_by_date(
|
||||||
row = (await session.execute(fallback)).scalar_one_or_none()
|
row = (await session.execute(fallback)).scalar_one_or_none()
|
||||||
|
|
||||||
if as_ == "html":
|
if as_ == "html":
|
||||||
|
content_override = await _localized_content(session, row, principal)
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, "partials/log.html",
|
request, "partials/log.html",
|
||||||
{"log": _log_partial_payload(row), "tone": wanted_tone,
|
{"log": _log_partial_payload(row, content_override=content_override),
|
||||||
"paid": not free_only},
|
"tone": wanted_tone, "paid": not free_only},
|
||||||
)
|
)
|
||||||
if row is None:
|
if row is None:
|
||||||
raise HTTPException(status_code=404, detail="No log on this date")
|
raise HTTPException(status_code=404, detail="No log on this date")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue