Seed-Daten per Fetch von externem Server laden, UI-Sprachumschalter (DE/EN) und KI-Übersetzungs-Prompt-Generator ergänzen

- Beispieldaten werden nicht mehr eingebettet (data/seed.js entfernt),
  sondern beim ersten Start per fetch(EventMap.config.SEED_DATA_URL) von
  einem externen, selbst gehosteten Server geladen (siehe ADR 0001,
  Revision 2026-07-28).
- Neuer UI-Sprachumschalter (Deutsch/Englisch) für die Bedienoberfläche:
  js/i18n/strings.js + js/i18n/i18n.js, Sprachwahl im Menü, Persistenz über
  settings.language, Retranslation ohne Reload.
- Neuer Menüpunkt "Ausflugsziel übersetzen (KI)": erzeugt einen Prompt, der
  eine externe KI bittet, Name/Beschreibungen eines bestehenden Ortes ins
  Englische, Spanische oder Französische zu übersetzen (js/ai/translatePromptBuilder.js,
  js/ui/translatePromptGenerator.js).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 15:16:31 +02:00
co-authored by Claude Sonnet 5
parent 4eb595aa81
commit 2aaff8f680
17 changed files with 1027 additions and 2007 deletions
+46 -20
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EventMap Familienfreundliche Ausflugsziele</title>
<title data-i18n="documentTitle">EventMap Familienfreundliche Ausflugsziele</title>
<link rel="stylesheet" href="vendor/leaflet/leaflet.css" />
<link rel="stylesheet" href="vendor/leaflet.markercluster/MarkerCluster.css" />
@@ -25,8 +25,8 @@
<header class="app-header">
<div class="app-header__row">
<div>
<h1 class="app-header__title">EventMap</h1>
<p class="app-header__subtitle">Familienfreundliche Ausflugsziele auf der Karte</p>
<h1 class="app-header__title" data-i18n="header.title">EventMap</h1>
<p class="app-header__subtitle" data-i18n="header.subtitle">Familienfreundliche Ausflugsziele auf der Karte</p>
</div>
<button
id="menu-toggle-btn"
@@ -34,32 +34,45 @@
class="app-header__menu-toggle"
aria-expanded="false"
aria-controls="app-menu"
data-i18n-aria-label="header.menuToggleLabel"
aria-label="Menü öffnen"
>
<span aria-hidden="true"></span>
</button>
</div>
<nav id="app-menu" class="app-menu" aria-label="Aktionen" hidden>
<button id="locate-btn" type="button" class="btn btn--secondary">
<nav id="app-menu" class="app-menu" data-i18n-aria-label="menu.ariaLabel" aria-label="Aktionen" hidden>
<div class="app-menu__language">
<label class="app-menu__language-label" for="language-select" data-i18n="settings.languageLabel">
Anzeigesprache
</label>
<select id="language-select" class="app-menu__language-select">
<option value="de">Deutsch</option>
<option value="en">English</option>
</select>
</div>
<button id="locate-btn" type="button" class="btn btn--secondary" data-i18n="menu.locate">
Meinen Standort verwenden
</button>
<button id="manual-location-btn" type="button" class="btn btn--secondary">
<button id="manual-location-btn" type="button" class="btn btn--secondary" data-i18n="menu.manualLocation">
Standort manuell eingeben
</button>
<button id="pick-location-btn" type="button" class="btn btn--secondary">
<button id="pick-location-btn" type="button" class="btn btn--secondary" data-i18n="menu.pickLocation">
📍 Standort auf Karte wählen
</button>
<button id="add-place-btn" type="button" class="btn btn--primary">
<button id="add-place-btn" type="button" class="btn btn--primary" data-i18n="menu.addPlace">
+ Neues Ausflugsziel hinzufügen
</button>
<button id="open-prompt-generator-btn" type="button" class="btn btn--secondary">
<button id="open-prompt-generator-btn" type="button" class="btn btn--secondary" data-i18n="menu.promptGenerator">
🤖 Neue Ausflugsziele mit KI erstellen
</button>
<button id="open-import-btn" type="button" class="btn btn--secondary">
<button id="open-translate-prompt-generator-btn" type="button" class="btn btn--secondary" data-i18n="menu.translatePlace">
🌐 Ausflugsziel übersetzen (KI)
</button>
<button id="open-import-btn" type="button" class="btn btn--secondary" data-i18n="menu.import">
📥 KI-Ausflugsziele importieren
</button>
<button id="open-backup-btn" type="button" class="btn btn--secondary">
<button id="open-backup-btn" type="button" class="btn btn--secondary" data-i18n="menu.backup">
💾 Daten sichern/wiederherstellen
</button>
</nav>
@@ -71,6 +84,7 @@
id="app-status-close"
type="button"
class="app-status__close"
data-i18n-aria-label="status.closeLabel"
aria-label="Meldung schließen"
>
@@ -78,24 +92,24 @@
</div>
<main class="app-layout">
<section class="map-panel" aria-label="Karte">
<section class="map-panel" data-i18n-aria-label="map.ariaLabel" aria-label="Karte">
<p id="map-picking-hint" class="map-picking-hint" hidden>
Klicke auf die Karte, um den Standort auszuwählen.
<button id="map-picking-cancel-btn" type="button" class="map-picking-hint__cancel">
<span data-i18n="map.pickingHint">Klicke auf die Karte, um den Standort auszuwählen.</span>
<button id="map-picking-cancel-btn" type="button" class="map-picking-hint__cancel" data-i18n="map.pickingCancel">
Abbrechen
</button>
</p>
<div id="map" class="map-panel__map">
<p class="map-panel__loading">Karte wird geladen…</p>
<p class="map-panel__loading" data-i18n="map.loading">Karte wird geladen…</p>
</div>
</section>
<div id="filter-panel" class="filter-panel" aria-label="Filter und Suche"></div>
<div id="filter-panel" class="filter-panel" data-i18n-aria-label="filterPanel.ariaLabel" aria-label="Filter und Suche"></div>
<aside class="list-panel" aria-label="Ergebnisliste">
<h2 class="list-panel__title">Ausflugsziele</h2>
<aside class="list-panel" data-i18n-aria-label="list.ariaLabel" aria-label="Ergebnisliste">
<h2 class="list-panel__title" data-i18n="list.title">Ausflugsziele</h2>
<div id="place-list" class="list-panel__content">
<p class="list-empty">Lade Ausflugsziele…</p>
<p class="list-empty" data-i18n="list.loading">Lade Ausflugsziele…</p>
</div>
</aside>
</main>
@@ -104,6 +118,7 @@
<div id="place-form-modal" class="place-form-modal" hidden></div>
<div id="manual-location-modal" class="manual-location-modal" hidden></div>
<div id="prompt-generator-modal" class="prompt-generator-modal" hidden></div>
<div id="translate-prompt-generator-modal" class="prompt-generator-modal" hidden></div>
<div id="import-modal" class="import-modal" hidden></div>
<script src="vendor/leaflet/leaflet.js"></script>
@@ -119,7 +134,6 @@
<script src="js/utils/clipboard.js"></script>
<script src="js/utils/download.js"></script>
<script src="js/filters/filterEngine.js"></script>
<script src="data/seed.js"></script>
<script src="js/config/constants.js"></script>
<script src="js/config/categories.js"></script>
<script src="js/models/schema.js"></script>
@@ -127,6 +141,12 @@
<script src="js/store/migrations.js"></script>
<script src="js/store/storage.js"></script>
<script src="js/store/store.js"></script>
<!-- UI-Sprachumschalter (ADR 0002): muss nach store.js (liest persistierte
Settings synchron beim Init) und vor allen UI-Modulen geladen werden. -->
<script src="js/i18n/strings.js"></script>
<script src="js/i18n/i18n.js"></script>
<script src="js/map/geolocation.js"></script>
<script src="js/map/markers.js"></script>
<script src="js/map/mapController.js"></script>
@@ -142,6 +162,12 @@
<script src="js/ai/promptBuilder.js"></script>
<script src="js/ui/promptGenerator.js"></script>
<!-- "Ausflugsziel übersetzen (KI)": erzeugt einen Prompt zum Übersetzen
eines bestehenden Ortes (Name/Beschreibungen) in eine Zielsprache
(Englisch/Spanisch/Französisch), analog zum obigen Prompt-Generator. -->
<script src="js/ai/translatePromptBuilder.js"></script>
<script src="js/ui/translatePromptGenerator.js"></script>
<!-- KI-JSON-Import + Backup-Export/Import (Phase 7): js/import/*.js sind
reine Validierungs-/Datenfunktionen ohne DOM-Zugriff und setzen daher
nur EventMap.utils/models/config/store voraus (bereits geladen);