From a758b9a6b481fe4e8a1e0a8b17713ee5884d7aff Mon Sep 17 00:00:00 2001 From: Dogfather Date: Sun, 2 Aug 2026 00:30:20 +0200 Subject: [PATCH] Mehrsprachigkeit: Sprachumschalter in der Navigation (DE/CH/EN/FR/PT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neues i18n-System, komplett clientseitig (kein Build-Tool): - main.js: I18N_CORE übersetzt Navigation, Fußzeile und Musik-Widget auf JEDER Seite (Markennamen wie DogFather/HasiDog/Casper/Team Dogi bleiben bewusst in jeder Sprache gleich). Sprachumschalter (Flagge + Kürzel) neu im Nav-Bar, per Klick wählbar, Wahl landet in localStorage und bleibt beim Klicken durch die Seite erhalten. - Jeder komplett neue Besuch (kein gespeicherter Wert) startet IMMER auf Deutsch - keine automatische Browsersprache-Erkennung, wie gewünscht. - assets/js/i18n-index.js: vollständiges Wörterbuch für die Startseite (Hero, Charaktere, Foto-Collage, Stats, "Was dich erwartet", Team-Vorschau, Aktionen, Galerie, Testimonial, finaler CTA) inkl. der dynamischen Live-Status-Texte. - Schweizer Hochdeutsch (de-CH) korrekt mit ss statt ß angelegt (z.B. "Musik-Feld schliessen"). - Weitere Unterseiten (streamer.html, vanvan.html, ...) bekommen ihr eigenes Wörterbuch schrittweise in künftigen Runden - Navigation/ Fußzeile/Musik-Widget sind aber schon jetzt überall mehrsprachig. --- assets/css/main.css | 7 + assets/js/i18n-index.js | 255 +++++++++++++++++++++++++++++++ assets/js/main.js | 331 +++++++++++++++++++++++++++++++++------- index.html | 143 +++++++++-------- 4 files changed, 619 insertions(+), 117 deletions(-) create mode 100644 assets/js/i18n-index.js diff --git a/assets/css/main.css b/assets/css/main.css index 1b3798d..012c99a 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -191,6 +191,13 @@ header.site-header { } .nav-dropdown a:hover, .nav-dropdown a.active { background: var(--bg-alt); color: var(--accent); } +/* Sprachumschalter — schmalerer Dropdown reicht (nur Flagge + 2-stelliges + Kürzel pro Zeile), rechtsbündig statt zentriert, damit er nicht über + den Seitenrand hinausragt. */ +.sprach-schalter > button { font-size: 1.02rem; } +.sprach-schalter .nav-dropdown { min-width: 130px; left: auto; right: 0; transform: none; } +.sprach-schalter .nav-dropdown a { display: flex; align-items: center; gap: .5rem; } + /* "Bewerben" ist der Treffpunkt beider Welten (Team Dogi + Spicy Media) — deshalb bewusst FEST babyblau + rot, unabhängig vom Theme der gerade offenen Seite. Statt einer einfachen 50/50-Teilung ein echtes diff --git a/assets/js/i18n-index.js b/assets/js/i18n-index.js new file mode 100644 index 0000000..82637d1 --- /dev/null +++ b/assets/js/i18n-index.js @@ -0,0 +1,255 @@ +/* ===================================================================== + i18n-index.js — Übersetzungs-Wörterbuch NUR für index.html + + Wird von main.js (applyTranslations) über data-i18n="schluessel" auf + die passenden Elemente angewendet. Nur auf der Startseite eingebunden + — andere Seiten haben aktuell noch kein eigenes Wörterbuch und bleiben + bewusst auf Deutsch (Navigation/Fußzeile/Musik-Widget sind aber überall + mehrsprachig, siehe I18N_CORE in main.js). + + Schweizer Hochdeutsch (de-CH) ist auf dieser Seite überall identisch zu + Deutsch (de) — keiner der Texte hier enthält ein ß. + ===================================================================== */ + +window.I18N_PAGE = { + idx_hero_h1: { + de: "Streamer. Community. HasiDog. Manager.
Gemeinsam stark.", + "de-CH": "Streamer. Community. HasiDog. Manager.
Gemeinsam stark.", + en: "Streamer. Community. HasiDog. Manager.
Stronger together.", + fr: "Streamer. Communauté. HasiDog. Manager.
Plus forts ensemble.", + pt: "Streamer. Comunidade. HasiDog. Manager.
Mais fortes juntos.", + }, + idx_hero_lead: { + de: "Drei Welten, eine Familie: Team Dogi.", + "de-CH": "Drei Welten, eine Familie: Team Dogi.", + en: "Three worlds, one family: Team Dogi.", + fr: "Trois mondes, une famille : Team Dogi.", + pt: "Três mundos, uma família: Team Dogi.", + }, + idx_live_loading: { + de: "Status wird geladen …", + "de-CH": "Status wird geladen …", + en: "Loading status …", + fr: "Chargement du statut …", + pt: "Carregando status …", + }, + idx_live_info: { + de: `DogFather geht jeden Tag ab 20 Uhr live — oft schon tagsüber, zu wechselnden Zeiten. Streamplan ansehen →`, + "de-CH": `DogFather geht jeden Tag ab 20 Uhr live — oft schon tagsüber, zu wechselnden Zeiten. Streamplan ansehen →`, + en: `DogFather goes live every day from 8 PM — often earlier too, at varying times. View the schedule →`, + fr: `DogFather est en live tous les jours à partir de 20h — souvent même plus tôt, à des horaires variables. Voir le programme →`, + pt: `DogFather fica ao vivo todos os dias a partir das 20h — muitas vezes já durante o dia, em horários variados. Ver a programação →`, + }, + idx_hero_btn_links: { + de: "Alle Links 🩵", "de-CH": "Alle Links 🩵", + en: "All Links 🩵", fr: "Tous les liens 🩵", pt: "Todos os links 🩵", + }, + idx_hero_btn_team: { + de: "Teil vom Team werden", "de-CH": "Teil vom Team werden", + en: "Join the team", fr: "Rejoindre l'équipe", pt: "Fazer parte da equipe", + }, + idx_hero_card1_tag: { de: "Welt 1", "de-CH": "Welt 1", en: "World 1", fr: "Monde 1", pt: "Mundo 1" }, + idx_hero_card1_desc: { + de: "Streamer, Entertainer, das Herz hinter dem Kanal. Halligalli, Humor und ein offenes Ohr für die Community.", + "de-CH": "Streamer, Entertainer, das Herz hinter dem Kanal. Halligalli, Humor und ein offenes Ohr für die Community.", + en: "Streamer, entertainer, the heart behind the channel. Good times, humor, and an open ear for the community.", + fr: "Streamer, entertainer, le cœur de la chaîne. Bonne humeur, humour et une oreille attentive pour la communauté.", + pt: "Streamer, entertainer, o coração por trás do canal. Diversão, humor e sempre um ouvido aberto para a comunidade.", + }, + idx_hero_card2_tag: { de: "Welt 2", "de-CH": "Welt 2", en: "World 2", fr: "Monde 2", pt: "Mundo 2" }, + idx_hero_card2_desc: { + de: "Die Community, das Maskottchen und Husky Casper — die verspielte, familiäre Seite von Team Dogi.", + "de-CH": "Die Community, das Maskottchen und Husky Casper — die verspielte, familiäre Seite von Team Dogi.", + en: "The community, the mascot, and Husky Casper — the playful, family side of Team Dogi.", + fr: "La communauté, la mascotte et le husky Casper — le côté joueur et familial de Team Dogi.", + pt: "A comunidade, o mascote e o husky Casper — o lado brincalhão e familiar do Team Dogi.", + }, + idx_hero_card3_tag: { de: "Welt 3", "de-CH": "Welt 3", en: "World 3", fr: "Monde 3", pt: "Mundo 3" }, + idx_hero_card3_desc: { + de: "Als Manager bei Spicy Media: Scouts, Creator und faire Zusammenarbeit auf Augenhöhe.", + "de-CH": "Als Manager bei Spicy Media: Scouts, Creator und faire Zusammenarbeit auf Augenhöhe.", + en: "As a manager at Spicy Media: scouts, creators, and fair collaboration at eye level.", + fr: "En tant que manager chez Spicy Media : scouts, creators et une collaboration équitable, d'égal à égal.", + pt: "Como manager na Spicy Media: scouts, creators e uma parceria justa entre iguais.", + }, + idx_char_eyebrow: { + de: "🩵 Die Gesichter der Welten", "de-CH": "🩵 Die Gesichter der Welten", + en: "🩵 The Faces of the Worlds", fr: "🩵 Les visages des mondes", pt: "🩵 Os rostos dos mundos", + }, + idx_char_lead: { + de: "Zwei Charaktere, ein Universum — klick dich in ihre Welt.", + "de-CH": "Zwei Charaktere, ein Universum — klick dich in ihre Welt.", + en: "Two characters, one universe — click your way into their world.", + fr: "Deux personnages, un seul univers — clique pour entrer dans leur monde.", + pt: "Dois personagens, um universo — clique e entre no mundo deles.", + }, + idx_char1_sub: { + de: "Streamer · Entertainer · Welt 1", "de-CH": "Streamer · Entertainer · Welt 1", + en: "Streamer · Entertainer · World 1", fr: "Streamer · Entertainer · Monde 1", pt: "Streamer · Entertainer · Mundo 1", + }, + idx_char2_sub: { + de: "Community · Maskottchen · Welt 2", "de-CH": "Community · Maskottchen · Welt 2", + en: "Community · Mascot · World 2", fr: "Communauté · Mascotte · Monde 2", pt: "Comunidade · Mascote · Mundo 2", + }, + idx_char_go: { + de: "Zur Welt →", "de-CH": "Zur Welt →", + en: "Enter the world →", fr: "Découvrir le monde →", pt: "Ver o mundo →", + }, + idx_collage_eyebrow: { + de: "🩵 Gesichter hinter dem Universum", "de-CH": "🩵 Gesichter hinter dem Universum", + en: "🩵 Faces Behind the Universe", fr: "🩵 Les visages derrière l'univers", pt: "🩵 Rostos por trás do universo", + }, + idx_collage_cap_team: { de: "Team 🩵🐾", "de-CH": "Team 🩵🐾", en: "Team 🩵🐾", fr: "Équipe 🩵🐾", pt: "Equipe 🩵🐾" }, + idx_collage_cap_huskylife: { de: "Husky-Life", "de-CH": "Husky-Life", en: "Husky Life", fr: "Vie de husky", pt: "Vida de husky" }, + idx_collage_cap_nose: { de: "Nase an Nase 🩵", "de-CH": "Nase an Nase 🩵", en: "Nose to Nose 🩵", fr: "Nez à nez 🩵", pt: "Nariz a nariz 🩵" }, + idx_collage_footer: { + de: "DogFather & Casper — die zwei Gesichter hinter dem ganzen Universum. 🩵🐾", + "de-CH": "DogFather & Casper — die zwei Gesichter hinter dem ganzen Universum. 🩵🐾", + en: "DogFather & Casper — the two faces behind the whole universe. 🩵🐾", + fr: "DogFather & Casper — les deux visages derrière tout l'univers. 🩵🐾", + pt: "DogFather & Casper — os dois rostos por trás de todo o universo. 🩵🐾", + }, + idx_stats_team: { + de: "Menschen im Team Dogi", "de-CH": "Menschen im Team Dogi", + en: "People in Team Dogi", fr: "Personnes dans Team Dogi", pt: "Pessoas no Team Dogi", + }, + idx_stats_daily_label: { de: "Täglich", "de-CH": "Täglich", en: "Daily", fr: "Chaque jour", pt: "Diariamente" }, + idx_stats_daily_desc: { + de: "live ab 20 Uhr", "de-CH": "live ab 20 Uhr", + en: "live from 8 PM", fr: "en live dès 20h", pt: "ao vivo a partir das 20h", + }, + idx_stats_worlds: { + de: "Welten, eine Familie", "de-CH": "Welten, eine Familie", + en: "worlds, one family", fr: "mondes, une famille", pt: "mundos, uma família", + }, + idx_stats_family: { + de: "Team Dogi ist Familie", "de-CH": "Team Dogi ist Familie", + en: "Team Dogi is family", fr: "Team Dogi, c'est la famille", pt: "Team Dogi é família", + }, + idx_expect_eyebrow: { + de: "Ein bisschen von allem", "de-CH": "Ein bisschen von allem", + en: "A little bit of everything", fr: "Un peu de tout", pt: "Um pouco de tudo", + }, + idx_expect_h2: { + de: "Was dich im Universum erwartet", "de-CH": "Was dich im Universum erwartet", + en: "What awaits you in the universe", fr: "Ce qui t'attend dans l'univers", pt: "O que te espera no universo", + }, + idx_expect_c1_h3: { de: "🎥 Streamer", "de-CH": "🎥 Streamer", en: "🎥 Streamer", fr: "🎥 Streamer", pt: "🎥 Streamer" }, + idx_expect_c1_p: { + de: "Halligalli, Humor & Hasencontent.", "de-CH": "Halligalli, Humor & Hasencontent.", + en: "Good times, humor & bunny content.", fr: "Bonne humeur, humour et contenu de lapin.", pt: "Diversão, humor e conteúdo de coelho.", + }, + idx_expect_c2_h3: { de: "🕰️ Zeitreise", "de-CH": "🕰️ Zeitreise", en: "🕰️ Timeline", fr: "🕰️ Chronologie", pt: "🕰️ Linha do tempo" }, + idx_expect_c2_p: { + de: "Die Geschichte von DogFather & Team Dogi.", "de-CH": "Die Geschichte von DogFather & Team Dogi.", + en: "The story of DogFather & Team Dogi.", fr: "L'histoire de DogFather et de Team Dogi.", pt: "A história de DogFather e do Team Dogi.", + }, + idx_expect_c3_h3: { de: "🩵 Community", "de-CH": "🩵 Community", en: "🩵 Community", fr: "🩵 Communauté", pt: "🩵 Comunidade" }, + idx_expect_c3_p: { + de: "Team Dogi ist Familie.", "de-CH": "Team Dogi ist Familie.", + en: "Team Dogi is family.", fr: "Team Dogi, c'est la famille.", pt: "Team Dogi é família.", + }, + idx_expect_c4_h3: { de: "🐾 Casper", "de-CH": "🐾 Casper", en: "🐾 Casper", fr: "🐾 Casper", pt: "🐾 Casper" }, + idx_expect_c4_p: { + de: "Der Husky an DogFathers Seite.", "de-CH": "Der Husky an DogFathers Seite.", + en: "The husky by DogFather's side.", fr: "Le husky aux côtés de DogFather.", pt: "O husky ao lado de DogFather.", + }, + idx_expect_c5_h3: { de: "🧑‍🤝‍🧑 Modi-Team", "de-CH": "🧑‍🤝‍🧑 Modi-Team", en: "🧑‍🤝‍🧑 Modi-Team", fr: "🧑‍🤝‍🧑 Modi-Team", pt: "🧑‍🤝‍🧑 Modi-Team" }, + idx_expect_c5_p: { + de: "VanVan, Diene & Co.", "de-CH": "VanVan, Diene & Co.", + en: "VanVan, Diene & co.", fr: "VanVan, Diene et l'équipe.", pt: "VanVan, Diene e equipe.", + }, + idx_expect_c6_h3: { de: "🖼️ Medien: DogFather", "de-CH": "🖼️ Medien: DogFather", en: "🖼️ Media: DogFather", fr: "🖼️ Médias : DogFather", pt: "🖼️ Mídia: DogFather" }, + idx_expect_c7_h3: { de: "🐰 Medien: HasiDog", "de-CH": "🐰 Medien: HasiDog", en: "🐰 Media: HasiDog", fr: "🐰 Médias : HasiDog", pt: "🐰 Mídia: HasiDog" }, + idx_expect_c8_h3: { de: "🐺 Medien: Casper", "de-CH": "🐺 Medien: Casper", en: "🐺 Media: Casper", fr: "🐺 Médias : Casper", pt: "🐺 Mídia: Casper" }, + idx_expect_media_p: { + de: "Fotos, Videos & Events.", "de-CH": "Fotos, Videos & Events.", + en: "Photos, videos & events.", fr: "Photos, vidéos et événements.", pt: "Fotos, vídeos e eventos.", + }, + idx_team_h2: { de: "Team Dogi 🩵", "de-CH": "Team Dogi 🩵", en: "Team Dogi 🩵", fr: "Team Dogi 🩵", pt: "Team Dogi 🩵" }, + idx_team_lead: { + de: "Von VanVan bis zu den Scouts — die Menschen, die DOGFATHER UNIVERSE tragen.", + "de-CH": "Von VanVan bis zu den Scouts — die Menschen, die DOGFATHER UNIVERSE tragen.", + en: "From VanVan to the scouts — the people who carry DOGFATHER UNIVERSE.", + fr: "De VanVan aux scouts — les personnes qui portent DOGFATHER UNIVERSE.", + pt: "De VanVan aos scouts — as pessoas que sustentam o DOGFATHER UNIVERSE.", + }, + idx_team_btn_modi: { + de: "Modi-Team ansehen", "de-CH": "Modi-Team ansehen", + en: "View Modi-Team", fr: "Voir l'équipe Modi", pt: "Ver equipe Modi", + }, + idx_team_btn_scouts: { + de: "Scout-Team ansehen", "de-CH": "Scout-Team ansehen", + en: "View Scout Team", fr: "Voir l'équipe Scouts", pt: "Ver equipe Scouts", + }, + idx_aktionen_h2: { + de: "Was gerade läuft", "de-CH": "Was gerade läuft", + en: "What's happening right now", fr: "Ce qui se passe en ce moment", pt: "O que está rolando agora", + }, + idx_aktionen_btn: { + de: "Alle Aktionen & Projekte", "de-CH": "Alle Aktionen & Projekte", + en: "All actions & projects", fr: "Toutes les actions et projets", pt: "Todas as ações e projetos", + }, + idx_galerie_h2: { de: "Impressionen", "de-CH": "Impressionen", en: "Impressions", fr: "Aperçus", pt: "Impressões" }, + idx_galerie_btn: { + de: "Zur ganzen Galerie", "de-CH": "Zur ganzen Galerie", + en: "View the full gallery", fr: "Voir toute la galerie", pt: "Ver galeria completa", + }, + idx_testimonial_eyebrow: { + de: "Was andere sagen", "de-CH": "Was andere sagen", + en: "What others say", fr: "Ce que les autres disent", pt: "O que os outros dizem", + }, + idx_testimonial_quote: { + de: "„Hier stehen bald echte Stimmen aus der Community. 🩵“", + "de-CH": "„Hier stehen bald echte Stimmen aus der Community. 🩵“", + en: "“Real voices from the community will be here soon. 🩵”", + fr: "« De vrais témoignages de la communauté arriveront bientôt ici. 🩵 »", + pt: "“Em breve, depoimentos reais da comunidade estarão aqui. 🩵”", + }, + idx_testimonial_attrib: { + de: "— Team Dogi", "de-CH": "— Team Dogi", en: "— Team Dogi", fr: "— Team Dogi", pt: "— Team Dogi", + }, + idx_testimonial_btn: { + de: "Mehr Stimmen", "de-CH": "Mehr Stimmen", + en: "More voices", fr: "Plus de témoignages", pt: "Mais depoimentos", + }, + idx_cta_eyebrow: { de: "🩵 Bereit?", "de-CH": "🩵 Bereit?", en: "🩵 Ready?", fr: "🩵 Prêt·e ?", pt: "🩵 Pronto?" }, + idx_cta_h2: { + de: "Werde Teil von DOGFATHER UNIVERSE", "de-CH": "Werde Teil von DOGFATHER UNIVERSE", + en: "Become part of DOGFATHER UNIVERSE", fr: "Rejoins DOGFATHER UNIVERSE", pt: "Faça parte do DOGFATHER UNIVERSE", + }, + idx_cta_lead: { + de: "Als Zuschauer:in, Creator, Scout oder Kooperationspartner — hier ist für jeden Platz.", + "de-CH": "Als Zuschauer:in, Creator, Scout oder Kooperationspartner — hier ist für jeden Platz.", + en: "As a viewer, creator, scout, or cooperation partner — there's room for everyone here.", + fr: "Que tu sois spectateur·rice, creator, scout ou partenaire — il y a une place pour toi ici.", + pt: "Como espectador(a), creator, scout ou parceiro(a) — aqui há espaço para todos.", + }, + idx_cta_btn_links: { + de: "Alle Links 🩵", "de-CH": "Alle Links 🩵", + en: "All Links 🩵", fr: "Tous les liens 🩵", pt: "Todos os links 🩵", + }, + idx_cta_btn_bewerben: { + de: "Jetzt bewerben", "de-CH": "Jetzt bewerben", + en: "Apply now", fr: "Postuler maintenant", pt: "Candidate-se agora", + }, + + /* Live-Status (wird von index.html per JS gesetzt, siehe dortiges + Inline-Skript — Schlüssel hier nur zentral gepflegt) */ + idx_live_now: { + de: "Jetzt live: {titel}", "de-CH": "Jetzt live: {titel}", + en: "Live now: {titel}", fr: "En direct : {titel}", pt: "Ao vivo agora: {titel}", + }, + idx_live_now_generic: { + de: "Jetzt live!", "de-CH": "Jetzt live!", + en: "Live now!", fr: "En direct !", pt: "Ao vivo agora!", + }, + idx_live_offline: { + de: "Gerade offline", "de-CH": "Gerade offline", + en: "Currently offline", fr: "Actuellement hors ligne", pt: "Offline no momento", + }, + idx_live_error: { + de: "Status gerade nicht abrufbar", "de-CH": "Status gerade nicht abrufbar", + en: "Status currently unavailable", fr: "Statut actuellement indisponible", pt: "Status indisponível no momento", + }, +}; diff --git a/assets/js/main.js b/assets/js/main.js index 92bb431..3a47673 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -8,38 +8,193 @@ (function () { "use strict"; + /* =================================================================== + Mehrsprachigkeit (i18n) + + Fünf Sprachen: Deutsch (Standard, IMMER die Sprache beim ersten + Besuch), Englisch, Schweizer Hochdeutsch (ß→ss, sonst wie Deutsch), + Französisch, Portugiesisch. Rein clientseitig, kein Build-Tool: + - I18N_CORE deckt die sitewide gemeinsamen Textstücke ab (Navigation, + Fußzeile, Musik-Widget) — läuft auf JEDER Seite, weil main.js + überall eingebunden ist. + - Seiteneigene Inhalte (z.B. die Startseite) bekommen zusätzlich ein + eigenes Wörterbuch über window.I18N_PAGE (siehe assets/js/i18n-*.js), + das per data-i18n="schluessel" auf Elemente gemappt wird. + Markennamen (DogFather, HasiDog, Casper, VanVan, Team Dogi, + Modi-Team, Scout-Team, Streamer, Creator, Manager, Community) bleiben + bewusst in JEDER Sprache unübersetzt — das ist die Marke. + Gewählte Sprache landet in localStorage und bleibt beim Klicken durch + die Seite erhalten. Ein komplett neuer Besuch (keine Speicherung) + startet immer auf Deutsch — nie automatische Browsersprache-Erkennung. + =================================================================== */ + const SPRACHEN = [ + { code: "de", flagge: "🇩🇪", kurz: "DE" }, + { code: "de-CH", flagge: "🇨🇭", kurz: "CH" }, + { code: "en", flagge: "🇬🇧", kurz: "EN" }, + { code: "fr", flagge: "🇫🇷", kurz: "FR" }, + { code: "pt", flagge: "🇵🇹", kurz: "PT" }, + ]; + const SPRACHE_SCHLUESSEL = "dogi-sprache"; + + function getSprache() { + const gespeichert = localStorage.getItem(SPRACHE_SCHLUESSEL); + return SPRACHEN.some((s) => s.code === gespeichert) ? gespeichert : "de"; + } + + function setSprache(code) { + if (!SPRACHEN.some((s) => s.code === code)) return; + localStorage.setItem(SPRACHE_SCHLUESSEL, code); + document.documentElement.setAttribute("lang", code); + renderHeader(); + renderFooter(); + applyTranslations(); + applyMusicWidgetSprache(); + // Seiteneigene Skripte (z.B. der Live-Status auf index.html) können + // darauf reagieren, um dynamisch gesetzten Text (nicht über + // data-i18n) ebenfalls neu zu übersetzen. + document.dispatchEvent(new CustomEvent("dogi-sprache-geaendert", { detail: { sprache: code } })); + } + + const I18N_CORE = { + nav_start: { de: "Start", "de-CH": "Start", en: "Home", fr: "Accueil", pt: "Início" }, + nav_dogfather: { de: "DogFather", "de-CH": "DogFather", en: "DogFather", fr: "DogFather", pt: "DogFather" }, + nav_dogfather_person: { de: "Die Person", "de-CH": "Die Person", en: "The Person", fr: "La personne", pt: "A pessoa" }, + nav_dogfather_streamer: { de: "Streamer", "de-CH": "Streamer", en: "Streamer", fr: "Streamer", pt: "Streamer" }, + nav_dogfather_casper: { de: "Casper", "de-CH": "Casper", en: "Casper", fr: "Casper", pt: "Casper" }, + nav_dogfather_vanvan: { de: "VanVan", "de-CH": "VanVan", en: "VanVan", fr: "VanVan", pt: "VanVan" }, + nav_dogfather_zeitreise: { de: "Zeitreise", "de-CH": "Zeitreise", en: "Timeline", fr: "Chronologie", pt: "Linha do tempo" }, + nav_hasidog: { de: "HasiDog", "de-CH": "HasiDog", en: "HasiDog", fr: "HasiDog", pt: "HasiDog" }, + nav_hasidog_welt: { de: "HasiDog-Welt", "de-CH": "HasiDog-Welt", en: "HasiDog World", fr: "Monde HasiDog", pt: "Mundo HasiDog" }, + nav_hasidog_modi: { de: "Modi-Team", "de-CH": "Modi-Team", en: "Modi-Team", fr: "Modi-Team", pt: "Modi-Team" }, + nav_hasidog_community: { de: "Community", "de-CH": "Community", en: "Community", fr: "Communauté", pt: "Comunidade" }, + nav_hasidog_stimmen: { de: "Stimmen", "de-CH": "Stimmen", en: "Voices", fr: "Témoignages", pt: "Depoimentos" }, + nav_hasidog_streamplan: { de: "Streamplan", "de-CH": "Streamplan", en: "Stream schedule", fr: "Programme des streams", pt: "Programação das lives" }, + nav_manager: { de: "Manager", "de-CH": "Manager", en: "Manager", fr: "Manager", pt: "Manager" }, + nav_manager_dogfather: { de: "Manager DogFather", "de-CH": "Manager DogFather", en: "Manager DogFather", fr: "Manager DogFather", pt: "Manager DogFather" }, + nav_manager_scouts: { de: "Scout-Team", "de-CH": "Scout-Team", en: "Scout Team", fr: "Équipe Scouts", pt: "Equipe Scouts" }, + nav_manager_creator: { de: "Creator", "de-CH": "Creator", en: "Creator", fr: "Creator", pt: "Creator" }, + nav_manager_werte: { de: "Werte", "de-CH": "Werte", en: "Values", fr: "Valeurs", pt: "Valores" }, + nav_manager_bewerben: { de: "Bewerben", "de-CH": "Bewerben", en: "Apply", fr: "Postuler", pt: "Candidatar-se" }, + nav_medien: { de: "Medien", "de-CH": "Medien", en: "Media", fr: "Médias", pt: "Mídia" }, + nav_medien_dogfather: { de: "DogFather", "de-CH": "DogFather", en: "DogFather", fr: "DogFather", pt: "DogFather" }, + nav_medien_hasidog: { de: "HasiDog", "de-CH": "HasiDog", en: "HasiDog", fr: "HasiDog", pt: "HasiDog" }, + nav_medien_casper: { de: "Casper", "de-CH": "Casper", en: "Casper", fr: "Casper", pt: "Casper" }, + nav_links: { de: "Links", "de-CH": "Links", en: "Links", fr: "Liens", pt: "Links" }, + nav_cta_bewerben: { de: "Bewerben 🩵", "de-CH": "Bewerben 🩵", en: "Apply 🩵", fr: "Postuler 🩵", pt: "Candidatar-se 🩵" }, + nav_toggle_aria: { de: "Menü öffnen", "de-CH": "Menü öffnen", en: "Open menu", fr: "Ouvrir le menu", pt: "Abrir menu" }, + sprache_aria: { de: "Sprache wählen", "de-CH": "Sprache wählen", en: "Choose language", fr: "Choisir la langue", pt: "Escolher idioma" }, + brand_tagline: { + de: "Streamer · Community · HasiDog · Manager", + "de-CH": "Streamer · Community · HasiDog · Manager", + en: "Streamer · Community · HasiDog · Manager", + fr: "Streamer · Communauté · HasiDog · Manager", + pt: "Streamer · Comunidade · HasiDog · Manager", + }, + footer_tagline: { + de: "Streamer. Community. HasiDog. Manager. Gemeinsam stark.", + "de-CH": "Streamer. Community. HasiDog. Manager. Gemeinsam stark.", + en: "Streamer. Community. HasiDog. Manager. Stronger together.", + fr: "Streamer. Communauté. HasiDog. Manager. Plus forts ensemble.", + pt: "Streamer. Comunidade. HasiDog. Manager. Mais fortes juntos.", + }, + footer_h4_hasidog_manager: { de: "HasiDog & Manager", "de-CH": "HasiDog & Manager", en: "HasiDog & Manager", fr: "HasiDog et Manager", pt: "HasiDog e Manager" }, + footer_h4_mehr: { de: "Mehr", "de-CH": "Mehr", en: "More", fr: "Plus", pt: "Mais" }, + footer_scouts: { de: "Scouts", "de-CH": "Scouts", en: "Scouts", fr: "Scouts", pt: "Scouts" }, + footer_alle_links: { de: "Alle Links", "de-CH": "Alle Links", en: "All links", fr: "Tous les liens", pt: "Todos os links" }, + footer_kontakt: { de: "Kontakt", "de-CH": "Kontakt", en: "Contact", fr: "Contact", pt: "Contato" }, + footer_copyright: { + de: "© {jahr} DogFather Universe. Team Dogi ist Familie 🩵", + "de-CH": "© {jahr} DogFather Universe. Team Dogi ist Familie 🩵", + en: "© {jahr} DogFather Universe. Team Dogi is family 🩵", + fr: "© {jahr} DogFather Universe. Team Dogi, c'est la famille 🩵", + pt: "© {jahr} DogFather Universe. Team Dogi é família 🩵", + }, + footer_impressum: { de: "Impressum & Datenschutz", "de-CH": "Impressum & Datenschutz", en: "Legal notice & privacy", fr: "Mentions légales et confidentialité", pt: "Aviso legal e privacidade" }, + music_title: { de: "Musik von Team Dogi 🎵", "de-CH": "Musik von Team Dogi 🎵", en: "Music from Team Dogi 🎵", fr: "Musique de Team Dogi 🎵", pt: "Música do Team Dogi 🎵" }, + music_close_aria: { de: "Musik-Feld schließen", "de-CH": "Musik-Feld schliessen", en: "Close music panel", fr: "Fermer le panneau musique", pt: "Fechar painel de música" }, + music_intro: { + de: "Läuft weiter, solange du auf dieser Seite bleibst. 🩵", + "de-CH": "Läuft weiter, solange du auf dieser Seite bleibst. 🩵", + en: "Keeps playing as long as you stay on this page. 🩵", + fr: "Continue de jouer tant que tu restes sur cette page. 🩵", + pt: "Continua tocando enquanto você ficar nesta página. 🩵", + }, + music_toggle_title: { de: "Musik von Hasidog & Van-Van", "de-CH": "Musik von Hasidog & Van-Van", en: "Music from Hasidog & Van-Van", fr: "Musique de Hasidog et Van-Van", pt: "Música de Hasidog e Van-Van" }, + music_spotify_suffix: { de: "auf Spotify anhören", "de-CH": "auf Spotify anhören", en: "listen on Spotify", fr: "écouter sur Spotify", pt: "ouvir no Spotify" }, + }; + + function t(schluessel) { + const eintrag = I18N_CORE[schluessel]; + if (!eintrag) return schluessel; + const sprache = getSprache(); + return eintrag[sprache] || eintrag.de || schluessel; + } + + /* Elemente mit data-i18n="schluessel" übersetzen — Wörterbuch kommt aus + window.I18N_PAGE (seiteneigen, z.B. assets/js/i18n-index.js). Fehlt + der Schlüssel oder die Sprache, bleibt der bestehende (deutsche) Text + einfach stehen, statt kaputtzugehen. */ + function applyTranslations() { + const sprache = getSprache(); + document.documentElement.setAttribute("lang", sprache); + + const seitenWoerterbuch = window.I18N_PAGE || {}; + document.querySelectorAll("[data-i18n]").forEach((el) => { + const eintrag = seitenWoerterbuch[el.getAttribute("data-i18n")]; + if (!eintrag) return; + const text = eintrag[sprache] || eintrag.de; + if (text) el.innerHTML = text; + }); + + // data-i18n-attr="alt:schluessel;title:andererSchluessel" — für Attribute + // statt Textinhalt (z.B. Bild-Alt-Texte). + document.querySelectorAll("[data-i18n-attr]").forEach((el) => { + el.getAttribute("data-i18n-attr") + .split(";") + .map((teil) => teil.trim()) + .filter(Boolean) + .forEach((teil) => { + const [attr, schluessel] = teil.split(":"); + const eintrag = seitenWoerterbuch[schluessel]; + if (!eintrag) return; + const text = eintrag[sprache] || eintrag.de; + if (text) el.setAttribute(attr, text); + }); + }); + } + /* ---------- Navigationsstruktur (an einer Stelle pflegen) ---------- */ const NAV = [ - { label: "Start", href: "index.html" }, + { key: "nav_start", href: "index.html" }, { - label: "DogFather", + key: "nav_dogfather", children: [ - { label: "Die Person", href: "dogfather.html" }, - { label: "Streamer", href: "streamer.html" }, - { label: "Casper", href: "casper.html" }, - { label: "VanVan", href: "vanvan.html" }, - { label: "Zeitreise", href: "zeitreise.html" }, + { key: "nav_dogfather_person", href: "dogfather.html" }, + { key: "nav_dogfather_streamer", href: "streamer.html" }, + { key: "nav_dogfather_casper", href: "casper.html" }, + { key: "nav_dogfather_vanvan", href: "vanvan.html" }, + { key: "nav_dogfather_zeitreise", href: "zeitreise.html" }, ], }, { - label: "HasiDog", + key: "nav_hasidog", children: [ - { label: "HasiDog-Welt", href: "hasidog.html" }, - { label: "Modi-Team", href: "team-modis.html" }, - { label: "Community", href: "community.html" }, - { label: "Stimmen", href: "stimmen.html" }, + { key: "nav_hasidog_welt", href: "hasidog.html" }, + { key: "nav_hasidog_modi", href: "team-modis.html" }, + { key: "nav_hasidog_community", href: "community.html" }, + { key: "nav_hasidog_stimmen", href: "stimmen.html" }, // Streamplan zurück unter HasiDog — Wunsch Dogi. - { label: "Streamplan", href: "streamplan.html" }, + { key: "nav_hasidog_streamplan", href: "streamplan.html" }, ], }, { - label: "Manager", + key: "nav_manager", children: [ - { label: "Manager DogFather", href: "manager.html" }, - { label: "Scout-Team", href: "team-scouts.html" }, - { label: "Creator", href: "creator.html" }, - { label: "Werte", href: "werte.html" }, - { label: "Bewerben", href: "bewerben.html" }, + { key: "nav_manager_dogfather", href: "manager.html" }, + { key: "nav_manager_scouts", href: "team-scouts.html" }, + { key: "nav_manager_creator", href: "creator.html" }, + { key: "nav_manager_werte", href: "werte.html" }, + { key: "nav_manager_bewerben", href: "bewerben.html" }, ], }, { @@ -47,14 +202,14 @@ // Foto/Video/Aktionen getrennt zu sein — Wunsch von Dogi: pro Welt // (DogFather/HasiDog/Casper) gibt es EINE Seite mit allem // kategorisiert (Fotos, Videos, Events), siehe medien-*.html. - label: "Medien", + key: "nav_medien", children: [ - { label: "DogFather", href: "medien-dogfather.html" }, - { label: "HasiDog", href: "medien-hasidog.html" }, - { label: "Casper", href: "medien-casper.html" }, + { key: "nav_medien_dogfather", href: "medien-dogfather.html" }, + { key: "nav_medien_hasidog", href: "medien-hasidog.html" }, + { key: "nav_medien_casper", href: "medien-casper.html" }, ], }, - { label: "Links", href: "links.html" }, + { key: "nav_links", href: "links.html" }, ]; /* Aktuelle Datei — wird beim nahtlosen Seitenwechsel mit aktualisiert. */ @@ -66,23 +221,41 @@ function renderNavItem(item) { if (!item.children) { - return `${item.label}`; + return `${t(item.key)}`; } const activeChild = item.children.some((c) => isActive(c.href)); return ` `; } + /* Sprachumschalter — eigener nav-group-artiger Dropdown, zeigt die + aktuell gewählte Sprache (Flagge + Kürzel) und listet beim Klick die + übrigen 4 Sprachen auf. */ + function renderSprachSchalter() { + const aktuell = getSprache(); + const aktiv = SPRACHEN.find((s) => s.code === aktuell) || SPRACHEN[0]; + return ` + `; + } + function renderHeader() { const el = document.getElementById("site-header"); if (!el) return; @@ -92,13 +265,14 @@ DOGFATHER UNIVERSE - Streamer · Community · HasiDog · Manager + ${t("brand_tagline")} - + `; @@ -120,6 +294,12 @@ }); }); + el.querySelectorAll(".sprach-schalter [data-sprache]").forEach((link) => { + link.addEventListener("click", (e) => { + e.preventDefault(); + setSprache(link.dataset.sprache); + }); + }); } /* Klick außerhalb schließt offene Menüs. Bewusst NUR EINMAL angemeldet — @@ -142,43 +322,43 @@
🩵 DOGFATHER UNIVERSE

- Streamer. Community. HasiDog. Manager. Gemeinsam stark. + ${t("footer_tagline")}

DogFather

-

HasiDog & Manager

+

${t("footer_h4_hasidog_manager")}

-

Mehr

+

${t("footer_h4_mehr")}

`; } @@ -228,22 +408,22 @@ widget.innerHTML = ` `; + title="${t("music_toggle_title")}">🎵`; document.body.appendChild(widget); const panel = document.getElementById("music-panel"); @@ -271,6 +451,23 @@ }); } + /* Das Musik-Widget wird nur EINMAL erzeugt (damit der laufende Player + beim Seitenwechsel nicht abreißt) — bei Sprachwechsel deshalb nicht + neu rendern, sondern nur die Texte/Attribute darin austauschen. */ + function applyMusicWidgetSprache() { + const titel = document.getElementById("music-title-text"); + if (titel) titel.textContent = t("music_title"); + const schliessen = document.getElementById("music-close"); + if (schliessen) schliessen.setAttribute("aria-label", t("music_close_aria")); + const intro = document.getElementById("music-intro-text"); + if (intro) intro.textContent = t("music_intro"); + const toggle = document.getElementById("music-toggle"); + if (toggle) toggle.setAttribute("title", t("music_toggle_title")); + document.querySelectorAll("#music-widget iframe[data-artist]").forEach((f) => { + f.setAttribute("title", `${f.dataset.artist} ${t("music_spotify_suffix")}`); + }); + } + /* =================================================================== Musik-Knopf: Chamäleon-Farben @@ -487,6 +684,11 @@ } alterInhalt.replaceWith(neuerInhalt); + // Seiteneigenes i18n-Wörterbuch zurücksetzen — sonst könnte das der + // VORHERIGEN Seite (z.B. Startseite) fälschlich auf der neuen Seite + // weiterwirken. Hat die neue Seite ein eigenes (z.B. i18n-index.js), + // setzt skripteUebernehmen() es gleich neu. + window.I18N_PAGE = null; // Seitenmotiv + Sonderklassen mitnehmen: Die Bildvariablen hängen am // , damit das Motiv die ganze Seite trägt — beim Wechsel also mit @@ -521,6 +723,7 @@ musikKnopfFarbenAnpassen(); // Chamäleon-Knopf an die neue Seite anpassen await skripteUebernehmen(dok); + applyTranslations(); // seiteneigene data-i18n-Elemente der neuen Seite übersetzen } function nahtloseNavigationAktivieren() { @@ -569,5 +772,23 @@ renderMusicWidget(); musikKnopfFarbenAnpassen(); nahtloseNavigationAktivieren(); + applyTranslations(); // seiteneigene data-i18n-Elemente (z.B. Startseite) übersetzen }); + + /* Kleine öffentliche Schnittstelle für seiteneigene Inline-Skripte + (z.B. der Live-Status auf index.html), die außerhalb dieser IIFE + ebenfalls die aktuelle Sprache übersetzen wollen. */ + window.dogiSprache = getSprache; + window.dogiUebersetzen = function (schluessel, ersetzungen) { + const eintrag = (window.I18N_PAGE || {})[schluessel]; + if (!eintrag) return schluessel; + const sprache = getSprache(); + let text = eintrag[sprache] || eintrag.de || schluessel; + if (ersetzungen) { + Object.keys(ersetzungen).forEach((platzhalter) => { + text = text.replace(`{${platzhalter}}`, ersetzungen[platzhalter]); + }); + } + return text; + }; })(); diff --git a/index.html b/index.html index abcc91e..b6b8d24 100644 --- a/index.html +++ b/index.html @@ -49,34 +49,34 @@
🩵 DOGFATHER UNIVERSE -

