log: serve translated content when available; English fallback

Adds module-level _resolve_log_content(session, log_id, lang) helper
to app/routers/pages.py: looks up StrategicLogTranslation by (log_id,
lang) when lang != 'en'; falls back silently to the English original
when no translation row exists yet (the expected case for the first
hour after a new language activates, or when translation fails for a
specific log).

log_page / log_page_day pull cu.user.lang and thread it through
_log_page_context so the template renders the right variant.

Two tests cover both branches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-27 17:13:57 +02:00
parent 924f37548b
commit 1ea71bc160
2 changed files with 92 additions and 4 deletions

View file

@ -11,7 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import CurrentUser, maybe_current_user, require_auth, require_token
from app.config import get_settings, load_groups
from app.db import get_session
from app.models import EmailSend, Referral, StrategicLog, User
from app.models import EmailSend, Referral, StrategicLog, StrategicLogTranslation, User
from app.services.access import is_paid_active, paid_status
from app.services.referral_service import assign_code_if_missing
from app.templates_env import templates
@ -75,7 +75,29 @@ async def _resolve_log_date(session: AsyncSession, day: str | None) -> date:
return datetime.now(timezone.utc).date()
def _log_page_context(target: date, paid: bool) -> dict:
async def _resolve_log_content(
session: AsyncSession, log_id: int, lang: str | None,
) -> str:
"""Return the strategic log content in the user's preferred language.
If ``lang`` is 'en'/None or no translation exists for the requested
language, returns the English original from StrategicLog.content.
A missing translation is the expected case for hours where
translation hasn't yet run; the fallback is silent.
"""
if lang and lang != "en":
row = (await session.execute(
select(StrategicLogTranslation)
.where(StrategicLogTranslation.log_id == log_id)
.where(StrategicLogTranslation.lang == lang)
)).scalar_one_or_none()
if row is not None:
return row.content_md
log_row = await session.get(StrategicLog, log_id)
return log_row.content if log_row is not None else ""
def _log_page_context(target: date, paid: bool, user_lang: str = "en") -> dict:
s = get_settings()
return {
"selected_iso": target.isoformat(),
@ -83,6 +105,7 @@ def _log_page_context(target: date, paid: bool) -> dict:
"current_tone": s.CASSANDRA_TONE.upper(),
"current_analysis": s.CASSANDRA_ANALYSIS.upper(),
"paid": paid,
"user_lang": user_lang,
}
@ -93,8 +116,9 @@ async def log_page(
cu: CurrentUser = Depends(require_auth),
):
target = await _resolve_log_date(session, None)
user_lang = cu.user.lang if cu.user else "en"
return templates.TemplateResponse(
request, "log.html", _log_page_context(target, is_paid_active(cu)),
request, "log.html", _log_page_context(target, is_paid_active(cu), user_lang),
)
@ -106,8 +130,9 @@ async def log_page_day(
cu: CurrentUser = Depends(require_auth),
):
target = await _resolve_log_date(session, day)
user_lang = cu.user.lang if cu.user else "en"
return templates.TemplateResponse(
request, "log.html", _log_page_context(target, is_paid_active(cu)),
request, "log.html", _log_page_context(target, is_paid_active(cu), user_lang),
)