Un menu qui liste des langues : quoi de plus banal ? Quelques liens, peut-être un drapeau, et l’affaire semble réglée. Pourtant, dès qu’on le prend au sérieux, ce petit composant se révèle un carrefour où se croisent l’internationalisation, le référencement, l’accessibilité et l’architecture logicielle. Ce billet met en avant les questions les plus pointues qu’il pose — celles qu’on ne voit pas venir tant qu’on ne les a pas rencontrées. Il reste volontairement au niveau des principes : aucune de ces questions n’est propre à une technologie ou à un projet particulier.
Ce n’est pas un « sélecteur de langue »
Premier renversement : le composant ne choisit pas qu’une langue. Un site sérieux mêle trois natures d’entrées : des versions globales officielles, des déclinaisons régionales ou de réseau (distributeurs, filiales), et parfois des sites tiers vers lesquels on redirige. Trois natures, trois comportements : rester sur place, changer de déclinaison, ou quitter le domaine. Nommer le composant « sélecteur de langue » masque la moitié de sa complexité. « Sélecteur de navigation globale » est plus juste — et cadre d’emblée les décisions qui suivent.
Nommer les langues : natif ou traduit ?
Faut-il écrire « Deutsch » ou « Allemand » ? La question paraît cosmétique ; elle est structurante. Si l’on affiche le nom des langues traduit dans la langue de la page courante, on s’engage à maintenir une matrice N×N : chaque langue doit connaître le nom de toutes les autres. Quatre langues, seize libellés ; dix langues, cent. Et à chaque ajout, on ne remplit pas une case, on remplit une ligne et une colonne.
Le nom natif — chaque langue écrite dans sa propre langue — supprime la matrice : un seul libellé par langue, valable partout. C’est aussi la convention dominante (un visiteur cherche « Nederlands », pas « Néerlandais »), et elle règle le cas de l’utilisateur égaré sur une page dont il ne lit pas la langue.
DE, NL, FR) lève toute ambiguïté sans traduction, aligne proprement les libellés en colonne, et donne au menu une lisibilité « tableau » que le nom seul n’a pas.
Repérer l’entrée active sans mentir à personne
La langue courante doit être immédiatement identifiable. Le réflexe — la mettre en gras ou en couleur — a deux défauts. D’abord l’accessibilité : un repère qui ne repose que sur la couleur est invisible pour une partie des visiteurs. L’information doit être portée sémantiquement, par aria-current, pas seulement par un style.
Ensuite, une question d’architecture : où décide-t-on de l’entrée active ? Au moment de la génération des pages, ou à l’exécution dans le navigateur ? Marquer l’actif à l’exécution — en lisant la langue déclarée du document — a l’avantage d’être robuste : la même page, servie telle quelle, se marque correctement quelle que soit la manière dont elle a été produite. Le style, lui, n’est qu’un habillage de cette vérité sémantique.
Rester ou partir : la sémantique interne / externe
Changer de langue sur le même site et partir vers un autre domaine sont deux gestes différents ; le composant doit les distinguer visuellement. Un discret ↗ signale « vous quittez le site » ; il n’a rien à faire sur un simple changement de langue interne. Point clé : ce signe doit être piloté par la donnée (l’entrée est-elle externe ?), jamais par une condition en dur sur une URL.
target="_blank", avec rel="noopener noreferrer" pour la sécurité). Mais chaque clic crée un onglet de plus : un visiteur curieux se retrouve vite avec une dizaine d’onglets quasi identiques. Une fenêtre nommée partagée par tous les liens externes (au lieu de _blank) les fait se rouvrir dans le même onglet — un détail qui change l’expérience.
Le menu parle aussi aux moteurs : hreflang
Un sélecteur de langue n’est pas qu’une affaire d’humains. Chaque lien vers une autre version linguistique gagne un attribut hreflang, qui indique aux moteurs la relation entre les versions — et évite qu’ils traitent des traductions comme du contenu dupliqué. La difficulté est la cohérence : le libellé visible, le code affiché et la valeur hreflang doivent raconter la même histoire, y compris la déclinaison régionale (fr n’est pas fr-CA, en n’est pas en-US). Un menu généré à la main dérive vite ; un menu généré depuis une source unique reste aligné par construction.
Piloter par la donnée, pas par des listes
C’est la décision d’architecture la plus rentable. Tant que le contenu du menu est écrit en dur dans un gabarit — ou pire, dispersé entre plusieurs endroits — ajouter une langue devient une chasse au trésor : le libellé ici, le lien là, l’attribut ailleurs. En externalisant tout dans une table de configuration, on obtient une règle simple : ajouter une langue ou une destination, c’est ajouter une ligne. Aucune logique à toucher.
À titre d’illustration, une ligne suffit à décrire une entrée : son code, son bloc (globale, réseau, tiers), son nom natif, sa cible, son caractère interne ou externe, son hreflang :
code ; bloc ; nom_natif ; cible ; externe ; hreflang
FR ; RESEAU ; Français ; /fr/ ; non ; fr
NL ; RESEAU ; Nederlands ; /nl/ ; non ; nl
DE ; GLOBALE ; Deutsch ; https://exemple.tld/ ; oui ; de
US ; TIERS ; North America ; https://autre.tld/ ; oui ; en-US
Un même fichier nourrit alors le libellé, le lien, le tri, le signe ↗ et le hreflang. Il devient aussi le seul endroit à modifier pour activer une langue : une colonne « active » permet de préparer une traduction en coulisse et de la révéler d’un simple basculement, sans redéploiement de code.
Le clavier et le lecteur d’écran : le vrai test
C’est là que beaucoup de menus échouent aux audits. Trois exigences non négociables :
- Un nom accessible sur le bouton. Un déclencheur qui ne contient qu’une icône (un globe, par exemple) marquée décorative n’a aucun nom pour un lecteur d’écran : il s’annonce « bouton », sans dire à quoi il sert. C’est l’échec classique
button-name(WCAG 4.1.2) ; unaria-labellocalisé le corrige. - Une navigation clavier complète. Ouverture au clavier, déplacement par les flèches,
Échapqui ferme et rend le focus au bouton,aria-expandedqui reflète l’état. Un menu qui ne s’utilise qu’à la souris est à moitié cassé. - Un focus visible. Sans anneau de focus, la navigation clavier existe mais ne se voit pas — donc n’existe pas vraiment.
Aucune de ces exigences n’est coûteuse ; toutes sont faciles à oublier, parce qu’elles ne se voient pas à la souris.
Le problème vraiment dur : la montée en charge
Tout ce qui précède se règle une fois. La question qui reste ouverte est celle de l’échelle : un menu à deux langues et un menu à trente langues ne sont pas le même composant. Et il faut distinguer deux choses qu’on confond :
Il n’y a pas de réponse unique, mais une gradation de réponses, à choisir selon le nombre réel de langues visées :
- Défilement borné — une hauteur maximale et un défilement interne. Le garde-fou le moins cher ; suffit jusqu’à une quinzaine d’entrées.
- Multi-colonnes — répartir le bloc le plus fourni sur deux ou trois colonnes en grand écran, une seule sur mobile.
- Sous-groupement régional — introduire une notion de région (Europe, Asie, Amériques…) et des sous-titres. Reste piloté par la donnée : une colonne de plus dans la table, pas une logique de plus dans le code.
- Champ de recherche — un filtre en direct, pertinent au-delà d’une vingtaine de langues ; la logique est générique, jamais spécifique à une langue.
- Deux niveaux — un menu compact (langues majeures, ou langues pertinentes selon les préférences du navigateur) et un lien « toutes les langues » vers une page dédiée. La réponse la plus robuste à très grande échelle.
La bonne stratégie est incrémentale : rester plat tant qu’on tient dans une poignée d’entrées, prévoir le défilement borné dès le départ comme filet, puis basculer sur le groupement régional — car il prolonge le principe « une ligne = une entrée » sans jamais créer une seconde source de vérité.
Générer sans se contredire : l’idempotence
Dernier détail qui n’en est pas un. Si le menu est (re)construit par une étape automatisée, cette étape doit être idempotente : la rejouer deux fois doit produire exactement le même résultat, jamais des doublons ni des entrées orphelines. La méthode fiable consiste à reconstruire le bloc entièrement à partir de la source de configuration à chaque passage, plutôt qu’à modifier l’existant par petites touches. Un composant qui se régénère à l’identique est un composant qu’on ose relancer sans crainte.
Bilan
Le sélecteur de navigation est un cas d’école : un composant que tout le monde croit trivial, et qui concentre en réalité presque toutes les tensions du développement web sérieux — internationalisation, référencement, accessibilité, architecture pilotée par la donnée, et passage à l’échelle. Les questions pointues qu’il pose (nom natif contre matrice de traduction, état actif sémantique, distinction interne/externe, cohérence hreflang, nom accessible du déclencheur, présentation au-delà de dix langues) ne sont pas des raffinements : ce sont les décisions qui séparent un menu qui « marche » d’un composant qu’on peut faire grandir sans le réécrire.
La ligne directrice qui les résout presque toutes tient en une phrase : une seule source de vérité, et le menu comme sa sortie déterministe. Le reste n’est que de la mise en forme.