Add bilingual UI and improve event actions layout

Add persistent German/English switching, translated UI messages and English 12-hour times. Align desktop event actions while preserving mobile layouts and record bilingual project guidance.

Include pending diary deletion exclusions. Validate both languages with nine tests.
This commit is contained in:
kai
2026-09-14 15:31:56 +02:00
parent b2af6c6857
commit b2af866cf0
28 changed files with 1339 additions and 506 deletions
+46
View File
@@ -0,0 +1,46 @@
"""Request-local UI translations. User content and stored identifiers stay unchanged."""
import json
from contextvars import ContextVar
from pathlib import Path
from urllib.parse import urlencode, urlsplit
LANGUAGE_COOKIE = "metalcircle_language"
current_language = ContextVar("current_language", default="de")
current_page = ContextVar("current_page", default="/")
ENGLISH = json.loads((Path(__file__).parent / "locales" / "en.json").read_text())
def gettext(message):
if not isinstance(message, str):
return message
return ENGLISH.get(message, message) if current_language.get() == "en" else message
def format_time(value):
"""Format display times without changing stored or form-submitted values."""
if current_language.get() == "en":
period = "AM" if value.hour < 12 else "PM"
return f"{value.hour % 12 or 12}:{value.minute:02d} {period}"
return value.strftime("%H:%M")
def format_datetime(value):
return f"{value.strftime('%d.%m.%Y')} {format_time(value)}"
def safe_return_path(value):
"""Only allow local absolute paths, including their query string and fragment."""
if not value or not value.startswith("/") or value.startswith("//"):
return "/"
if "\\" in value or any(ord(char) < 32 or ord(char) == 127 for char in value):
return "/"
parsed = urlsplit(value)
if parsed.scheme or parsed.netloc or parsed.path.startswith("/language/"):
return "/"
return value
def language_url(language):
return f"/language/{language}?{urlencode({'next': current_page.get()})}"