Streamer. Community. HasiDog. Manager.
Gemeinsam stark.

-

Drei Welten, eine Familie: Team Dogi.

+

Streamer. Community. HasiDog. Manager.
Gemeinsam stark.

+

Drei Welten, eine Familie: Team Dogi.

- Status wird geladen … -

DogFather geht jeden Tag ab 20 Uhr live — oft schon tagsüber, zu wechselnden Zeiten. Streamplan ansehen →

+ Status wird geladen … +

DogFather geht jeden Tag ab 20 Uhr live — oft schon tagsüber, zu wechselnden Zeiten. Streamplan ansehen →

@@ -85,9 +85,9 @@
- 🩵 Die Gesichter der Welten + 🩵 Die Gesichter der Welten

DogFather & HasiDog

-

Zwei Charaktere, ein Universum — klick dich in ihre Welt.

+

Zwei Charaktere, ein Universum — klick dich in ihre Welt.

@@ -115,7 +115,7 @@
- 🩵 Gesichter hinter dem Universum + 🩵 Gesichter hinter dem Universum

DogFather & Casper

@@ -128,18 +128,18 @@
DogFather und Casper zusammen
-

Team 🩵🐾

+

Team 🩵🐾

Casper
-

Husky-Life

+

Husky-Life

DogFather und Casper Nase an Nase
-

