Eztebe
14 min de lecture

Site multilingue : hreflang, URL traduites et basque, expliqués avec ce site en exemple

Hreflang, URL traduites, x-default, sélecteur de langue, basque : un site en plusieurs langues que Google comprend, avec un vrai site en exemple et un outil de vérification.

Écrit par

Un site multilingue qui se référence dans chaque langue tient en cinq règles : une URL par langue, traduite ; des balises hreflang qui forment un cercle complet ; aucune redirection selon la langue du navigateur ; un sélecteur de langue qui mène à la même page ; des textes relus par quelqu'un qui parle la langue.

Ce site est l'exemple : trois langues, et tout ce qui suit est tiré de son code. En bas de l'article, un outil vérifie vos propres balises.

À retenir

  • Google n'indexe pas des « traductions » mais des pages : chaque langue a son URL, son titre, sa description et son contenu, une seule langue par page.
  • Les balises hreflang forment un cercle : chaque version cite toutes les autres et elle-même, plus une version x-default. Un lien retour qui manque, et la paire est ignorée.
  • Pas de redirection automatique selon la langue du navigateur ou l'adresse IP : Googlebot explore depuis les États-Unis et ne verrait jamais vos pages françaises.
  • Le sélecteur de langue mène à la même page dans l'autre langue, jamais à l'accueil ; et un drapeau n'est pas une langue.
  • Pour le basque comme pour toute langue que vous ne parlez pas : traduire moins de pages, mais les faire relire. Une mauvaise version vaut moins que pas de version.

Un site multilingue n'est pas un site traduit

Pour Google, une langue est un ensemble de pages distinctes, chacune avec sa propre adresse, et non un bouton qui change les textes d'une même page. Un site en trois langues est donc trois sites qui se répondent, reliés par des balises que les moteurs comprennent. Tout le reste de cet article découle de cette idée.

Trois pratiques courantes ne produisent aucune page indexable : le petit widget « traduire cette page » qui remplace les textes par JavaScript, les traductions côte à côte dans une même colonne, et la traduction automatique collée en vrac sans relecture. Dans les trois cas, Google voit une page, dans une langue, parfois deux mélangées, et vos clients espagnols ne vous trouvent pas.

Un vrai site multilingue coûte plus que la traduction des textes, et c'est pour ça que je le facture à part, 20 à 25 % par langue. Ce supplément paie ce qui suit.

Où mettre la langue dans l'URL ?

Dans un répertoire, presque toujours : /fr/, /es/, /eu/ sur un seul domaine. C'est le plus simple à gérer, et toute l'autorité acquise (liens, ancienneté) profite à toutes les langues. Un domaine par pays se justifie pour des marques ou des offres réellement distinctes, pas pour un cabinet qui parle à ses clients des deux côtés de la Bidassoa.

Les quatre façons de placer la langue, et quand les choisir.

| Structure | Exemple | Quand | | --- | --- | --- | | Répertoire | eztebe.com/es/ | Le choix par défaut : un domaine, une autorité, une administration | | Domaine par pays | exemple.fr, exemple.es | Des offres, des équipes ou des marques différentes par pays | | Sous-domaine | es.exemple.com | Rarement utile aujourd'hui ; hérite moins bien de l'autorité du domaine principal | | Paramètre | exemple.com/?lang=es | À éviter : Google le déconseille, et les URL sont illisibles |

Un mot sur le .eus. Ce domaine existe pour la communauté bascophone, et il a du sens pour un site écrit en basque. J'ai pris eztebe.com, pas eztebe.eus : ce site n'est pas en basque, et un .eus sans euskara serait une promesse non tenue.

Des URL traduites, pas seulement préfixées

Une URL traduite contient le mot que tapent les gens dans leur langue. Ma page « site vitrine » s'appelle /fr/services/site-vitrine, /es/servicios/web-corporativa et /en/services/business-website : trois noms différents, parce que le mot-clé n'est pas le même dans les trois marchés. Un simple préfixe (/es/services/site-vitrine) donnerait une page espagnole avec une URL française.

Techniquement, cela demande une table de correspondance entre le chemin interne et le chemin public de chaque langue. Sur ce site, elle tient dans un fichier :

