Commit Graph

6 Commits

Author SHA1 Message Date
2a35b2910a feat(i18n): serve every page in 30 languages
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>
2026-08-22 01:18:29 +02:00
4424db22cb fix(cypress): allow scrollTo on non-scrollable pages in footer dropup test
cy.scrollTo('bottom') threw on CI ("element is not scrollable") whenever
the rendered page fit inside the viewport. Pass ensureScrollable:false
so the call is a no-op on short pages — the footer is already in view
and the subsequent rect-position pre-check enforces the actual
precondition that chooseDirection() needs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 02:38:06 +02:00
f3c15e3e1c fix(navigation): unclip dropdowns and flip toward the side with more space
- Move <header> overflow:hidden into body.fullscreen scope and drop the
  implicit-vertical-clip overflow-x:auto from .navbar-nav so dropdown
  menus can escape the navbar.
- Drive top-level dropdowns through bootstrap.Dropdown (popperConfig
  strategy:'fixed'), and add a chooseDirection() helper that toggles
  .dropup/.dropdown on the .nav-item based on space above vs below
  before each show. Split the navigation.css rules to position the menu
  with top:100% or bottom:100% accordingly.
- Mark the dropdown toggle with data-bs-toggle="dropdown" in the
  template; cover that with a Jinja-rendered unit test and add Cypress
  specs for the header (opens downward) and footer (flips to .dropup)
  cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 02:26:02 +02:00
252b50d2a7 feat: migrate to pyproject.toml, add test suites, split CI workflows
- Replace requirements.txt with pyproject.toml for modern Python packaging
- Add unit, integration, lint and security test suites under tests/
- Add utils/export_runtime_requirements.py and utils/check_hadolint_sarif.py
- Split monolithic CI into reusable lint.yml, security.yml and tests.yml
- Refactor ci.yml to orchestrate reusable workflows; publish on semver tag only
- Modernize Dockerfile: pin python:3.12-slim, install via pyproject.toml
- Expand Makefile with lint, security, test and CI targets
- Add test-e2e via act with portfolio container stop/start around run
- Fix navbar_logo_visibility.spec.js: win.fullscreen() → win.enterFullscreen()
- Set use_reloader=False in app.run() to prevent double-start in CI
- Add app/core.* and build artifacts to .gitignore
- Fix apt-get → sudo apt-get in tests.yml e2e job
- Fix pip install --ignore-installed to handle stale act cache

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 23:03:09 +02:00
dc2626e020 Added test for log 2025-07-21 12:18:07 +02:00
7bc0f32145 Added cypress tests 2025-07-08 17:16:57 +02:00