The interface ships translated; page content stays English until a LibreTranslate instance fills app/i18n/content/ through make i18n. A string without a catalogue entry falls back to its English source, so a half-filled catalogue degrades instead of breaking. Translation runs after ConfigurationResolver.resolve_links(), on a copy. resolve_links matches by the `name` field, so translating it beforehand would break every `link:` reference in the configuration. negotiate() normalises to the primary subtag itself. Werkzeug's best_match returns an exact match before it considers a primary-tag fallback, so the Chrome default `de-DE,en;q=0.8` resolves to English there. "/" carries Vary: Accept-Language, without which a shared cache pins the first visitor's language for everyone. The route rule lists the known codes as a converter argument. A bare "/<lang>/" answers /robots.txt and /favicon.ico with a permanently cacheable 308 to their trailing-slash form. Templates gain lang, dir, the RTL stylesheet, a canonical URL and 30 hreflang alternates. Those are the first external URLs in this app: ProxyFix takes the scheme from X-Forwarded-Proto so they do not claim http:// behind a TLS-terminating proxy, X-Forwarded-Host stays untrusted because nginx passes a client-supplied one through, and TRUSTED_HOSTS lets Flask reject a forged Host outright. Flask only autoescapes .html/.htm/.xml/.xhtml/.svg, so every *.html.j2 template interpolated configuration raw. Enabling it changes two lines of the shipped page, both an apostrophe. read_catalog degrades an unreadable catalogue to English rather than serving a 500, and drops non-string entries that would otherwise render as "42". i18n_sync writes atomically, never overwrites an existing entry, refuses to touch a catalogue it could not parse, and leaves the file alone when a run translated nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
13 lines
602 B
Plaintext
13 lines
602 B
Plaintext
PORT=5001
|
|
FLASK_ENV=production
|
|
# Single source of truth for the Docker image tag — read by both the
|
|
# Makefile (build/run-dev/run-prod) and docker-compose.yml so every
|
|
# build path produces and consumes the same tag.
|
|
IMAGE_NAME=portfolio
|
|
# Comma-separated public hostnames. Leave empty and the app reflects whatever
|
|
# Host header arrives into its canonical, hreflang and redirect URLs.
|
|
TRUSTED_HOSTS=
|
|
# LibreTranslate instance used by `make i18n` to fill app/i18n/content/.
|
|
# Only needed when generating translations, never at runtime.
|
|
LIBRETRANSLATE_URL=http://localhost:5002
|
|
LIBRETRANSLATE_API_KEY= |