// src/i18n/routing.ts (extrait)
export const routing = defineRouting({
  locales: ["fr", "es", "en"],
  defaultLocale: "fr",
  localePrefix: "always",
  localeDetection: false,
  pathnames: {
    "/services/[slug]": { fr: "/services/[slug]", es: "/servicios/[slug]", en: "/services/[slug]" },
    "/projets": { fr: "/projets", es: "/proyectos", en: "/projects" },
    "/a-propos": { fr: "/a-propos", es: "/sobre-mi", en: "/about" },
    "/contact": "/contact",
  },
});

Le [slug] lui-même est traduit : site-vitrine devient web-corporativa. Cette table sert ensuite trois fois, pour les liens internes, pour les balises hreflang et pour le sélecteur de langue. C'est ce qui garantit qu'ils racontent tous la même histoire.

Le cercle hreflang

La balise hreflang dit à Google : « cette page existe aussi en espagnol, à cette adresse ». Chaque version de la page porte la liste complète des versions, elle-même comprise, plus une version par défaut, x-default. Google n'accepte la relation que si elle est réciproque : si la page espagnole ne cite pas la page française, la paire est ignorée.

Trois façons de déclarer ces balises : dans le <head> de la page, dans un en-tête HTTP Link, ou dans le sitemap. Une seule suffit ; deux ne font pas de mal si elles disent exactement la même chose, mais c'est une maintenance en double. Ce site utilise le <head> et le sitemap, générés par le même code, donc impossibles à désaccorder.

Les codes suivent la norme ISO 639-1 pour la langue, avec une région ISO 3166-1 facultative : fr, es, eu, en-GB. Une région seule (ES) n'est pas valide, fr_FR avec un tiret bas non plus.

N'ajoutez la région que si le contenu diffère vraiment d'un pays à l'autre : fr-BE pour une page belge avec des prix et des mentions belges, sinon fr tout court, qui couvre tous les francophones.

Le cercle : ce que contient le <head> de chacune des trois versions

Version FR

eztebe.com/fr/services/site-vitrine

  1. hreflang="fr"/fr/services/site-vitrineelle-même
  2. hreflang="es"/es/servicios/web-corporativa
  3. hreflang="en"/en/services/business-website
  4. hreflang="x-default"/fr/services/site-vitrine

Version ES

eztebe.com/es/servicios/web-corporativa

  1. hreflang="fr"/fr/services/site-vitrine
  2. hreflang="es"/es/servicios/web-corporativaelle-même
  3. hreflang="en"/en/services/business-website
  4. hreflang="x-default"/fr/services/site-vitrine

Version EN

eztebe.com/en/services/business-website

  1. hreflang="fr"/fr/services/site-vitrine
  2. hreflang="es"/es/servicios/web-corporativa
  3. hreflang="en"/en/services/business-websiteelle-même
  4. hreflang="x-default"/fr/services/site-vitrine

Les trois listes sont identiques, à une ligne près : chaque page y figure, y compris elle-même, et x-default désigne la version française, celle qui est servie quand aucune langue ne correspond. Exemple réel, pris sur ce site.

Sur ce site, la liste est construite par une fonction unique, appelée par chaque page. Elle produit la canonique, les hreflang et le x-default, et laisse de côté les langues où la page n'existe pas :

// src/lib/seo.ts (extrait)
const languages: Record<string, string> = {};
for (const l of locales) {
  const h = hrefFor(l); // null si la page n'existe pas dans cette langue
  if (h) languages[l] = absoluteUrl(l, h);
}
languages["x-default"] = absoluteUrl(defaultLocale, hrefFor(defaultLocale)!);

return {
  alternates: { canonical: absoluteUrl(locale, hrefFor(locale)!), languages },
};

Ce qui sort dans le HTML de la page espagnole :

<link rel="canonical" href="https://www.eztebe.com/es/servicios/web-corporativa">
<link rel="alternate" hreflang="fr" href="https://www.eztebe.com/fr/services/site-vitrine">
<link rel="alternate" hreflang="es" href="https://www.eztebe.com/es/servicios/web-corporativa">
<link rel="alternate" hreflang="en" href="https://www.eztebe.com/en/services/business-website">
<link rel="alternate" hreflang="x-default" href="https://www.eztebe.com/fr/services/site-vitrine">

Deux détails font la différence. Les URL sont absolues, avec le www et sans slash final, exactement comme le site les sert : une URL qui redirige casse le cercle. Et la canonique de chaque version pointe vers elle-même, jamais vers la version française : une canonique qui désigne une autre langue dit à Google d'ignorer tout le reste.

Le sitemap répète la même liste pour chaque URL, avec la date de dernière modification réelle du contenu :

