const SUPPORTED_LANGS = Object.keys(DICT); let LANG = DEFAULT_LANG; function getStripePricingTableHtml(lang) { const pricingTableId = SITE_CONFIG.stripe.pricingTableIds[lang] || SITE_CONFIG.stripe.pricingTableIds.en; return ( `` ); } function normalizeLang(value) { if (!value) return null; const lang = String(value).toLowerCase().split(/[-_]/)[0]; return DICT[lang] ? lang : null; } function getStoredLang() { const cookieMatch = document.cookie.match( new RegExp(`(?:^|; )${LANG_STORAGE_KEY}=([^;]*)`), ); try { return ( normalizeLang(localStorage.getItem(LANG_STORAGE_KEY)) || normalizeLang(cookieMatch && decodeURIComponent(cookieMatch[1])) ); } catch (e) { return normalizeLang(cookieMatch && decodeURIComponent(cookieMatch[1])); } } function getUrlLang() { return normalizeLang(new URLSearchParams(location.search).get("lang")); } function getBrowserLang() { const browserLangs = navigator.languages && navigator.languages.length ? navigator.languages : [navigator.language]; for (const lang of browserLangs) { const normalized = normalizeLang(lang); if (normalized) return normalized; } return null; } function getInitialLang() { return getUrlLang() || getStoredLang() || getBrowserLang() || DEFAULT_LANG; } function getRootPrefix() { if (document.body && document.body.dataset.rootPrefix) { return document.body.dataset.rootPrefix; } const pathParts = location.pathname.split("/").filter(Boolean); const parentDir = pathParts.length > 1 ? pathParts[pathParts.length - 2] : ""; return SUPPORTED_LANGS.includes(parentDir) ? "../" : "./"; } function withLangParam(href, lang) { const [pathWithSearch, hash = ""] = href.split("#"); const [path, search = ""] = pathWithSearch.split("?"); const params = new URLSearchParams(search); params.set("lang", lang); return `${path}?${params.toString()}${hash ? `#${hash}` : ""}`; } function getLocalizedPageUrl(lang) { const pathParts = location.pathname.split("/").filter(Boolean); const parentDir = pathParts.length > 1 ? pathParts[pathParts.length - 2] : ""; const fileName = pathParts[pathParts.length - 1] || ""; if (!SUPPORTED_LANGS.includes(parentDir)) return null; if (fileName !== "cgv.html" && fileName !== "legals.html") return null; return withLangParam(`../${lang}/${fileName}`, lang); } function updateInternalLinks(lang) { const rootPrefix = getRootPrefix(); const homeHref = withLangParam(rootPrefix, lang); const offerHref = withLangParam(`${rootPrefix}offre.html`, lang); const legalPrefix = `${rootPrefix}${lang}/`; const anchors = { nav_offer: `${homeHref}#offre`, nav_acad: `${homeHref}#academies`, nav_how: `${homeHref}#how`, nav_faq: `${homeHref}#faq`, legal1: withLangParam(`${legalPrefix}cgv.html`, lang), legal2: withLangParam(`${legalPrefix}legals.html`, lang), }; Object.entries(anchors).forEach(([key, href]) => { document .querySelectorAll(`a[data-i18n="${key}"]`) .forEach((link) => link.setAttribute("href", href)); }); document.querySelectorAll("a.brand, a.foot-logo-wrap").forEach((link) => { link.setAttribute("href", homeHref); }); document.querySelectorAll("a[href]").forEach((link) => { const href = link.getAttribute("href"); if (!href || href.startsWith("http") || href.startsWith("mailto:")) return; if (href.includes("offre.html")) { link.setAttribute("href", offerHref); } else if (href === "#offre" || href === "./#offre") { link.setAttribute("href", `${homeHref}#offre`); } else if (href === "#academies" || href === "./#academies") { link.setAttribute("href", `${homeHref}#academies`); } }); } function updateGoogleTagManagerNoScript() { const gtmId = SITE_CONFIG.googleTagManagerId; if (!gtmId) return; document .querySelectorAll('iframe[src*="googletagmanager.com/ns.html"]') .forEach((iframe) => { iframe.setAttribute( "src", `https://www.googletagmanager.com/ns.html?id=${gtmId}`, ); }); } function loadGoogleTagManagerScript() { const gtmId = SITE_CONFIG.googleTagManagerId; if (!gtmId) return; if ( document.querySelector( `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`, ) ) { return; } (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); const f = d.getElementsByTagName(s)[0]; const j = d.createElement(s); const dl = l !== "dataLayer" ? `&l=${l}` : ""; j.async = true; j.src = `https://www.googletagmanager.com/gtm.js?id=${i}${dl}`; f.parentNode.insertBefore(j, f); })(window, document, "script", "dataLayer", gtmId); } function updateConfiguredSiteUrl() { const siteUrl = SITE_CONFIG.siteUrl; const siteUrlAliases = SITE_CONFIG.siteUrlAliases || []; if (!siteUrl) return; document.querySelectorAll("[href], [src]").forEach((el) => { ["href", "src"].forEach((attr) => { const value = el.getAttribute(attr); const alias = siteUrlAliases.find( (item) => value && value.includes(item), ); if (alias) { el.setAttribute(attr, value.replaceAll(alias, siteUrl)); } }); }); if (!document.createTreeWalker || !document.body) return; const walker = document.createTreeWalker(document.body, 4); let node = walker.nextNode(); while (node) { const alias = siteUrlAliases.find((item) => node.nodeValue.includes(item), ); if (alias) { node.nodeValue = node.nodeValue.replaceAll(alias, siteUrl); } node = walker.nextNode(); } } function updateSocialLinks() { const socialLinks = SITE_CONFIG.socialLinks || {}; const selectors = { instagram: 'a[aria-label="Instagram"]', facebook: 'a[aria-label="Facebook"]:not([data-disabled="true"])', tiktok: 'a[aria-label="TikTok"]:not([data-disabled="true"])', linkedin: 'a[aria-label="LinkedIn"]:not([data-disabled="true"])', }; Object.entries(selectors).forEach(([key, selector]) => { const href = socialLinks[key]; if (!href) return; document .querySelectorAll(selector) .forEach((link) => { link.setAttribute("href", href); link.setAttribute("target", "_blank"); link.setAttribute("rel", "noopener noreferrer"); }); }); } function getFooterPath() { return `${getRootPrefix()}footer.html`; } async function loadFooter() { const placeholders = document.querySelectorAll('[data-include="footer"]'); if (!placeholders.length || !window.fetch) return; try { const response = await fetch(getFooterPath()); if (!response.ok) return; const rootPrefix = getRootPrefix(); const footerHtml = (await response.text()).replaceAll( 'src="assets/', `src="${rootPrefix}assets/`, ); placeholders.forEach((placeholder) => { placeholder.innerHTML = footerHtml; }); setLang(LANG); updateConfiguredSiteUrl(); updateSocialLinks(); } catch (e) {} } function setLang(l, options = {}) { const normalizedLang = normalizeLang(l); if (!normalizedLang) return; LANG = normalizedLang; if (options.persist) { try { localStorage.setItem(LANG_STORAGE_KEY, normalizedLang); } catch (e) {} document.cookie = `${LANG_STORAGE_KEY}=${encodeURIComponent( normalizedLang, )};path=/;max-age=31536000;SameSite=Lax`; } const localizedPageUrl = options.navigate ? getLocalizedPageUrl(normalizedLang) : null; if (localizedPageUrl && localizedPageUrl !== location.pathname + location.search) { location.href = localizedPageUrl; return; } document.documentElement.lang = normalizedLang; document.querySelectorAll("[data-i18n]").forEach((el) => { const v = DICT[normalizedLang][el.getAttribute("data-i18n")]; if (v != null) el.textContent = v; }); document.querySelectorAll("[data-i18n-html]").forEach((el) => { const key = el.getAttribute("data-i18n-html"); const v = key === "pricing" ? getStripePricingTableHtml(normalizedLang) : DICT[normalizedLang][key]; if (v != null) el.innerHTML = v; }); document.querySelectorAll("[data-i18n-content]").forEach((el) => { const v = DICT[normalizedLang][el.getAttribute("data-i18n-content")]; if (v != null) el.setAttribute("content", v); }); document.querySelectorAll("[data-i18n-ph]").forEach((el) => { const v = DICT[normalizedLang][el.getAttribute("data-i18n-ph")]; if (v != null) el.setAttribute("placeholder", v); }); document .querySelectorAll(".langsw button") .forEach((b) => { const isActive = b.dataset.lang === normalizedLang; b.classList.toggle("on", isActive); b.setAttribute("aria-pressed", String(isActive)); }); updateInternalLinks(normalizedLang); renderCountdown(); } document .querySelectorAll(".langsw button") .forEach((b) => b.addEventListener("click", () => setLang(b.dataset.lang, { persist: true, navigate: true }), ) ); loadGoogleTagManagerScript(); updateGoogleTagManagerNoScript(); updateConfiguredSiteUrl(); updateSocialLinks(); loadFooter(); /* ---------------- countdown ---------------- */ const TARGET = new Date("2026-07-19T23:59:59+02:00").getTime(); function renderCountdown() { const now = Date.now(); let diff = Math.max(0, TARGET - now); const d = Math.floor(diff / 86400000); diff -= d * 86400000; const h = Math.floor(diff / 3600000); diff -= h * 3600000; const m = Math.floor(diff / 60000); diff -= m * 60000; const s = Math.floor(diff / 1000); const p = (n) => String(n).padStart(2, "0"); const du = (DICT[LANG] && DICT[LANG].cd_d) || "d"; const str = `${d}${du} ${p(h)}h ${p(m)}m ${p(s)}s`; document.querySelectorAll(".js-cd").forEach((e) => (e.textContent = str)); } setLang(getInitialLang()); setInterval(renderCountdown, 1000); /* ---------------- header scroll ---------------- */ const hd = document.getElementById("hd"); if (hd) { addEventListener( "scroll", () => hd.classList.toggle("scrolled", scrollY > 10), { passive: true }, ); } /* ---------------- mobile menu ---------------- */ const burger = document.getElementById("burger"); if (burger && hd) { burger.addEventListener("click", () => { const open = hd.classList.toggle("menu-open"); burger.setAttribute("aria-expanded", open); }); } document.querySelectorAll("#navlinks a").forEach((a) => a.addEventListener("click", () => { if (hd) hd.classList.remove("menu-open"); if (burger) burger.setAttribute("aria-expanded", "false"); }), ); /* ---------------- faq accordion ---------------- */ document.querySelectorAll(".qa button").forEach((btn) => { btn.addEventListener("click", () => { const qa = btn.parentElement, ans = qa.querySelector(".ans"), open = qa.classList.toggle("open"); btn.setAttribute("aria-expanded", open); ans.hidden = !open; }); }); /* ---------------- reveal on scroll ---------------- */ const reduce = matchMedia("(prefers-reduced-motion:reduce)").matches; if (!reduce && "IntersectionObserver" in window) { const io = new IntersectionObserver( (es) => { es.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }); }, { threshold: 0.14 }, ); document.querySelectorAll(".rv").forEach((el) => io.observe(el)); } else { document.querySelectorAll(".rv").forEach((el) => el.classList.add("in")); }