Article SEO SEO Technique

SEO Local : Local Search Ads et Annonces de Recherche Locale – Concept complet

Introduction

Dans le paysage numérique actuel, l’optimisation pour les moteurs de recherche est devenue un levier incontournable pour toute entreprise souhaitant se démarquer dans un marché concurrentiel. Pour les commerces de proximité, les réseaux de points de vente, les franchises ou les entreprises qui ciblent une zone de chalandise précise, le SEO local occupe une place encore plus stratégique. Il permet non seulement d’améliorer la visibilité en ligne, mais surtout de générer des visites en magasin, des appels, des demandes d’itinéraires et des prises de rendez-vous.

On estime qu’environ 46 % des recherches effectuées sur Google ont une intention locale, et qu’une part significative de ces recherches aboutit à une visite physique ou à un achat dans les 24 heures. Le SEO local n’est donc pas qu’un canal de notoriété : c’est un véritable moteur de business pour les entreprises qui savent en tirer parti.

Cet article explore en profondeur le concept de SEO local, le rôle clé de Google Business Profile (anciennement Google My Business) et le fonctionnement des Local Search Ads, c’est-à-dire les annonces de recherche locale diffusées via Google Ads, notamment dans Google Search et Google Maps. Vous y trouverez des bonnes pratiques concrètes, des exemples d’optimisation et une FAQ détaillée pour structurer une stratégie complète de visibilité locale.

Concepts clés du SEO local

Qu’est-ce que le SEO local ?

Le SEO local est une stratégie de marketing numérique qui vise à améliorer la visibilité d’une entreprise dans les résultats de recherche géolocalisés. Contrairement au SEO dit « généraliste » qui cible une audience potentiellement mondiale ou nationale, le SEO local concentre ses efforts sur les internautes situés dans une zone géographique précise (ville, agglomération, département, quartier, secteur de livraison, etc.).

Quand un utilisateur tape une requête du type « coiffeur près de moi », « avocat Paris 8 », « pizzeria Lyon ouverte maintenant » ou qu’il effectue une recherche directement dans Google Maps, l’algorithme active des signaux locaux pour proposer des résultats proches de sa position : c’est ce que l’on appelle la recherche locale.

Les principaux objectifs du SEO local sont :

  • Apparaître dans le Local Pack (les 3 résultats locaux avec carte dans Google) et dans Google Maps.
  • Renforcer la visibilité organique du site sur des requêtes locales (ex. « dentiste Marseille », « boulangerie bio Bordeaux »).
  • Générer des actions locales mesurables : appels, demandes d’itinéraires, prises de rendez-vous, formulaires, commandes, visites en point de vente.

Les facteurs clés qui influencent le SEO local

