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
+25 -23
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="de">
<html lang="{{ language() }}">
<head>
@@ -204,19 +204,20 @@
</style>
<link rel="stylesheet" href="/static/css/language.css">
</head>
{% macro event_card(concert, linked=false) %}
<a href="/concerts/{{ concert.id }}" class="concert-card{% if linked %} linked-event{% endif %}">
<span class="event-category">{{ concert.event_type_label }}</span>
{% if concert.is_invited %}<span class="event-category"> · ✉ Eingeladen</span>{% elif concert.visibility == 'friends' %}<span class="event-category"> · 🔒 Nur Freunde</span>{% elif concert.visibility == 'private' %}<span class="event-category"> · 🔐 Privat</span>{% endif %}
<span class="event-category">{{ concert.event_type_label | t }}</span>
{% if concert.is_invited %}<span class="event-category"> {{ _('· ✉ Eingeladen') }}</span>{% elif concert.visibility == 'friends' %}<span class="event-category"> {{ _('· 🔒 Nur Freunde') }}</span>{% elif concert.visibility == 'private' %}<span class="event-category"> {{ _('· 🔐 Privat') }}</span>{% endif %}
<div class="concert-artist">
{% if concert.event_type == 'festival' %}🎪{% elif concert.event_type == 'other' %}🥂{% else %}🎸{% endif %}
{{ concert.artist }}
</div>
<div class="concert-meta">
<div class="concert-date">
📅 {{ concert.date }}{% if concert.end_date %} {{ concert.end_date }}{% else %} um {{ concert.time }} Uhr{% endif %}
📅 {{ concert.date }}{% if concert.end_date %} {{ concert.end_date }}{% else %} {{ _('um') }} {{ concert.time }} {{ _('Uhr') }}{% endif %}
</div>
<div>📍 {{ concert.venue }}</div>
</div>
@@ -233,7 +234,7 @@
<div>
<h1>
{% if archive %}☠ Vergangene Veranstaltungen{% else %}<img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle">{% endif %}
{% if archive %}{{ _('☠ Vergangene Veranstaltungen') }}{% else %}<img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle">{% endif %}
</h1>
<div class="subtitle">
@@ -246,42 +247,43 @@
<div class="header-actions">
<a href="{% if archive %}/{% else %}/events/past{% endif %}" class="admin-button">
{% if archive %}⚡ Bevorstehend{% else %}☠ Vergangene{% endif %}
{% if archive %}{{ _('⚡ Bevorstehend') }}{% else %}{{ _('☠ Vergangene') }}{% endif %}
</a>
<a
href="/concerts/new"
class="new-concert-button"
>
+ Veranstaltung hinzufügen
{{ _('+ Veranstaltung hinzufügen') }}
</a>
{% include '_language_switch.html' %}
{% include '_user_menu.html' %}
</div>
</header>
<section class="search-panel" aria-label="Suche">
<section class="search-panel" aria-label="{{ _('Suche') }}">
<form class="search-form" method="get" action="{% if archive %}/events/past{% else %}/{% endif %}">
<input type="search" name="q" value="{{ search_query }}" placeholder="Veranstaltungen, Orte oder User suchen …" aria-label="Veranstaltungen oder User suchen">
<button class="new-concert-button" type="submit">Suchen</button>
<input type="search" name="q" value="{{ search_query }}" placeholder="{{ _('Veranstaltungen, Orte oder User suchen …') }}" aria-label="{{ _('Veranstaltungen oder User suchen') }}">
<button class="new-concert-button" type="submit">{{ _('Suchen') }}</button>
<details class="advanced-filters" {% if filters.date_from or filters.date_to or filters.venue or filters.country or filters.category %}open{% endif %}>
<summary>Weitere Filter{% if filters.date_from or filters.date_to or filters.venue or filters.country or filters.category %} · aktiv{% endif %}</summary>
<summary>{{ _('Weitere Filter') }}{% if filters.date_from or filters.date_to or filters.venue or filters.country or filters.category %} {{ _('· aktiv') }}{% endif %}</summary>
<div class="advanced-filter-fields">
<label>Von Datum<input type="date" name="date_from" value="{{ filters.date_from }}"></label>
<label>Bis Datum<input type="date" name="date_to" value="{{ filters.date_to }}"></label>
<label>Ort / Location<input type="search" name="venue" value="{{ filters.venue }}" placeholder="z. B. Düsseldorf"></label>
<label>Land<input type="search" name="country" value="{{ filters.country }}" placeholder="z. B. Deutschland"></label>
<label>Kategorie<select name="category"><option value="">Alle Kategorien</option><option value="concert" {% if filters.category == 'concert' %}selected{% endif %}>Konzert</option><option value="festival" {% if filters.category == 'festival' %}selected{% endif %}>Festival</option><option value="other" {% if filters.category == 'other' %}selected{% endif %}>Sonstiges</option></select></label>
<label>{{ _('Von Datum') }}<input type="date" name="date_from" value="{{ filters.date_from }}"></label>
<label>{{ _('Bis Datum') }}<input type="date" name="date_to" value="{{ filters.date_to }}"></label>
<label>{{ _('Ort / Location') }}<input type="search" name="venue" value="{{ filters.venue }}" placeholder="{{ _('z. B. Düsseldorf') }}"></label>
<label>{{ _('Land') }}<input type="search" name="country" value="{{ filters.country }}" placeholder="{{ _('z. B. Deutschland') }}"></label>
<label>{{ _('Kategorie') }}<select name="category"><option value="">{{ _('Alle Kategorien') }}</option><option value="concert" {% if filters.category == 'concert' %}selected{% endif %}>{{ _('Konzert') }}</option><option value="festival" {% if filters.category == 'festival' %}selected{% endif %}>Festival</option><option value="other" {% if filters.category == 'other' %}selected{% endif %}>{{ _('Sonstiges') }}</option></select></label>
</div>
{% if filters.date_from or filters.date_to or filters.venue or filters.country or filters.category %}<a class="clear-filters" href="{% if archive %}/events/past{% else %}/{% endif %}{% if search_query %}?q={{ search_query|urlencode }}{% endif %}">Filter löschen</a>{% endif %}
{% if filters.date_from or filters.date_to or filters.venue or filters.country or filters.category %}<a class="clear-filters" href="{% if archive %}/events/past{% else %}/{% endif %}{% if search_query %}?q={{ search_query|urlencode }}{% endif %}">{{ _('Filter löschen') }}</a>{% endif %}
</details>
</form>
</section>
{% if search_query %}
<h2 class="search-results-title">User zu „{{ search_query }}“</h2>
<h2 class="search-results-title">{{ _('User zu „') }}{{ search_query }}“</h2>
{% if user_results %}
<div class="user-results">
{% for result in user_results %}
@@ -291,8 +293,8 @@
</a>
{% endfor %}
</div>
{% else %}<p class="subtitle">Keine passenden User gefunden.</p>{% endif %}
<h2 class="search-results-title">Veranstaltungen zu „{{ search_query }}“</h2>
{% else %}<p class="subtitle">{{ _('Keine passenden User gefunden.') }}</p>{% endif %}
<h2 class="search-results-title">{{ _('Veranstaltungen zu „') }}{{ search_query }}“</h2>
{% endif %}
@@ -315,7 +317,7 @@
{% if archive and past_concerts %}
<h2 class="past-concerts-title">
Archiv
{{ _('Archiv') }}
</h2>
{% for concert in past_concerts %}
@@ -339,11 +341,11 @@
<div class="empty-state">
<h2>
{% if archive %}Noch keine vergangenen Veranstaltungen ☠{% else %}Noch keine Veranstaltungen 🎸{% endif %}
{% if archive %}{{ _('Noch keine vergangenen Veranstaltungen ☠') }}{% else %}{{ _('Noch keine Veranstaltungen 🎸') }}{% endif %}
</h2>
<p>
{% if archive %}Hier erscheinen abgeschlossene Veranstaltungen.{% else %}Leg die erste Veranstaltung an!{% endif %}
{% if archive %}{{ _('Hier erscheinen abgeschlossene Veranstaltungen.') }}{% else %}{{ _('Leg die erste Veranstaltung an!') }}{% endif %}
</p>
</div>