// src/app/sitemap.ts (extrait) : une entrée par langue, chacune avec toutes les alternatives
locales.map((l) => ({
  url: absoluteUrl(l, hrefFor(l)!),
  lastModified: dateDeMiseAJourDuContenu,
  alternates: { languages }, // fr, es, en, x-default
}));

Ne redirigez pas selon la langue du navigateur

Il est tentant d'envoyer un visiteur espagnol vers /es/ dès son arrivée. Google le déconseille, pour une raison simple : Googlebot explore depuis les États-Unis, en anglais. Avec une redirection automatique, il ne verrait jamais /fr/ ni /es/, et vos pages françaises n'existeraient pas pour lui. La même logique vaut pour une redirection selon l'adresse IP.

Ce site désactive donc la détection (localeDetection: false), et sa racine redirige vers /fr de façon permanente, en 308, parce que la langue par défaut est fixe. Chaque URL répond toujours la même chose, à tout le monde. Si vous tenez à guider le visiteur, un bandeau discret « esta página existe en castellano » fait le travail sans rien casser.

Le sélecteur de langue mène à la même page

Le sélecteur de langue est un lien vers la même page dans l'autre langue : la page « site vitrine » en espagnol, pas l'accueil espagnol. Un visiteur qui change de langue et retombe sur l'accueil repart. Chaque lien porte l'attribut hreflang et lang de sa langue cible, et son libellé est écrit dans cette langue : « Español », pas « Espagnol ».

Pas de drapeaux. Un drapeau désigne un pays, pas une langue : un lecteur bascophone vit en Espagne ou en France, un hispanophone peut être à Buenos Aires. Le mot suffit, et il se lit par tout le monde.

Un piège vécu sur ce site : au moment de la génération statique, le chemin interne (/services/web-corporativa) et le chemin public ne coïncidaient pas sur les pages espagnoles, et le HTML servi aux robots contenait des liens de sélecteur en 404 (/fr/services/web-corporativa). Rien de visible dans le navigateur, où tout se recalculait côté client.

C'est un crawl avec Screaming Frog qui l'a montré. Depuis, le sélecteur se fie à la table de correspondance des slugs, identique à la génération et dans le navigateur. Moralité : vérifiez le HTML brut, pas seulement l'écran.

Le basque, et toute langue que vous ne parlez pas

Au Pays basque, une version en euskara est une marque de respect, et souvent attendue par les institutions. Mais une version en basque bâclée fait plus de mal qu'une absence de version : elle dit au lecteur qu'on ne l'a pas pris au sérieux.

Je ne parle pas basque. Quand un client en a besoin, je fais traduire par un traducteur, et j'intègre son travail avec les mêmes balises que les autres langues.

Trois règles pour une langue qu'on ne maîtrise pas :

  1. Traduire moins, mais bien. L'accueil, les services, le contact, les mentions légales. Les articles de blog seulement quand ils ont un sens pour ce lectorat. Une page qui n'existe pas dans une langue n'apparaît pas dans son cercle hreflang, et ce n'est pas un problème.
  2. Une langue par page. Pas de paragraphe en castillan au milieu d'une page en euskara. Un terme isolé dans une autre langue se balise avec lang : <span lang="eu">amatxi</span>.
  3. Pas de traduction automatique brute en ligne. Google traite les traductions automatiques publiées en masse sans relecture comme du contenu de faible valeur. Si une version n'a pas été relue, mettez-la en noindex ou ne la publiez pas encore.

Le code du basque est eu, sans région : le basque est le même des deux côtés de la frontière. Si les mentions légales ou les prix diffèrent entre la version pour la France et celle pour l'Espagne, ce sont deux pages, eu-FR et eu-ES. Sinon, une seule.

Les métadonnées, langue par langue

Chaque version a son title, sa description et son image de partage, écrits à partir des mots-clés recherchés dans cette langue, pas traduits. « Site vitrine », « web corporativa » et « business website » ne sont pas des traductions l'un de l'autre : ce sont les mots que chaque marché tape dans Google.

L'attribut lang de la page suit la norme BCP 47 (fr-FR, es-ES, en-GB), et le code inLanguage des données structurées dit la même chose.

Les détails qui trahissent une traduction : les dates (« 30 octobre 2026 » contre « 30 de octubre de 2026 »), les nombres (3 000 € contre 3.000 €), la ponctuation (les espaces avant ? et : en français, les ¿ en espagnol), les guillemets. Le navigateur sait faire tout cela avec l'API Intl, à condition qu'on le lui demande.

