L'attribut HTML5 lang et la pseudo-class CSS3 :lang()
Savez-vous qu'en HTML5, l'attribut lang (équivalent xHTML de xml:lang) peut être placé dans n'importe quelles balises de la page. Il définit alors dans quelle langue doit être interprétée la portion de code dans cette balise et ses enfants. Cet attribut fonctionne de paire avec la pseudo-class :lang() que l'on retrouve en CSS3. Nous allons mettre en avant ces deux amis le temps d'un article et le temps de comprendre à quel point ils s'entendent bien.
L'attribut lang en HTML5
Abordons en premier lieu l'utilisation de l'attribut lang en HTML5 telle que décrite par le W3C. Vous le connaissez déjà si vous avez jeté un simple coup d'œil à la page minimale de tous documents HTML5. Voyons plutôt :
Exemple de sémantique d'une page HTML5
<!DOCTYPE html> <html lang="fr-fr"> <head> <meta charset="utf-8" /> <title>Document HTML5 de base</title> </head> <body> <blockquote lang="en" cite="http://www.ffdream.com/encyclopedie-mythe_ultimecia2.html"> <p>I'll be here...</p> <p>Why...?</p> <p>I'll be waiting...here...</p> <p>For what?</p> <p>I'll be waiting...for you...so...</p> <p>If you come here...</p> <p>You'll find me.</p> <p>I promise.</p> </blockquote> <p>Citation de l'introduction de Final Fantasy VIII</p> </body> </html>
Vous l'avez remarqué, il est là, à la ligne 2, <html lang="fr-fr">. Vous aurez probablement lu également qu'il remplace <meta http-equiv="content-language" content="fr-fr" /> en xHTML. C'est aussi juste que c'est incorrecte. En réalité, un http-equiv complète/corrige/remplace le travail d'une entête http. Ainsi quand une réponse du serveur web ne contient pas l'entête Content-Language: fr-fr, le navigateur sait quand même dans quelle langue interpréter les données (ici française). Si le document est en HTML5 (Le navigateur le sait grâce à <!DOCTYPE html>) il sait d'avance qu'il faut interpréter chaque nœud du document dans la langue indiquée par son plus proche parent ayant un attribut lang remplis.
Vous l'avez peut-être compris, en HTML5, on ne dit pas "mon document est écris en français" mais "cette partie de mon document est écrite en française, mais celle-ci est écrite en anglaise". C'est exactement le cas dans mon exemple précédent ; si vous regardez attentivement à la ligne 8, la balise <blockquote> contient un attribut lang "en" ! Par conséquent —car l'action de langue se propage sur tous les nœuds fils— toutes les balises <p> enfants sont également à interpréter en anglais.
Rien n’empêche tout de même de définir dans le header http renvoyé par le serveur Content-Language: pour définir la langue principale du document.
La pseudo-class :lang() en CSS3
Abordons à présent l'utilisation de la pseudo-class :lang() en CSS3 telle que décrite par le W3C. A l'instar de :nth-child(), elle prend un paramètre pour fonctionner. La langue d'interprétation de l'élément cible est le paramètre en question (par exemple :lang(fr-fr)). Et je vous laisse deviner quel mécanisme permet à la pseudo-class de définir la langue d'interprétation du nœud ? Bingo ! L'attribut lang HTML5 du/des nœud(s) ciblé(s) ou de son/leur plus proche parent contenant l'attribut lang.
Exemple de ciblage CSS
/* La seule phrase en rouge sera : */ /* "Citation de l'introduction de Final Fantasy VIII". */ /* Les autres phrases ne le seront pas car elles ne sont pas interprétées en français. */ p:lang(fr-fr) { color: #ff0000; }
Attention : <html lang="fr-fr"> peut être ciblée par :lang(fr) et :lang(fr-fr) mais <html lang="fr"> ne peut être ciblée que par :lang(fr) et pas par :lang(fr-fr).
/* Ceci fonctionne aussi pour fr-fr ! */ p:lang(fr) { color: #ff0000; }
A savoir : :lang() est fonctionnelle IE8+ (pour un support plus vaste vous pouvez plutôt faire appel aux classes CSS conditionnelles).
C'est bien beau tout ça, mais quel intérêt ? Voyons ça plus bas.
Synergie des deux technologies pour du multilingue
Imaginons un template HTML taillé pour afficher une même page en multilingue. Un moteur de template pour PHP, ASP, Ruby, Node.js etc... transforme le template en deux pages distinctes fournis au client en fonction de la demande de la page en français ou en anglaise (avec une url différente par exemple). Il pourrait ressembler à cela :
<html lang="{CULTURE_COUNTRY}"> <!-- ... header, body, etc... --> <section> <div class="languages"> <span>{OTHERS_LANGUAGES}</span> <a href="" title="" lang="fr-fr">{LANG_FRENCH}</a> <a href="" title="" lang="en-gb">{LANG_ENGLISH}</a> <a href="" title="" lang="es-es">{LANG_SPANISH}</a> </div> <form id="form" method="post" action="./"> <header>{TITLE}</header> <aside> <header>{TITLE_ALLOWS}</header> <ul> <!-- BEGIN allows --> <li>{allows.ELEMENT_ALLOWS}</li> <!-- END allows --> </ul> <footer>{END_ALLOWS}</footer> </aside> <div class="field"> <label for="form-email">{LABEL_EMAIL}</label> <input id="form-email" type="text" placeholder="{LABEL_EMAIL}" /> </div> <div class="field"> <label for="form-password">{LABEL_PASSWORD}</label> <input id="form-password" type="password" placeholder="{LABEL_PASSWORD}" /> </div> <div class="field"> <label><input id="form-submit" type="submit" value="{LABEL_REGISTRATION}" /></label> </div> </form> </section>
et génèrerait en français :
<html lang="fr-fr"> <!-- ... header, body, etc... --> <section> <div class="languages"> <span>Autres langues</span> <a href="" title="" lang="fr-fr">Français</a> <a href="" title="" lang="en-gb">English</a> <a href="" title="" lang="es-es">Español</a> </div> <form id="form" method="post" action="./"> <header>Inscription</header> <aside> <header>Notre service permet</header> <ul> <li>de tchatter avec d'autres utilisateurs</li> <li>de partager vos photos</li> <li>de rencontrer des personnes en vrai</li> </ul> <footer>Pour le reste, vous êtes grand !</footer> </aside> <div class="field"> <label for="form-email">Email</label> <input id="form-email" type="text" placeholder="Email" /> </div> <div class="field"> <label for="form-password">Mot de passe</label> <input id="form-password" type="password" placeholder="Mot de passe" /> </div> <div class="field"> <label><input id="form-submit" type="submit" value="Inscription" /></label> </div> </form> </section>
et génèrerait en anglais :
<html lang="en-gb"> <!-- ... header, body, etc... --> <section> <div class="languages"> <span>Others languages</span> <a href="" title="" lang="fr-fr">Français</a> <a href="" title="" lang="en-gb">English</a> <a href="" title="" lang="es-es">Español</a> </div> <form id="form" method="post" action="./"> <header>Registration</header> <aside> <header>Our service allows you</header> <ul> <li>to chat with other users</li> <li>to share your photos</li> <li>to meet people in real life</li> </ul> <footer>It's your ride now!</footer> </aside> <div class="field"> <label for="form-email">Email</label> <input id="form-email" type="text" placeholder="Email" /> </div> <div class="field"> <label for="form-password">Password</label> <input id="form-password" type="password" placeholder="Password" /> </div> <div class="field"> <label><input id="form-submit" type="submit" value="Registration" /></label> </div> </form> </section>
Premier exemple : habillage deux colonnes
Habillons le code précédent avec la CSS suivante, je vous invite à lire attentivement la partie Partie langues et ponctuation :
/********************************/ /** Partie design et placement **/ /********************************/ /* Les langues en haut à droite. */ section .languages { float: right; font-size: 0.8em; } /* Pas de flottants qui dépassent du formulaire. */ section form:after { content: ""; display: block; clear: both; } /* Mise du titre en avant. */ section > header { font-size: 1.5em; border-bottom: dashed 1px #cccccc; } /* Les informations accompagnant le formulaire à droite. */ section aside { float: right; padding: 6px 6px 6px 16px; width: 55%; box-sizing: border-box; -moz-box-sizing: border-box; } /* Homogénéité des tailles pour les labels. */ section label { display: inline-block; width: 100px; box-sizing: border-box; -moz-box-sizing: border-box; } /* Alignement et espacement des champs. */ section .field { padding: 16px 16px 0 8px; width: 45%; box-sizing: border-box; -moz-box-sizing: border-box; text-align: right; } /***********************************/ /** Partie langues et ponctuation **/ /***********************************/ /* lang(fr) cible l'élément que si lui-même ou l'un de ses parents contient lang="fr" ou lang="fr-*", */ /* contrairement à [lang=fr-fr] qui ne cible l'élément que s'il a un attribut lang. */ section:lang(fr) a[lang=fr-fr]:after { content: " -"; /* Ajout d'un tiret après le français en français. */ } section:lang(en) a[lang=en-gb]:after { content: " -"; /* Ajout d'un tiret après l'anglais en anglais. */ } /* Après {OTHERS_LANGUAGES}, en français un espace sépare les ":" du texte, et pas en anglais. */ section .languages span:lang(fr):after { content: " :"; } section .languages span:lang(en):after { content: ":"; } /* On cache le lien de changement vers la langue courante, utiliser :lang() sur ".lang" ou "section" revient au même, */ /* les balises héritent toutes deux de l'attribut lang de la balise <html>. */ section .languages:lang(fr) a[lang=fr-fr] { display: none; } section .languages:lang(en) a[lang=en-gb] { display: none; } /* Deux points après le {OTHERS_LANGUAGES} et chaque label pour cet exemple : */ /* en français, */ section aside header:lang(fr):after, section label:lang(fr):after { content: " :"; } /* en anglais, */ section aside header:lang(en):after, section label:lang(en):after { content: ":"; } /* mais pas de tiret après le bouton de validation. */ section label:last-child:after { content: ""; } /* Une virgule après chaque item de la liste. */ /* Sauf pour le dernier qui a un point. */ section li:after { content: ","; } section li:last-child:after { content: "."; }
Ce qui nous donne en français puis en anglais :
Quel intérêt de séparer quelques éléments de ponctuation des phrases elles-mêmes. Vous allez le comprendre avec le second exemple qui utilise la même structure HTML que le premier
Deuxième exemple : habillage en ligne
Changeons l'apparence précédente avec cette nouvelle CSS :
/********************************/ /** Partie design et placement **/ /********************************/ /* Les langues en haut à droite */ section .languages { float: right; font-size: 0.9em; } /* On cache {OTHERS_LANGUAGES} */ section .languages span { display: none; } /* On cache le lien de changement vers la langue courante, utiliser :lang() sur ".lang" ou "section" revient au même, */ /* les balises héritent toutes deux de l'attribut lang de la balise <html>. */ section .languages:lang(fr) a[lang=fr-fr] { display: none; } section .languages:lang(en) a[lang=en-gb] { display: none; } /* Mise en avant du titre. */ section > header { font-size: 1.2em; font-weight: bold; } /* Mise en ligne d'élément nativement en block dans l'aside. */ /* Le texte de l'aside ne sera plus en liste mais en ligne. */ section aside header, section aside footer, section ul, section li { display: inline; } /* On rend inexistant le <ul>. */ section ul { margin-left: 0; padding: 0; } /* on centre le texte */ section aside { text-align: center; padding: 16px; } /* Mise en ligne d'élément nativement en block dans le formulaire. */ /* Le formulaire ne sera plus en liste mais en ligne. */ section .field { padding: 8px; } /***********************************/ /** Partie langues et ponctuation **/ /***********************************/ /* lang(fr) cible l'élément que si lui même ou l'un de ses parents contient lang="fr" ou lang="fr-*", */ /* contrairement à [lang=fr-fr] qui ne cible l'élément que s'il a un attribut lang. */ section:lang(fr) a[lang=fr-fr]:after { content: " |"; /* Ajout d'un pipe après le français en français. */ } section:lang(en) a[lang=en-gb]:after { content: " |"; /* Ajout d'un pipe après l'anglais en anglais. */ } /* On décide, car la liste n'est plus qu'une phrase, de l'entourer de guillemets. */ section aside header:lang(fr):before { content: "« "; } section aside footer:lang(fr):after { content: " »"; } section aside header:lang(en):before { content: "“"; } section aside footer:lang(en):after { content: "”"; } /* Ponctuation de la liste devenue une phrase. */ section li:after { content: ", "; /* Chaque item est séparé par une virgule */ } section li:nth-last-child(2):lang(fr):after { content: " et "; /* Mais l'avant dernier, en français, par un "et" */ } section li:nth-last-child(2):lang(en):after { content: " and "; /* Alors qu'en anglais, se sera par un "and" */ } section li:last-child:after { content: "."; /* Finalement, on remplace la virgule finale par un point. */ }
Ce qui nous donne en français puis en anglais :
De l'utilisation de lang et lang
Je viens de vous présenter l'une des possibilités offertes par l'attribut lang et la pseudo-class :lang(). Cet article n'a pas pour but de dire si la déportation de certains éléments de ponctuations dans une CSS est une bonne ou une mauvaise pratique, mais plutôt de présenter nos deux éléments à travers un exemple concret et surtout de les découvrir pour ceux qui ne les connaissaient pas. Il ne vous aura peut-être pas échappé non plus que l'utilisation de la technique des classes CSS conditionnelles peut remplacer ce mécanisme natif.
1 Commentaire
Choisir un Avatar depuis une url
Adresse : (64px x 64px)ou Changer la couleur de fond
et Choisir un Avatar dans la liste
Votre commentaire a été ajouter ci-dessous ! Si vous désirez le supprimer ultérieurement, servez vous du code suivant :
Les commentaires sont actuellement affiché du plus rescent au plus ancien. Vous pouvez inverser l'ordre en cliquant ci-dessous :
Je laisse un message mais que dire : j'arrive et je découvre le site donc à voir sur le long terme, à +
Je trouve ce commentaire pertinent ?
Je trouve ce commentaire pertinent ?