new feat übersetzer und faq

This commit is contained in:
2026-07-24 15:16:10 +00:00
parent 11b0d20c42
commit 45b37597a9
14 changed files with 1205 additions and 51 deletions

View File

@@ -53,6 +53,7 @@ ALLOWED_MIME_TYPES = {
}
LOCATION_VIDEO_EXTENSIONS = {".mp4", ".webm", ".mov", ".m4v"}
ONBOARDING_VERSION = 2
DEFAULT_INVITATION_GROUPS = [
{
@@ -330,8 +331,8 @@ TEXTS = {
"hero_headline_with_group": "Willkommen zu unserer Hochzeit, {name}",
"hero_text_single": "Wir freuen uns riesig, diesen besonderen Tag am 04.09.26 mit dir zu feiern.",
"hero_text_group": "Wir freuen uns riesig, diesen besonderen Tag am 04.09.26 mit euch zu feiern.",
"hero_app_hint_single": "In dieser Webapp kannst du zu- oder absagen, Fotos hochladen, die Galerie ansehen und alle wichtigen Infos rund um den Tag finden. Bitte gib deine Zu- oder Absage möglichst bald ab.",
"hero_app_hint": "In dieser Webapp könnt ihr zu- oder absagen, Fotos hochladen, die Galerie ansehen und alle wichtigen Infos rund um den Tag finden. Bitte gebt eure Zu- oder Absage möglichst bald ab.",
"hero_app_hint_single": "In dieser Webapp kannst du zu- oder absagen, Fotos hochladen, die Galerie ansehen und alle wichtigen Infos rund um den Tag finden. Im Header findest du außerdem unsere FAQ und eine Deutsch-Englisch-Übersetzungshilfe. Bitte gib deine Zu- oder Absage möglichst bald ab.",
"hero_app_hint": "In dieser Webapp könnt ihr zu- oder absagen, Fotos hochladen, die Galerie ansehen und alle wichtigen Infos rund um den Tag finden. Im Header findet ihr außerdem unsere FAQ und eine Deutsch-Englisch-Übersetzungshilfe. Bitte gebt eure Zu- oder Absage möglichst bald ab.",
"to_guest_area": "Zum Gästebereich",
"schedule_text": "15:00 Trauung, 17:00 Empfang, 19:00 Dinner.",
"schedule_intro": "Hier findet ihr den Ablauf für unseren Hochzeitstag.",
@@ -373,6 +374,7 @@ TEXTS = {
"gifts_teaser": "Wir würden uns über diese Geschenke sehr freuen...",
"gifts_reveal_button": "Wunsch aufdecken",
"gifts_image_alt": "Ein großer Haufen Geldscheine",
"gifts_money_label": "Moneten",
"gifts_caption": "Trommelwirbel... in unserer 70-Quadratmeter-Wohnung ist leider kein Platz mehr für Materielles.",
"gifts_text": "Also: Money! Ein finanzieller Beitrag zu unserer Reise nach der Hochzeit ist herzlich willkommen.",
"gallery_uploaded_by": "von {name}",
@@ -409,6 +411,57 @@ TEXTS = {
"flash_admin_only": "Dieser Bereich ist nur für Admins verfügbar.",
"dashboard": "Dashboard",
"back": "Zurück",
"faq": "Häufig gestellte Fragen",
"faq_intro": "Hier findet ihr Antworten auf die wichtigsten Fragen rund um unsere Hochzeit.",
"faq_deadline_question": "Bis wann sollen wir zu- oder absagen?",
"faq_deadline_answer": "Bitte gebt uns bis spätestens 4. August 2026 über den Bereich „Zu-/Absage“ Bescheid.",
"faq_change_question": "Kann ich meine Antwort später noch ändern?",
"faq_change_answer": "Ja, ihr könnt eure Zu- oder Absage jederzeit bis zum Ende der Rückmeldefrist anpassen.",
"faq_when_where_question": "Wann und wo findet die Hochzeit statt?",
"faq_when_where_answer": "Unsere Hochzeit beginnt um 15:30 Uhr auf der Hochheimer Terrasse. Weitere Informationen findet ihr auf der Location-Seite.",
"faq_parking_question": "Gibt es Parkmöglichkeiten?",
"faq_parking_answer": "In der Hochheimer Altstadt und in der näheren Umgebung gibt es viele Parkmöglichkeiten. Plant bitte etwas Zeit für die Parkplatzsuche und den anschließenden Fußweg ein.",
"faq_hotel_question": "Wo können wir übernachten?",
"faq_hotel_answer": "Für eine möglichst kurze Anreise empfehlen wir euch eine Unterkunft direkt in Hochheim oder in der näheren Umgebung. Eine Auswahl findet ihr auf unserer Hotel-Seite.",
"faq_upload_question": "Wie funktioniert der Foto-Upload?",
"faq_upload_answer": "Über den Bereich „Upload“ könnt ihr Fotos direkt von eurem Smartphone oder Computer hochladen.",
"faq_visibility_question": "Wer kann die hochgeladenen Bilder sehen?",
"faq_visibility_answer": "Die Bilder sind ausschließlich für eingeloggte Hochzeitsgäste sichtbar. Eure eigenen Uploads könnt ihr selbst wieder löschen.",
"translator": "Übersetzungshilfe",
"translator_kicker": "Deutsch & Englisch",
"translator_close": "Übersetzungshilfe schließen",
"translator_direction": "Übersetzungsrichtung",
"translator_swap": "Übersetzungsrichtung wechseln",
"translator_input_label": "Was möchtest du übersetzen?",
"translator_placeholder": "Kurzen Text eingeben …",
"translator_quick_phrases": "Schnelle Sätze",
"translator_phrase_meet": "Kennenlernen",
"translator_phrase_drink": "Getränk anbieten",
"translator_phrase_restroom": "Toilette finden",
"translator_privacy": "Der Text wird erst nach dem Klick an DeepL übertragen. Bitte keine Namen, Kontaktdaten oder vertraulichen Inhalte eingeben.",
"translator_submit": "Mit DeepL übersetzen",
"translator_policy": "Datenschutz bei DeepL",
"language_german": "Deutsch",
"language_english": "Englisch",
"tour_title": "Willkommen in unserer Hochzeits-App",
"tour_progress": "Schritt {current} von {total}",
"tour_dashboard_title": "Alles auf einen Blick",
"tour_dashboard_text": "Im Dashboard findet ihr alle wichtigen Bereiche und Informationen für unseren Hochzeitstag.",
"tour_rsvp_title": "Zu- oder Absage",
"tour_rsvp_text": "Gebt die Rückmeldung für jedes Mitglied eurer Einladung bequem über den Bereich „Zu-/Absage“ ab.",
"tour_photos_title": "Fotos & Galerie",
"tour_photos_text": "Ladet eure Fotos direkt vom Smartphone hoch, entdeckt die gemeinsame Galerie und ladet Erinnerungen herunter.",
"tour_routes_title": "Hotels, Location & Routen",
"tour_routes_text": "Bei den Hotels und der Location könnt ihr Routen mit Google Maps öffnen. Karten werden erst nach eurem Klick geladen.",
"tour_translator_title": "Übersetzungshilfe",
"tour_translator_text": "Über das Buch im Header könnt ihr kurze deutsche oder englische Texte für die Übersetzung mit DeepL vorbereiten.",
"tour_faq_title": "Häufig gestellte Fragen",
"tour_faq_text": "Das Fragezeichen im Header führt euch jederzeit zu den wichtigsten Antworten rund um die Hochzeit.",
"tour_back": "Zurück",
"tour_next": "Weiter",
"tour_skip": "Überspringen",
"tour_finish": "Los gehts",
"tour_restart": "Einführung erneut ansehen",
"countdown_button_label": "Countdown bis zur Hochzeit",
"countdown_until": "Noch",
"countdown_started": "Die Feier hat begonnen",
@@ -474,8 +527,8 @@ TEXTS = {
"hero_headline_with_group": "Welcome to our wedding, {name}",
"hero_text_single": "We are incredibly excited to celebrate this special day on 09/04/26 with you.",
"hero_text_group": "We are incredibly excited to celebrate this special day on 09/04/26 with all of you.",
"hero_app_hint_single": "In this web app, you can send your RSVP, upload photos, view the gallery, and find all important details for the day. Please submit your RSVP soon.",
"hero_app_hint": "In this web app, you can send your RSVP, upload photos, view the gallery, and find all important details for the day. Please submit your RSVP soon.",
"hero_app_hint_single": "In this web app, you can send your RSVP, upload photos, view the gallery, and find all important details for the day. The header also includes our FAQ and a German-English translation helper. Please submit your RSVP soon.",
"hero_app_hint": "In this web app, you can send your RSVP, upload photos, view the gallery, and find all important details for the day. The header also includes our FAQ and a German-English translation helper. Please submit your RSVP soon.",
"to_guest_area": "Open guest area",
"schedule_text": "3:00 PM ceremony, 5:00 PM reception, 7:00 PM dinner.",
"schedule_intro": "Here is the schedule for our wedding day.",
@@ -517,6 +570,7 @@ TEXTS = {
"gifts_teaser": "We would be very happy to receive these gifts...",
"gifts_reveal_button": "Reveal wish",
"gifts_image_alt": "A big pile of cash",
"gifts_money_label": "Moolah",
"gifts_caption": "Drum roll... in our 70-square-meter apartment, we sadly have no room left for material things.",
"gifts_text": "So: Money! A financial contribution to our trip after the wedding is very welcome.",
"gallery_uploaded_by": "by {name}",
@@ -553,6 +607,57 @@ TEXTS = {
"flash_admin_only": "This area is available to admins only.",
"dashboard": "Dashboard",
"back": "Back",
"faq": "Frequently Asked Questions",
"faq_intro": "Here you will find answers to the most important questions about our wedding.",
"faq_deadline_question": "When do we need to RSVP by?",
"faq_deadline_answer": "Please let us know whether you will be attending by 4 August 2026 in the “RSVP” section.",
"faq_change_question": "Can I change my response later?",
"faq_change_answer": "Yes, you can update your RSVP at any time before the response deadline.",
"faq_when_where_question": "When and where is the wedding taking place?",
"faq_when_where_answer": "Our wedding begins at 3:30 PM at Hochheimer Terrasse. You can find more information on the location page.",
"faq_parking_question": "Is parking available?",
"faq_parking_answer": "There are plenty of parking options in Hochheim's old town and the surrounding area. Please allow some extra time to find a space and walk to the venue.",
"faq_hotel_question": "Where can we stay overnight?",
"faq_hotel_answer": "For the shortest possible journey, we recommend staying in Hochheim or nearby. You can find a selection on our hotels page.",
"faq_upload_question": "How does the photo upload work?",
"faq_upload_answer": "You can upload photos directly from your smartphone or computer in the “Upload” section.",
"faq_visibility_question": "Who can see the uploaded photos?",
"faq_visibility_answer": "The photos are visible only to logged-in wedding guests. You can delete your own uploads at any time.",
"translator": "Translation helper",
"translator_kicker": "English & German",
"translator_close": "Close translation helper",
"translator_direction": "Translation direction",
"translator_swap": "Switch translation direction",
"translator_input_label": "What would you like to translate?",
"translator_placeholder": "Enter a short text …",
"translator_quick_phrases": "Quick phrases",
"translator_phrase_meet": "Meeting someone",
"translator_phrase_drink": "Offering a drink",
"translator_phrase_restroom": "Finding the restroom",
"translator_privacy": "Your text is sent to DeepL only after you click the button. Please do not enter names, contact details, or confidential information.",
"translator_submit": "Translate with DeepL",
"translator_policy": "DeepL privacy policy",
"language_german": "German",
"language_english": "English",
"tour_title": "Welcome to our wedding app",
"tour_progress": "Step {current} of {total}",
"tour_dashboard_title": "Everything at a glance",
"tour_dashboard_text": "The dashboard brings together all the important sections and information for our wedding day.",
"tour_rsvp_title": "RSVP",
"tour_rsvp_text": "Submit a response for every member of your invitation conveniently in the “RSVP” section.",
"tour_photos_title": "Photos & gallery",
"tour_photos_text": "Upload photos directly from your phone, explore the shared gallery, and download your favourite memories.",
"tour_routes_title": "Hotels, location & routes",
"tour_routes_text": "You can open Google Maps routes from the hotel and location pages. Maps load only after you click.",
"tour_translator_title": "Translation helper",
"tour_translator_text": "Use the book in the header to prepare short German or English texts for translation with DeepL.",
"tour_faq_title": "Frequently asked questions",
"tour_faq_text": "The question mark in the header takes you to the most important answers about the wedding at any time.",
"tour_back": "Back",
"tour_next": "Next",
"tour_skip": "Skip",
"tour_finish": "Lets go",
"tour_restart": "View introduction again",
"countdown_button_label": "Wedding countdown",
"countdown_until": "Countdown",
"countdown_started": "The celebration has started",
@@ -767,6 +872,7 @@ def init_db() -> None:
name TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'guest',
onboarding_completed INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
)
"""
@@ -811,6 +917,8 @@ def init_db() -> None:
group_cols = table_columns(conn, "groups")
if "role" not in group_cols:
conn.execute("ALTER TABLE groups ADD COLUMN role TEXT NOT NULL DEFAULT 'guest'")
if "onboarding_completed" not in group_cols:
conn.execute("ALTER TABLE groups ADD COLUMN onboarding_completed INTEGER NOT NULL DEFAULT 0")
conn.commit()
@@ -947,14 +1055,38 @@ def welcome():
is_single_guest = member_count <= 1
welcome_headline = t("hero_headline_with_group").format(name=session.get("group_name", ""))
welcome_text = t("hero_text_single") if is_single_guest else t("hero_text_group")
onboarding_row = db.execute(
"SELECT onboarding_completed FROM groups WHERE id = ?",
(int(session["group_id"]),),
).fetchone()
completed_onboarding_version = (
int(onboarding_row["onboarding_completed"] or 0) if onboarding_row else 0
)
show_onboarding = (
request.args.get("tour") == "1"
or completed_onboarding_version < ONBOARDING_VERSION
)
return render_template(
"welcome.html",
welcome_headline=welcome_headline,
welcome_text=welcome_text,
welcome_hint=t("hero_app_hint_single") if is_single_guest else t("hero_app_hint"),
show_onboarding=show_onboarding,
)
@app.post("/onboarding/complete")
@login_required
def complete_onboarding():
db = get_db()
db.execute(
"UPDATE groups SET onboarding_completed = ? WHERE id = ?",
(ONBOARDING_VERSION, int(session["group_id"])),
)
db.commit()
return ("", 204)
@app.get("/gaestebereich")
@app.get("/gästebereich")
@login_required
@@ -1002,6 +1134,21 @@ def dashboard():
return redirect(url_for("guest_area"))
@app.get("/faq")
@login_required
def faq():
faq_items = [
("faq_deadline_question", "faq_deadline_answer"),
("faq_change_question", "faq_change_answer"),
("faq_when_where_question", "faq_when_where_answer"),
("faq_parking_question", "faq_parking_answer"),
("faq_hotel_question", "faq_hotel_answer"),
("faq_upload_question", "faq_upload_answer"),
("faq_visibility_question", "faq_visibility_answer"),
]
return render_template("faq.html", faq_items=faq_items)
@app.route("/rsvp", methods=["GET", "POST"])
@login_required
def rsvp():