Les erreurs que je vois le plus souvent

  1. Des hreflang sans lien retour. La page française cite l'espagnole, l'espagnole ne cite pas la française. La paire est ignorée. Remède : une seule fonction génère la liste pour toutes les versions.
  2. Une canonique vers la langue principale. Toutes les versions pointent vers /fr/. Google n'indexe que le français. Chaque version est canonique d'elle-même.
  3. Pas de x-default, ou un x-default qui redirige. Désignez une vraie page, qui répond en 200.
  4. Des codes inventés. ES (région seule), fr_FR (tiret bas), uk pour le Royaume-Uni (c'est en-GB ; uk, c'est l'ukrainien).
  5. Des URL relatives ou qui redirigent. Les hreflang doivent pointer vers l'URL finale, absolue, telle qu'elle est servie.
  6. Une redirection selon la langue ou l'IP. Voir plus haut : Googlebot ne verra qu'une langue.
  7. Deux langues sur une page. Menu en espagnol, texte en français, parce que la traduction est « en cours ». Pour Google, cette page n'est dans aucune langue.
  8. Un sélecteur vers l'accueil. Le visiteur perd sa page, et vous perdez le visiteur.
  9. Un sitemap sans alternatives. Il n'est pas faux, mais il laisse passer une occasion de confirmer le cercle.

Vérifiez votre site

La Search Console n'a plus de rapport dédié aux hreflang depuis 2022. Il reste les crawlers (Screaming Frog, Ahrefs) et ce petit outil, qui fait ce que fait Google : il lit une page, ouvre chacune de ses versions et vérifie que le cercle se referme.

Vérifiez les hreflang de votre site

Collez l'adresse d'une page : l'outil lit ses balises hreflang, ouvre chaque version et vérifie que le cercle est complet. Rien n'est enregistré.

Lisez d'abord les erreurs : lien retour manquant, canonique qui pointe ailleurs, code invalide, version qui redirige. Les avertissements (x-default absent, attribut lang manquant) ne bloquent pas l'indexation, mais ils se corrigent en cinq minutes. Testez une page de service plutôt que l'accueil : c'est là que les erreurs se cachent, parce que les slugs y sont traduits.

Questions fréquentes

Faut-il un nom de domaine par langue ?

Rarement. Des répertoires (/fr, /es, /eu) sur un seul domaine sont plus simples à gérer et concentrent l'autorité que le site accumule. Un domaine par pays se justifie quand les offres, les équipes ou les marques diffèrent vraiment d'un pays à l'autre.

hreflang fr ou fr-FR ?

fr, sauf si le contenu diffère selon le pays. fr couvre tous les francophones, de Bayonne à Bruxelles. fr-BE a du sens pour une page avec des prix, des mentions ou un vocabulaire belges, et seulement si une page fr ou fr-FR existe à côté.

Google pénalise-t-il la traduction automatique ?

Pas en soi. Ce que Google filtre, c'est le contenu publié en masse sans valeur ajoutée, et une traduction automatique non relue en fait partie. Traduisez avec les outils que vous voulez, faites relire par quelqu'un qui parle la langue, et n'indexez pas ce qui n'a pas été relu.

Combien de pages faut-il traduire ?

Celles qui amènent des clients : l'accueil, les services, le contact, les mentions légales. Les articles de blog, seulement quand ils ont un sens pour ce marché ; une page absente dans une langue sort simplement de son cercle hreflang. Mieux vaut cinq pages justes que quarante pages approximatives.

Combien coûte une langue supplémentaire ?

Sur un site que je construis, 20 à 25 % du prix du site par langue, traduction comprise quand je la fais moi-même (français, espagnol, anglais). Le détail est dans ma grille de prix.

Faut-il un .eus ?

Si le site est écrit en basque, ou si l'identité basque est centrale pour la marque, oui. Sinon, un .com ou un .es avec un répertoire /eu/ fait exactement le même travail pour Google, et sans promesse que le site ne tient pas.

Et maintenant

Si votre site existe déjà en plusieurs langues, passez-le dans l'outil ci-dessus : en une minute, vous saurez si le cercle est complet. S'il reste à construire, c'est le sujet de ma page site multilingue, et vous pouvez me décrire votre projet : nous en parlons une heure, et vous recevez un devis sous 48 heures.

Tous les articles

Un projet où ces questions se posent ?

Je peux vous aider à les trancher — et à construire la suite.