Un bon référencement local repose sur une combinaison de signaux techniques, sémantiques et de réputation. Parmi les éléments les plus importants, on retrouve :

  • NAP (Name, Address, Phone) : la cohérence et l’exactitude du Nom de l’entreprise, de l’Adresse et du Numéro de téléphone sur l’ensemble du web (site, Google Business Profile, annuaires, réseaux sociaux, etc.). Des informations contradictoires peuvent nuire à la confiance des utilisateurs et à vos classements.
  • Google Business Profile : la création et l’optimisation de la fiche d’établissement officielle de Google, qui alimente les résultats de recherche locale, Google Maps et le Local Pack.
  • Les avis clients : le volume, la note moyenne et la fraîcheur des avis publiés par les internautes, ainsi que la façon dont l’entreprise y répond. Les avis influencent à la fois les classements locaux et le taux de clic.
  • Le contenu optimisé pour des mots-clés locaux : pages de services par ville, contenus de blog géolocalisés, FAQ locales, landing pages par zone desservie, etc.
  • Les signaux on-page : balises </code>, méta-descriptions, balises <code><h1></code>, maillage interne, données structurées locales (schema.org LocalBusiness, Organization, etc.).</li> <li><strong>Les citations locales</strong> : la présence dans des annuaires locaux et thématiques (Pages Jaunes, annuaires professionnels, sites de réservation, plateformes de prise de rendez-vous, chambres de commerce, etc.).</li> <li><strong>La proximité géographique</strong> : la distance entre l’établissement et la position de l’internaute, un critère fondamental pour apparaître dans le Local Pack et dans Maps.</li> <li><strong>La pertinence et la notoriété</strong> : correspondance entre la requête de l’utilisateur et votre activité, et popularité globale de votre marque (liens entrants, citations, mentions de marque, trafic direct).</li> </ul> <h3 id="annonces-de-recherche-locale-local-search-ads">Annonces de Recherche Locale (Local Search Ads)</h3> <p>Les <strong>annonces de recherche locale</strong>, appelées également <strong>Local Search Ads</strong>, sont une forme de publicité payante mise en place via <strong>Google Ads</strong> pour apparaître de manière prioritaire dans les résultats de recherche liés à une intention locale. Elles peuvent s’afficher :</p> <ul> <li>en haut des résultats de recherche Google, au-dessus des résultats organiques et du Local Pack ;</li> <li>dans <strong>Google Maps</strong> (application mobile et version web), sous forme de fiches sponsorisées ;</li> <li>directement dans l’interface Maps, avec un repère « sponsorisé » ou un bandeau mis en avant.</li> </ul> <p>Ces annonces s’appuient sur la combinaison de trois éléments :</p> <ul> <li>un <strong>compte Google Ads</strong> correctement structuré (campagnes locales, extensions de lieu, ciblage géographique, mots-clés locaux) ;</li> <li>un <strong>profil Google Business Profile vérifié et lié</strong> au compte Google Ads, afin de diffuser les informations de l’établissement (adresse, téléphone, itinéraires, horaires, photos) ;</li> <li>des <strong>signaux de localisation</strong> (position de l’utilisateur, historique de recherches, langue, appareil, etc.).</li> </ul> <p>Les avantages des Local Search Ads incluent :</p> <ul> <li>une <strong>visibilité premium</strong> en haut des résultats locaux, y compris devant des concurrents mieux positionnés en SEO pur ;</li> <li>la possibilité de <strong>cibler précisément une audience locale</strong> (rayon autour du magasin, code postal, ville, agglomération, zones d’enchères spécifiques) ;</li> <li>une <strong>orientation directe vers l’action</strong> : clic pour appeler, clic pour demander un itinéraire, clic vers le site ou vers une page de prise de rendez-vous ;</li> <li>un <strong>ROI potentiellement élevé</strong> lorsque la campagne est correctement paramétrée (mots-clés qualifiés, bonnes extensions, enchères ajustées, zone de chalandise bien définie).</li> </ul> <p>Les Local Search Ads ne remplacent pas le SEO local organique. Elles viennent le compléter pour capter des internautes prêts à agir immédiatement, en particulier sur mobile où une grande partie des recherches locales débouche sur une visite ou un contact dans la journée.</p> <h3 id="role-de-google-business-profile-dans-le-seo-local">Rôle de Google Business Profile dans le SEO local</h3> <p><strong>Google Business Profile</strong> (anciennement Google My Business) est la pierre angulaire de toute stratégie de référencement local. Cette fiche d’établissement gratuite permet à Google d’afficher vos informations dans :</p> <ul> <li>le panneau de connaissance (Knowledge Panel) sur la droite des résultats de recherche sur ordinateur ;</li> <li>le <strong>Local Pack</strong> (carte + 3 résultats locaux) ;</li> <li>Google Maps, sur ordinateur et sur mobile.</li> </ul> <p>Un profil bien optimisé augmente significativement les chances d’apparaître sur les requêtes locales pertinentes et d’être choisi par les internautes. Il influe également sur les performances des campagnes Local Search Ads, puisque ces dernières s’appuient sur les informations de votre fiche.</p> <p>Les éléments fondamentaux à renseigner et optimiser dans votre Google Business Profile sont :</p> <ul> <li>le <strong>nom de l’établissement</strong>, conforme à votre nom commercial réel ;</li> <li>l’<strong>adresse postale complète</strong> et correctement géolocalisée sur la carte ;</li> <li>le <strong>numéro de téléphone</strong> local, idéalement fixe, et identique à celui présent sur votre site ;</li> <li>la <strong>catégorie principale</strong> et les catégories secondaires décrivant précisément votre activité ;</li> <li>les <strong>horaires d’ouverture</strong>, y compris les horaires exceptionnels (jours fériés, périodes spéciales) ;</li> <li>le <strong>lien vers votre site web</strong> ou une page de réservation ;</li> <li>des <strong>photos de qualité</strong> (façade, intérieur, produits, équipe, menus, réalisations, etc.) ;</li> <li>la gestion des <strong>avis clients</strong> : incitation à laisser des avis, réponses systématiques, traitement rapide des avis négatifs ;</li> <li>l’utilisation de <strong>posts</strong> (actualités, offres, événements) pour dynamiser la fiche.</li> </ul> <p>Les entreprises disposant d’un profil Google Business complet, actif et régulièrement mis à jour constatent en général une hausse du nombre de vues, de clics vers le site, d’appels et de demandes d’itinéraires. Ce canal devient souvent l’une des principales sources de trafic qualifié pour les commerces de proximité.</p> <h2 id="bonnes-pratiques-pour-le-seo-local">Bonnes pratiques pour le SEO local</h2> <h3 id="optimiser-le-contenu-pour-les-mots-cles-locaux">Optimiser le contenu pour les mots-clés locaux</h3> <p>Une stratégie solide de SEO local commence par une <strong>recherche de mots-clés géolocalisés</strong>. L’objectif est d’identifier les requêtes réellement utilisées par vos prospects dans votre zone : type de service + ville, quartier, code postal, ou expressions comme « près de moi » et « à proximité ».</p> <p>Quelques exemples de mots-clés locaux :</p> <ul> <li>« restaurant français Paris 11 »</li> <li>« ostéopathe Lyon Part-Dieu »</li> <li>« plombier urgence Toulouse »</li> <li>« avocat droit du travail Lille près de moi »</li> <li>« garage auto Bordeaux ouvert maintenant »</li> </ul> <p>Pour tirer pleinement parti de ces expressions, il est recommandé de :</p> <ul> <li>créer des <strong>pages dédiées par ville ou par zone</strong> si vous servez plusieurs localisations ;</li> <li>intégrer le <strong>nom de la ville</strong> dans les balises <code><title></code>, les <code><h1></code> et les URL lorsque c’est pertinent ;</li> <li>rédiger des <strong>contenus utiles et spécifiques</strong> à la zone : exemples de clients locaux, cas pratiques, références, actualités du quartier ;</li> <li>ajouter des <strong>éléments de réassurance locale</strong> : témoignages de clients de la ville, logos de partenaires locaux, participation à des événements de la région.</li> </ul> <h3 id="ameliorer-la-structure-et-la-performance-du-site-web">Améliorer la structure et la performance du site web</h3> <p>La structure de votre site joue un rôle important dans votre capacité à bien vous positionner sur les requêtes locales. Un site clair, rapide et facile à parcourir est mieux compris par les moteurs de recherche et offre une meilleure expérience à l’utilisateur.</p> <p>Points d’attention pour le SEO local :</p> <ul> <li>mettre en place un <strong>menu clair</strong> qui donne rapidement accès aux pages locales (magasins, villes, zones desservies) ;</li> <li>organiser les <strong>pages de localisation</strong> dans une arborescence logique (ex. <code>/magasins/paris-15/</code>, <code>/agences/lyon/</code>) ;</li> <li>afficher vos <strong>coordonnées complètes</strong> (NAP) dans le pied de page et sur la page « Contact » ;</li> <li>intégrer une <strong>carte Google Maps</strong> sur la page de contact ou les pages locales pour renforcer les signaux de géolocalisation ;</li> <li>optimiser la <strong>vitesse de chargement</strong>, en particulier sur mobile, car une grande partie des recherches locales se fait sur smartphone ;</li> <li>assurer une <strong>expérience mobile irréprochable</strong> (design responsive, boutons d’appel cliquables, formulaires simplifiés).</li> </ul> <h3 id="creer-du-contenu-de-qualite-oriente-local">Créer du contenu de qualité orienté local</h3> <p>Au-delà des pages de services, un contenu éditorial régulier contribue à renforcer votre autorité locale. Il ne s’agit pas seulement de publier des articles génériques, mais de produire des contenus en lien avec votre territoire :</p> <ul> <li>présentations de <strong>cas clients locaux</strong> (ex. « Comment nous avons aidé une entreprise de transport à Lyon ») ;</li> <li>articles sur des <strong>événements de la région</strong> auxquels vous participez ou que vous sponsorisez ;</li> <li>guides pratiques locaux (ex. « Les meilleurs itinéraires vélo autour de Nantes » pour un magasin de cycles) ;</li> <li>contenus autour de <strong>questions fréquentes</strong> que se posent vos clients dans votre zone.</li> </ul> <p>Ce type de contenu enrichit votre présence sémantique sur des termes locaux et renforce la perception de proximité auprès de votre audience.</p> <h3 id="exploiter-google-business-profile-au-maximum">Exploiter Google Business Profile au maximum</h3> <p>Pour maximiser l’impact de votre profil Google Business Profile, il est conseillé d’adopter une gestion active et régulière, comparable à la gestion d’un mini-site dédié :</p> <ul> <li>mettre à jour fréquemment vos <strong>informations essentielles</strong> (horaires, photos, services disponibles, menu, prestations temporaires) ;</li> <li>publier régulièrement des <strong>posts</strong> pour communiquer sur vos promotions, événements, nouveautés ou actualités locales ;</li> <li>ajouter des <strong>photos professionnelles</strong> : plus une fiche est illustrée, plus elle inspire confiance et incite au clic ;</li> <li>répondre à tous les <strong>avis clients</strong>, qu’ils soient positifs ou négatifs, avec un ton professionnel et personnalisé ;</li> <li>utiliser les <strong>questions / réponses</strong> (Q&A) pour anticiper les interrogations fréquentes des internautes ;</li> <li>vérifier régulièrement les <strong>statistiques intégrées</strong> (vues, recherches, demandes d’itinéraires, appels) pour piloter vos actions.</li> </ul> <p>Un profil bien entretenu envoie un signal de sérieux aux utilisateurs et aux algorithmes de Google, ce qui se traduit souvent par une meilleure visibilité et plus d’interactions utiles (appels, clics, visites).</p> <h3 id="gerer-les-avis-clients-et-l-e-reputation-locale">Gérer les avis clients et l’e-réputation locale</h3> <p>Les avis en ligne sont un pilier de la décision pour les recherches locales. La quasi-totalité des internautes consultent des avis avant de choisir un commerce ou un prestataire à proximité, et une grande partie d’entre eux évite une entreprise lorsqu’ils trouvent des informations incorrectes ou des avis très négatifs non traités.</p> <p>Quelques bonnes pratiques :</p> <ul> <li><strong>solliciter des avis</strong> de manière régulière (après une prestation, par e-mail, SMS, QR code en magasin, etc.) ;</li> <li>faciliter l’accès à la page d’avis avec un <strong>lien direct</strong> vers votre profil Google Business ;</li> <li>répondre aux avis positifs en remerciant le client, ce qui encourage d’autres personnes à partager leur expérience ;</li> <li>traiter les <strong>avis négatifs</strong> avec tact : reconnaître la situation, proposer une solution ou un contact direct, montrer que vous prenez en compte les retours ;</li> <li>surveiller d’autres plateformes d’avis (Facebook, plateformes spécialisées de votre secteur) pour garder une vision globale de votre réputation en ligne.</li> </ul> <h3 id="exploiter-les-local-search-ads-en-complement-du-seo-local">Exploiter les Local Search Ads en complément du SEO local</h3> <p>Une fois vos fondamentaux de SEO local en place (site optimisé, profil Google Business complet, contenu local, avis clients), vous pouvez amplifier votre visibilité grâce aux <strong>Local Search Ads</strong>. Quelques conseils pour bien démarrer :</p> <ul> <li>lier votre <strong>compte Google Ads</strong> à votre profil Google Business Profile afin d’activer les <strong>extensions de lieu</strong> et la diffusion d’annonces locales dans Google Maps ;</li> <li>cibler un <strong>rayon de diffusion</strong> réaliste autour de votre établissement (par exemple 5 à 15 km selon la nature de votre activité et votre zone de chalandise) ;</li> <li>sélectionner des <strong>mots-clés à forte intention</strong> (ex. « achat », « réservation », « rendez-vous », « urgence », « près de moi ») plutôt que des requêtes trop larges ;</li> <li>utiliser des <strong>extensions d’appel</strong> et des <strong>extensions de lieu</strong> pour favoriser les contacts directs ;</li> <li>suivre précisément les <strong>conversions locales</strong> : appels, demandes d’itinéraires, formulaires, réservations, afin d’ajuster vos enchères et votre budget.</li> </ul> <p>Combinées à une fiche Google Business performante, les Local Search Ads permettent de capter des prospects très chauds, souvent prêts à se déplacer ou à commander dans un délai très court.</p> <h2 id="outils-et-ressources-pour-le-seo-local">Outils et ressources pour le SEO local</h2> <p>Plusieurs outils peuvent vous aider à analyser, suivre et optimiser votre visibilité locale :</p> <ul> <li><strong>Google Search Console</strong> : permet d’analyser la performance organique de votre site sur les requêtes locales, d’identifier les pages qui génèrent des impressions et des clics, et de détecter d’éventuels problèmes techniques (indexation, mobile, données structurées).</li> <li><strong>Google Analytics</strong> (ou une solution d’analytics équivalente) : sert à suivre le trafic, les conversions locales (appels, formulaires, clics sur le bouton d’itinéraire), les zones géographiques d’où proviennent vos visiteurs et le comportement des utilisateurs sur vos pages locales.</li> <li><strong>Outils spécialisés en SEO local</strong> comme BrightLocal : ils aident à suivre vos positions locales, gérer les avis, vérifier la cohérence de vos citations (NAP) et mesurer vos résultats par zone géographique.</li> <li><strong>Outils d’audit SEO</strong> (par exemple Screaming Frog, Sitebulb, ou équivalents) : utiles pour contrôler les balises, la structure du site, les liens internes et les erreurs techniques qui peuvent nuire à vos performances locales.</li> <li><strong>Plateformes de gestion de fiches multi-lieux</strong> : indispensables pour les enseignes et réseaux disposant de dizaines ou centaines de points de vente, afin de maintenir des informations uniformes à grande échelle et d’automatiser certaines mises à jour.</li> </ul> <h2 id="faq-questions-frequemment-posees-sur-le-seo-local">FAQ – Questions fréquemment posées sur le SEO local</h2> <ol> <li> <strong>Qu’est-ce que le SEO local ?</strong> <p>Le SEO local est l’ensemble des techniques qui visent à améliorer la visibilité d’une entreprise dans les résultats de recherche géolocalisés (Google Search et Google Maps). Il permet de se positionner sur des requêtes contenant un nom de ville, un quartier, un code postal ou une intention de proximité, afin d’attirer des internautes situés dans votre zone de chalandise et de les convertir en clients, visiteurs ou prospects.</p> </li> <li> <strong>Pourquoi le SEO local est-il important ?</strong> <p>Le SEO local est important car une part très significative des recherches effectuées sur Google a une intention locale. Une proportion importante des internautes qui effectuent une recherche locale visite ou contacte une entreprise dans les 24 heures, et une fraction non négligeable effectue un achat. Pour un commerce de proximité, un restaurant, un cabinet médical, un artisan, un showroom ou une agence, ignorer le référencement local revient à laisser le terrain à la concurrence.</p> </li> <li> <strong>Quelle est la différence entre SEO local et SEO « classique » ?</strong> <p>Le SEO « classique » vise à positionner un site sur des requêtes potentiellement nationales ou globales, souvent sans notion de localisation explicite. Le SEO local, lui, se concentre sur une zone géographique précise et intègre des signaux spécifiques : fiche Google Business Profile, avis clients locaux, cohérence du NAP, proximité de l’utilisateur, requêtes contenant un nom de ville ou une intention de proximité. Les pages et critères techniques restent importants, mais ils sont complétés par ces signaux locaux.</p> </li> <li> <strong>Qu’est-ce qu’une annonce de recherche locale (Local Search Ad) ?</strong> <p>Une annonce de recherche locale, ou Local Search Ad, est une publicité payante diffusée via Google Ads qui s’affiche lorsqu’un internaute effectue une recherche avec une intention locale. Elle peut apparaître dans les résultats de recherche Google comme une annonce sponsorisée liée à un lieu, mais aussi directement dans Google Maps sous forme de fiche sponsorisée. Ces annonces mettent en avant l’adresse, les horaires, le numéro de téléphone et les itinéraires vers l’établissement.</p> </li> <li> <strong>Est-ce que les Local Search Ads remplacent le SEO local ?</strong> <p>Non, les Local Search Ads ne remplacent pas le SEO local. Elles viennent le compléter. Le SEO local organique permet de construire une visibilité durable et de capter des clics sans paiement au clic, tandis que les Local Search Ads offrent une exposition supplémentaire en haut des résultats et permettent de cibler de manière très fine des internautes à forte intention. Une stratégie performante combine généralement les deux leviers.</p> </li> <li> <strong>Comment optimiser ma fiche Google Business Profile ?</strong> <p>Pour optimiser votre fiche Google Business Profile, commencez par vérifier et compléter toutes les informations de base (nom, adresse, téléphone, site, catégories, horaires). Ajoutez des photos de qualité, renseignez vos services ou produits, publiez régulièrement des posts, répondez aux avis et mettez à jour vos horaires exceptionnels. Assurez-vous que vos informations sont cohérentes avec celles de votre site et des autres annuaires en ligne.</p> </li> <li> <strong>Les avis Google influencent-ils vraiment mon classement local ?</strong> <p>Oui, les avis Google ont un double impact. D’une part, ils constituent un signal de confiance pour l’algorithme de Google, en particulier lorsque le volume d’avis, la note moyenne et la fraîcheur sont positifs. D’autre part, ils influencent fortement le comportement des utilisateurs : une fiche avec beaucoup d’avis récents et une bonne note attire davantage de clics, d’appels et de visites qu’une fiche peu évaluée ou très mal notée.</p> </li> <li> <strong>Comment mesurer l’efficacité de ma stratégie de SEO local ?</strong> <p>L’efficacité d’une stratégie de SEO local se mesure à travers plusieurs indicateurs : volumes d’impressions et de clics sur les requêtes locales dans Google Search Console, nombre de vues et d’actions (appels, demandes d’itinéraires, clics vers le site) sur votre fiche Google Business Profile, positions locales sur vos mots-clés stratégiques, trafic organique localisé dans Google Analytics, ainsi que les visites et ventes générées en point de vente.</p> </li> <li> <strong>Combien de temps faut-il pour obtenir des résultats en SEO local ?</strong> <p>Les premiers effets d’une optimisation locale (mise à jour de la fiche Google Business, corrections NAP, amélioration du site) peuvent se faire sentir en quelques semaines, notamment sur la visibilité de la fiche et le volume d’actions locales. Toutefois, la construction d’une position solide et durable sur des requêtes concurrentielles nécessite souvent plusieurs mois de travail régulier : publication de contenu, collecte d’avis, optimisation continue, suivi et ajustements.</p> </li> <li> <strong>Le SEO local est-il utile pour une entreprise qui ne reçoit pas de clients en point de vente ?</strong> <p>Oui, le SEO local est également pertinent pour les entreprises qui interviennent chez le client (artisans, services à domicile, consultants locaux, coachs, etc.) ou qui proposent des services en ligne mais ciblent une région spécifique. Dans ce cas, la stratégie se concentre sur la zone desservie, les requêtes géolocalisées pertinentes et la mise en avant de la proximité et de la connaissance du territoire plutôt que sur une adresse physique ouverte au public.</p> </li> </ol> </div> <!-- CTA Section --> <div class="mt-12 pt-8 border-t border-gray-200"> <div class="bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-center text-white"> <h3 class="text-2xl font-bold mb-4">Besoin d'aide avec votre SEO ?</h3> <p class="mb-6 text-purple-100">Notre équipe d'experts peut vous aider à optimiser votre site e-commerce</p> <div class="flex flex-col sm:flex-row gap-4 justify-center"> <a href="/seo-ecommerce" class="bg-white text-purple-600 px-8 py-3 rounded-lg font-semibold hover:bg-purple-50 transition inline-block"> Découvrir nos services SEO </a> <a href="/#contact" class="bg-purple-800 text-white px-8 py-3 rounded-lg font-semibold hover:bg-purple-900 transition inline-block"> Nous contacter </a> </div> </div> </div> </article> <!-- Section Commentaires --> <div class="mt-12 max-w-4xl mx-auto bg-white rounded-2xl shadow-xl p-8 md:p-12"> <h2 class="text-3xl font-bold text-gray-900 mb-6 flex items-center gap-3"> <i class="fas fa-comments text-purple-600"></i> Commentaires </h2> <!-- Liste des commentaires approuvés --> <div id="comments-list" class="mb-8 space-y-6"> <!-- Les commentaires approuvés seront chargés ici via JavaScript --> </div> <!-- Formulaire de commentaire --> <div class="border-t border-gray-200 pt-8"> <h3 class="text-xl font-semibold text-gray-900 mb-4">Laisser un commentaire</h3> <form id="comment-form" class="space-y-4"> <input type="hidden" id="article-slug" value="seo-local-local-search-ads-et-annonces-de-recherche-locale-concept-complet"> <div class="grid md:grid-cols-2 gap-4"> <div> <label for="comment-name" class="block text-sm font-medium text-gray-700 mb-2">Nom *</label> <input type="text" id="comment-name" name="name" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> <div> <label for="comment-email" class="block text-sm font-medium text-gray-700 mb-2">Email *</label> <input type="email" id="comment-email" name="email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> </div> <div> <label for="comment-message" class="block text-sm font-medium text-gray-700 mb-2">Message *</label> <textarea id="comment-message" name="message" rows="5" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"></textarea> </div> <div class="text-sm text-gray-600"> <i class="fas fa-info-circle text-purple-600"></i> Votre commentaire sera soumis à modération avant publication. </div> <button type="submit" class="bg-gradient-to-r from-purple-600 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-purple-700 hover:to-blue-700 transition inline-flex items-center gap-2"> <i class="fas fa-paper-plane"></i> Publier le commentaire </button> </form> <div id="comment-status" class="mt-4 hidden"></div> </div> </div> <!-- Navigation Articles --> <div class="mt-12 max-w-4xl mx-auto"> <a href="/blog" class="inline-flex items-center gap-2 text-purple-600 hover:text-purple-800 transition font-semibold"> <i class="fas fa-arrow-left"></i> Retour au blog </a> </div> </div> </section> <!-- Script pour les commentaires --> <script> const articleSlug = 'seo-local-local-search-ads-et-annonces-de-recherche-locale-concept-complet'; // Charger les commentaires approuvés async function loadComments() { try { const response = await fetch(`/gestion-commentaires/get-comments.php?slug=${articleSlug}`); const data = await response.json(); const commentsList = document.getElementById('comments-list'); if (data.success && data.comments.length > 0) { commentsList.innerHTML = data.comments.map(comment => ` <div class="border-l-4 border-purple-600 pl-4 py-2"> <div class="flex items-center gap-2 mb-2"> <strong class="text-gray-900">${comment.name}</strong> <span class="text-sm text-gray-500">•</span> <span class="text-sm text-gray-500">${new Date(comment.date).toLocaleDateString('fr-FR')}</span> </div> <p class="text-gray-700">${comment.message}</p> </div> `).join(''); } else { commentsList.innerHTML = '<p class="text-gray-500 italic">Aucun commentaire pour le moment. Soyez le premier à commenter !</p>'; } } catch (error) { console.error('Erreur lors du chargement des commentaires:', error); } } // Gérer la soumission du formulaire document.getElementById('comment-form').addEventListener('submit', async function(e) { e.preventDefault(); const formData = { slug: articleSlug, name: document.getElementById('comment-name').value, email: document.getElementById('comment-email').value, message: document.getElementById('comment-message').value }; const submitBtn = this.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Envoi en cours...'; try { const response = await fetch('/gestion-commentaires/submit-comment.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); const messageDiv = document.getElementById('comment-status'); if (result.success) { messageDiv.className = 'mt-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded-lg'; messageDiv.textContent = 'Merci ! Votre commentaire a été soumis et sera publié après modération.'; messageDiv.classList.remove('hidden'); this.reset(); } else { messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = result.message || 'Une erreur est survenue. Veuillez réessayer.'; messageDiv.classList.remove('hidden'); } } catch (error) { const messageDiv = document.getElementById('comment-status'); messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = 'Erreur de connexion. Veuillez réessayer plus tard.'; messageDiv.classList.remove('hidden'); } finally { submitBtn.disabled = false; submitBtn.innerHTML = originalText; } }); // Charger les commentaires au chargement de la page loadComments(); </script> <footer class="bg-gray-950 text-gray-400 py-12"> <div class="container mx-auto px-6"> <div class="flex flex-col md:flex-row justify-between items-center"> <div class="mb-6 md:mb-0 flex items-start gap-4"> <img src="/images/logo.png" alt="Logo VRAIVEX" class="h-32 w-32 md:h-40 md:w-40 object-contain"> <div> <a href="#" class="text-2xl font-bold block"> <span class="gradient-text">VRAIVEX</span> </a> <p class="mt-2 text-sm">Automatisation, IA et SEO au service de la performance e-commerce</p> </div> </div> <div class="flex flex-col items-center md:items-end"> <div class="grid grid-cols-2 md:flex md:flex-wrap gap-3 md:space-x-6 mb-4 text-center md:text-right"> <a href="/#about" class="hover:text-white transition text-sm">À propos</a> <a href="/#services" class="hover:text-white transition text-sm">Services</a> <a href="/#prestations" class="hover:text-white transition text-sm">Prestations</a> <a href="/#bestsellers" class="hover:text-white transition text-sm">Best Sellers</a> <a href="/#brands" class="hover:text-white transition text-sm">Nos marques</a> <a href="/creation-site-ecommerce" class="hover:text-white transition text-sm">Création Sites</a> <a href="/seo-ecommerce" class="hover:text-white transition text-sm">SEO E-commerce</a> <a href="/partenaires" class="hover:text-white transition text-sm">Partenaires</a> <a href="/#contact" class="hover:text-white transition text-sm">Contact</a> </div> <p class="text-sm text-center md:text-right">© 2025 VRAIVEX. Tous droits réservés.</p> </div> </div> </div> </footer> <!-- Back to Top Button --> <button id="backToTop" class="fixed bottom-8 right-8 bg-gradient-to-r from-purple-600 to-blue-600 text-white p-4 rounded-full shadow-lg hover:shadow-xl transform hover:scale-110 transition-all duration-300 z-50 hidden"> <i class="fas fa-arrow-up text-xl"></i> </button> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 100) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); document.querySelector(this.getAttribute('href')).scrollIntoView({ behavior: 'smooth' }); }); }); // Mobile menu toggle const mobileMenuButton = document.getElementById('mobileMenuButton'); const mobileMenu = document.getElementById('mobileMenu'); const menuIcon = document.getElementById('menuIcon'); if (mobileMenuButton && mobileMenu) { mobileMenuButton.addEventListener('click', function() { mobileMenu.classList.toggle('hidden'); // Toggle icon between bars and times if (mobileMenu.classList.contains('hidden')) { menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); } else { menuIcon.classList.remove('fa-bars'); menuIcon.classList.add('fa-times'); } }); // Close menu when clicking on a link const mobileLinks = mobileMenu.querySelectorAll('a'); mobileLinks.forEach(link => { link.addEventListener('click', function() { mobileMenu.classList.add('hidden'); menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); }); }); } // Brands Carousel const brandsCarousel = document.getElementById('brandsCarousel'); const brandsContainer = document.getElementById('brandsContainer'); const brandsPrevBtn = document.getElementById('brandsPrevBtn'); const brandsNextBtn = document.getElementById('brandsNextBtn'); const brandsPrevBtnMobile = document.getElementById('brandsPrevBtnMobile'); const brandsNextBtnMobile = document.getElementById('brandsNextBtnMobile'); if (brandsContainer && brandsCarousel) { let currentIndex = 0; const cards = brandsContainer.querySelectorAll('.brand-card'); const cardsPerView = { mobile: 1, tablet: 2, desktop: 3, large: 4 }; function getCardsPerView() { const width = window.innerWidth; if (width >= 1280) return cardsPerView.large; if (width >= 1024) return cardsPerView.desktop; if (width >= 768) return cardsPerView.tablet; return cardsPerView.mobile; } function updateCarousel() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const cardWidth = containerWidth / cardsPerViewCount; const maxIndex = Math.max(0, cards.length - cardsPerViewCount); currentIndex = Math.min(currentIndex, maxIndex); brandsContainer.style.transform = `translateX(-${currentIndex * cardWidth}px)`; // Update button states const isAtStart = currentIndex === 0; const isAtEnd = currentIndex >= maxIndex; if (brandsPrevBtn) { brandsPrevBtn.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtn.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtn) { brandsNextBtn.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtn.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } if (brandsPrevBtnMobile) { brandsPrevBtnMobile.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtnMobile.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtnMobile) { brandsNextBtnMobile.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtnMobile.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } } function nextSlide() { const cardsPerViewCount = getCardsPerView(); const maxIndex = Math.max(0, cards.length - cardsPerViewCount); if (currentIndex < maxIndex) { currentIndex++; updateCarousel(); } } function prevSlide() { if (currentIndex > 0) { currentIndex--; updateCarousel(); } } // Event listeners if (brandsNextBtn) brandsNextBtn.addEventListener('click', nextSlide); if (brandsPrevBtn) brandsPrevBtn.addEventListener('click', prevSlide); if (brandsNextBtnMobile) brandsNextBtnMobile.addEventListener('click', nextSlide); if (brandsPrevBtnMobile) brandsPrevBtnMobile.addEventListener('click', prevSlide); // Set responsive width for cards function setCardWidths() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const gap = 24; // 24px gap const cardWidth = (containerWidth - (gap * (cardsPerViewCount - 1))) / cardsPerViewCount; cards.forEach(card => { card.style.width = `${cardWidth}px`; card.style.flexShrink = '0'; }); } // Initialize setCardWidths(); updateCarousel(); // Update on resize let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(function() { setCardWidths(); currentIndex = 0; updateCarousel(); }, 250); }); } </script> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 50) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Scroll animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, observerOptions); // Observe all fade-in-up elements document.querySelectorAll('.fade-in-up').forEach(el => { observer.observe(el); }); // Smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Counter animation for stats function animateCounter(element, target, duration = 2000) { let start = 0; const increment = target / (duration / 16); const timer = setInterval(() => { start += increment; if (start >= target) { element.textContent = target + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); clearInterval(timer); } else { element.textContent = Math.floor(start) + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); } }, 16); } // Observe stats section const statsObserver = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.classList.contains('animated')) { entry.target.classList.add('animated'); const statsCards = entry.target.querySelectorAll('.stats-card'); statsCards.forEach((card, index) => { setTimeout(() => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; setTimeout(() => { card.style.transition = 'all 0.6s ease'; card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 100); }, index * 100); }); } }); }, { threshold: 0.3 }); const statsSection = document.querySelector('section.bg-gradient-to-r'); if (statsSection) { statsObserver.observe(statsSection); } </script> <script> // Gestion du formulaire de contact const contactForm = document.getElementById('contact-form'); const formMessage = document.getElementById('form-message'); const submitBtn = document.getElementById('submit-btn'); if (contactForm) { contactForm.addEventListener('submit', async function(e) { e.preventDefault(); // Désactiver le bouton pendant l'envoi submitBtn.disabled = true; submitBtn.textContent = 'Envoi en cours...'; // Récupérer les données du formulaire const formData = new FormData(contactForm); try { // Vérifier que les données sont bien dans le FormData const formDataObj = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message') }; console.log('Données du formulaire:', formDataObj); // Vérifier que tous les champs sont remplis if (!formDataObj.name || !formDataObj.email || !formDataObj.subject || !formDataObj.message) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = 'Veuillez remplir tous les champs du formulaire.'; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; } // Essayer d'abord avec JSON (plus fiable) // Si ça ne fonctionne pas, on essaiera avec FormData let response; try { // Méthode 1 : Envoyer en JSON (plus fiable selon les forums) response = await fetch('gestion-formulaire-contact/send-email-json.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formDataObj) }); } catch (jsonError) { console.warn('Erreur avec JSON, essai avec FormData:', jsonError); // Méthode 2 : Fallback avec FormData response = await fetch('send-email.php', { method: 'POST', body: formData }); } // Lire le texte de la réponse d'abord pour déboguer const responseText = await response.text(); console.log('Réponse serveur:', responseText.substring(0, 500)); // Vérifier si la réponse est OK if (!response.ok) { // Essayer de parser le JSON d'erreur try { const errorResult = JSON.parse(responseText); // Afficher le message d'erreur du serveur directement à l'utilisateur formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = errorResult.message || `Erreur ${response.status}: ${response.statusText}`; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; // Sortir de la fonction pour ne pas continuer } catch (e) { throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}. Réponse: ${responseText.substring(0, 200)}`); } } // Vérifier si PHP n'est pas exécuté (le serveur renvoie le code PHP brut) if (responseText.trim().startsWith('<?php') || responseText.includes('<?php')) { console.error('ERREUR: PHP n\'est pas exécuté par le serveur. Le code PHP est renvoyé brut.'); console.error('Le serveur web n\'est pas configuré pour exécuter PHP.'); // Utiliser la solution de secours : sauvegarder dans localStorage const messageData = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message'), timestamp: new Date().toISOString() }; // Sauvegarder dans localStorage comme solution de secours const savedMessages = JSON.parse(localStorage.getItem('vraivex_messages') || '[]'); savedMessages.push(messageData); localStorage.setItem('vraivex_messages', JSON.stringify(savedMessages)); // Afficher un message spécial formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-yellow-600 text-white'; formMessage.innerHTML = '⚠️ PHP n\'est pas configuré sur le serveur. Votre message a été sauvegardé localement. <br>Veuillez nous contacter directement à <strong>contact@vraivex.fr</strong> ou consulter les messages sauvegardés dans la console du navigateur.'; // Afficher les messages sauvegardés dans la console console.log('Messages sauvegardés localement:', savedMessages); console.log('Pour consulter les messages, tapez dans la console: JSON.parse(localStorage.getItem("vraivex_messages"))'); return; // Sortir de la fonction } // Essayer de parser le JSON let result; try { result = JSON.parse(responseText); } catch (parseError) { console.error('Erreur de parsing JSON:', parseError); console.error('Réponse reçue:', responseText.substring(0, 500)); throw new Error('Le serveur a renvoyé une réponse invalide. Vérifiez la console pour plus de détails.'); } // Afficher le message de résultat formMessage.classList.remove('hidden'); if (result.success) { formMessage.className = 'mb-6 p-4 rounded-lg bg-green-600 text-white'; formMessage.textContent = 'Message envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.'; contactForm.reset(); } else { formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; let errorMsg = result.message || 'Une erreur est survenue. Veuillez réessayer.'; // Afficher le message de debug en développement (à retirer en production) if (result.debug) { console.error('Erreur détaillée:', result.debug); } formMessage.textContent = errorMsg; } } catch (error) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; // Message d'erreur plus détaillé pour le débogage let errorMsg = 'Une erreur est survenue lors de la communication avec le serveur. '; errorMsg += 'Veuillez réessayer plus tard ou nous contacter directement à contact@vraivex.fr'; // En mode développement, afficher plus de détails if (error.message) { console.error('Erreur détaillée:', error); console.error('Message:', error.message); console.error('Stack:', error.stack); } formMessage.textContent = errorMsg; } finally { // Réactiver le bouton submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; // Masquer le message après 5 secondes setTimeout(() => { formMessage.classList.add('hidden'); }, 5000); } }); } // Back to Top Button functionality const backToTopButton = document.getElementById('backToTop'); // Show/hide button based on scroll position window.addEventListener('scroll', () => { if (window.pageYOffset > 300) { backToTopButton.classList.remove('hidden'); } else { backToTopButton.classList.add('hidden'); } }); // Smooth scroll to top when clicked backToTopButton.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); </script> </body> </html>