BLOG

Une question en tête ?

Nous répondons à toutes les questions concernant les exosquelettes. Contactez notre équipe de professionnels via le formulaire de contact ou sur l’adresse mail suivante: contact@exosquelette.fr

/** * i18n-core.js * * Lightweight internationalisation runtime for multi-site WordPress deployments. * * How it works: * 1. A per-site language file (e.g. lang-fr.js) sets window.I18N.strings * before this script runs, or this script sets the default empty object. * 2. T(key) — resolves a dot-notation key like "quiz.step1.question" * against I18N.strings and returns the matching string. * Falls back to the key itself if not found, and warns in the console. * 3. Tf(key, vars) — same as T() but performs {placeholder} substitution * using the vars object, e.g. Tf('quiz.result', { n: 3, total: 7 }). * 4. On DOMContentLoaded the runtime auto-translates the DOM: * data-i18n="some.key" → element.textContent (or innerHTML * when the string contains \n, which * is converted to
after escaping) * data-i18n-placeholder="some.key" → element.placeholder attribute * * Load order (in or before body close): * * */ (function (window, document) { 'use strict'; /* ── namespace ─────────────────────────────────────────────────────────── */ window.I18N = window.I18N || {}; window.I18N.strings = window.I18N.strings || {}; /* ── internal helpers ───────────────────────────────────────────────────── */ /** * Resolve a dot-notation key against an object. * Returns undefined when any segment is missing. */ function _resolve(obj, key) { var parts = key.split('.'); var cursor = obj; for (var i = 0; i < parts.length; i++) { if (cursor === null || cursor === undefined || typeof cursor !== 'object') { return undefined; } cursor = cursor[parts[i]]; } return cursor; } /* ── public API ─────────────────────────────────────────────────────────── */ /** * T(key) — translate a dot-notation key. * Returns the translated string, or the key itself when not found. */ window.T = function T(key) { var value = _resolve(window.I18N.strings, key); if (value === undefined || value === null) { console.warn('[i18n] Missing key:', key); return key; } return String(value); }; /** * Tf(key, vars) — translate then replace {placeholders}. * vars is a plain object whose keys match the placeholder names. * Example: Tf('quiz.result', { n: 3, total: 7 }) * with string "You got {n} out of {total}" → "You got 3 out of 7" */ window.Tf = function Tf(key, vars) { var str = window.T(key); if (!vars || typeof vars !== 'object') { return str; } return str.replace(/\{([^}]+)\}/g, function (match, placeholder) { return Object.prototype.hasOwnProperty.call(vars, placeholder) ? String(vars[placeholder]) : match; }); }; /* ── DOM auto-translation ────────────────────────────────────────────────── */ function _translateDOM() { var textNodes = document.querySelectorAll('[data-i18n]'); for (var i = 0; i < textNodes.length; i++) { var el = textNodes[i]; var str = window.T(el.getAttribute('data-i18n')); if (str.indexOf('\n') !== -1) { // Escape HTML special chars first, then convert \n →
. // & must be escaped before < and > to avoid double-escaping &. el.innerHTML = str .replace(/&/g, '&') .replace(//g, '>') .replace(/\n/g, '
'); } else { el.textContent = str; } } var placeholderNodes = document.querySelectorAll('[data-i18n-placeholder]'); for (var j = 0; j < placeholderNodes.length; j++) { var pel = placeholderNodes[j]; pel.setAttribute( 'placeholder', window.T(pel.getAttribute('data-i18n-placeholder')) ); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', _translateDOM); } else { _translateDOM(); } }(window, document)); /** * lang-fr.js — Master French string file * * This is the source-of-truth language file. All other lang-xx.js files * are translations of the keys defined here. * * Load this BEFORE i18n-core.js so the strings are available at runtime: * * * * Structure mirrors the features that use it — add namespaces as more * snippets are migrated to the i18n system. */ window.I18N = window.I18N || {}; window.I18N.strings = { quiz: { header: { titlePart: "Quel exosquelette vous convient ?", titlePro: "Trouvez votre solution pro" }, progress: { stepLabel: "Étape {n} sur {total}", coordonneesLabel: "Vos coordonnées", confirmLabel: "Confirmation" }, step1: { question: "Vous êtes…", profilPart: { label: "Un particulier", sub: "Usage personnel, sport, sénior, rééducation" }, profilPro: { label: "Un professionnel", sub: "Entreprise, industrie, établissement médical" } }, step2: { question: "Pour quel usage ?", hint: "Vous pouvez sélectionner plusieurs réponses", options: { industrie: { label: "Travail / Industrie", sub: "Port de charges, TMS" }, sport: { label: "Sport & Loisir", sub: "Ski, randonnée, trail" }, senior: { label: "Particulier / Sénior", sub: "Mobilité quotidienne" } } }, step3work: { question: "Précisez votre contexte", hint: "Vous pouvez sélectionner plusieurs réponses", options: { prevention: { label: "Prévenir les accidents du travail / TMS", sub: "Réduire les risques de troubles musculosquelettiques" }, handicap: { label: "Maintenir dans l'emploi une personne en situation de handicap", sub: "Compensation du handicap, maintien au poste" }, autre: { label: "Autre", sub: "Précisez si vous le souhaitez" } }, autrePlaceholder: "Précisez votre contexte (facultatif)" }, step3med: { question: "Précisez votre contexte", hint: "Vous pouvez sélectionner plusieurs réponses", options: { reeducation: { label: "Retrouver de la mobilité après une blessure ou un arrêt", sub: "Récupérer des capacités motrices après un accident ou une pathologie" }, mobilite: { label: "Mobilité quotidienne ou ponctuelle", sub: "Faciliter la marche et les déplacements" }, compensation: { label: "Compenser une faiblesse physique ou une perte d'autonomie", sub: "Compenser une faiblesse musculaire ou neurologique" }, autonomie: { label: "Maintenir son autonomie physique", sub: "Maintenir la masse musculaire et les capacités physiques" }, autre: { label: "Autre" } } }, step4: { question: "Quelle partie du corps ?", hint: "Vous pouvez sélectionner plusieurs réponses", options: { dos: { label: "Dos / Lombaires", sub: "Flexions, charges" }, jambes: { label: "Jambes / Genoux", sub: "Marche, position debout" }, bras: { label: "Bras / Épaules", sub: "Travail en hauteur" }, entier: { label: "Corps entier", sub: "Rééducation complète" }, autre: { label: "Autre", sub: "Précisez si vous le souhaitez" } }, autrePlaceholder: "Précisez la partie du corps (facultatif)" }, step5: { question: "Quel type d'exosquelette ?", hint: "Vous pouvez sélectionner plusieurs réponses", options: { passif: { label: "Passif", sub: "Assistance mécanique, sans moteur" }, actif: { label: "Actif", sub: "Motorisé, assistance électrique" }, indecis: { label: "Je ne sais pas encore", sub: "Conseillez-moi" } } }, step6: { question: "Quelle enveloppe budgétaire ?", hint: "Plusieurs réponses possibles si vous hésitez entre des gammes", options: { low: { label: "Moins de 2 000 €", sub: "Exosquelettes passifs, sport, sénior" }, mid: { label: "1 500 € – 10 000 €", sub: "Semi-actifs, industrie légère" }, high: { label: "10 000 € – 50 000 €", sub: "Actifs, industrie lourde" }, premium: { label: "+ de 50 000 €", sub: "Solutions avancées" } } }, step7: { previewPart: { title: "Votre sélection personnalisée est prête !", text: "Recevez vos 3 meilleures recommandations par email avec comparatif complet et avis experts." }, previewPro: { title: "Vos recommandations pro sont prêtes !", text: "En plus du comparatif, nous pouvons vous mettre en contact directement avec les marques sélectionnées pour un devis personnalisé." }, form: { namePrenom: { label: "Nom et Prénom", placeholder: "Votre nom et prénom" }, email: { label: "Email", placeholder: "votre@email.com" }, entreprise: { label: "Nom de l'entreprise", placeholder: "Votre entreprise" }, tel: { label: "Téléphone", placeholder: "+33 6 00 00 00 00", optional: "(optionnel)" }, codePostal: { label: "Code postal", placeholder: "75000" }, secteur: { label: "Secteur d'activité", placeholder: "Sélectionnez votre secteur", options: { industrie: "Industrie", btp: "BTP", logistique: "Logistique", agriculture: "Agriculture", sante: "Santé", collectivite:"Collectivité", autre: "Autre" } } }, submit: "Recevoir mes recommandations →", legal: "Pas de spam. Désinscription en 1 clic. Données protégées (RGPD)." }, errors: { requiredFields: "Veuillez remplir votre nom/prénom et email.", invalidEmail: "Veuillez entrer un email valide." }, confirm: { title: "Merci pour votre confiance !", message: "Vous recevrez un mail dès aujourd'hui avec vos recommandations personnalisées pour votre besoin.", ctaPrompt: "Vous souhaitez avoir plus d'informations ?\nÉcrivez-nous", browseBtn: "🔍 Parcourir le site", contactBtn: "✉️ Nous contacter" }, buttons: { next: "Suivant →", back: "← Retour" } } };