Nase an Nase 🩵

+

Nase an Nase 🩵

-

DogFather & Casper — die zwei Gesichter hinter dem ganzen Universum. 🩵🐾

+

DogFather & Casper — die zwei Gesichter hinter dem ganzen Universum. 🩵🐾

@@ -147,10 +147,10 @@
-

Menschen im Team Dogi

-

Täglich

live ab 20 Uhr

-

3

Welten, eine Familie

-

🩵

Team Dogi ist Familie

+

Menschen im Team Dogi

+

Täglich

live ab 20 Uhr

+

3

Welten, eine Familie

+

🩵

Team Dogi ist Familie

@@ -158,17 +158,17 @@
@@ -176,12 +176,12 @@
-

Team Dogi 🩵

-

Von VanVan bis zu den Scouts — die Menschen, die DOGFATHER UNIVERSE tragen.

+

Team Dogi 🩵

+

Von VanVan bis zu den Scouts — die Menschen, die DOGFATHER UNIVERSE tragen.

@@ -189,10 +189,10 @@
-

Was gerade läuft

+

Was gerade läuft

@@ -200,7 +200,7 @@
-

Impressionen

+

Impressionen

Casper
VanVan
@@ -208,7 +208,7 @@
DogFather
@@ -216,13 +216,13 @@
- Was andere sagen + Was andere sagen
-

„Hier stehen bald echte Stimmen aus der Community. 🩵"

-

— Team Dogi

+

„Hier stehen bald echte Stimmen aus der Community. 🩵"

+

— Team Dogi

@@ -232,12 +232,12 @@
- 🩵 Bereit? -

Werde Teil von DOGFATHER UNIVERSE

-

Als Zuschauer:in, Creator, Scout oder Kooperationspartner — hier ist für jeden Platz.

+ 🩵 Bereit? +

Werde Teil von DOGFATHER UNIVERSE

+

Als Zuschauer:in, Creator, Scout oder Kooperationspartner — hier ist für jeden Platz.

@@ -247,6 +247,7 @@ +