Bien gérer la localisation des sites en CSS3 et HTML5

Il y a des solutions simples pour gérer avec une feuille CSS l'affichage de différentes langues. Vous vous êtes peut-être déjà confronté à un problème similaire : votre super menu s'affichant en ligne est parfait dans la langue des Vulcains avec une taille de caractère mais malheureusement ne l'ai plus avec la langue des Na'vis.

Quand votre site sera déployé pour des localisations différentes, les feuilles CSS ne pourront alors pas être les mêmes et vous allez maintenir autant de fichiers différents que de localisation ? C'est ce que vous ferriez en faisant un très mauvais choix. Arrêtons-nous 5 minutes pour explorer une piste bien pratique pour assurer la maintenance d'une feuille CSS et de ces différentes localisations.

Oubliez les multiples variantes d'une même feuille CSS

Une idée rapide serait de créer une feuille CSS initiale basée sur le rendu de texte « Lorem Ipsum » et de vérifier que son affichage se passe sans accroche. Il conviendrait ensuite de dupliquer cette CSS de base une fois finie et de modifier toutes les propriétés CSS qui divergent pour une autre langue de manière à ce que tout se passe bien également.

Mais QUID de la maintenance ? Si maintenant je dois modifier la feuille CSS de base à partir de laquelle mes 30 variantes par localisations ont été crées : je vais devoir réinjecter localisation par localisation toutes les nouvelles modifications en prenant soin de ne pas écraser dans chacune les lignes qui varies en fonction de la langue. Je ne vous parle même pas des conflits.

Il y a également de fortes chances pour qu'au final, prise par groupe, les variantes pour 30 localisations ne soient qu'au nombre de 3 (petits mots, mots moyens et grands mots). Ça fait beaucoup de fichiers inutiles, car redondant, tout ça.

Une feuille CSS unique pour toutes les localisations

Et si plutôt que d'éparpiller le code de manière redondante dans autant de fichier qu'il y a de localisation, nous gérions la localisation dans un seul et unique fichier ? Vous allez me dire que chaque localisation aurait alors des lignes qui ne la concerne pas et souffrirait d'un poids de page inutilement élevé ? Vous avez raison, mais faisons abstraction de ce point pour le moment.

Création avec comme base l'anglais

Tout commence de la même manière : on crée sa feuille CSS en se basant sur la langue anglaise qui a été fournie comme référence pour la création. Après plusieurs lignes, notre design est fini et les textes s'affichent comme suit :

HTML5 en anglais

<!DOCTYPE html>
<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <title>Localisation example</title>
    </head>
    <body>
        <div class="logo">
            <h1><a href="javascript:;" title="Return to Home">Title</a></h1>
        </div>
        <nav class="main-nav">
            <ul>
                <li><a href="javascript:;" title="Home">Home</a></li>
                <li><a href="javascript:;" title="Products">Products</a></li>
                <li><a href="javascript:;" title="Care and Advice">Care and Advice</a></li>
                <li><a href="javascript:;" title="Fun and Games">Fun and Games</a></li>
                <li><a href="javascript:;" title="News">News</a></li>
            </ul>
        </nav>
    </body>
</html>

CSS3 sans localisation

/* Séparateurs haut et bas de l'exemple */
body {
     border-bottom: 1px solid #aaa;
     border-top: 1px solid #aaa;
     padding: 8px 0;
}

/* Maintenir le titre à gauche */
.logo {
    position: absolute;
    font-size: 1.7em;
    font-weight: bold;
    padding: 0 16px;
}

/* Placer la zone menu à droite */
.main-nav {
    float: right;
}

/* Rendre le ul « transparent » */
.main-nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Permettre aux items d'êtres en ligne */
.main-nav li {
    display: inline-block;
    padding-left: 0;
}

/* Inclure de l'espace à gauche de tous les items sauf le premier */
.main-nav li + li {
    padding: 8px 0 8px 11px;
}

/* Créer un séparateur entre chaque item */
.main-nav li + li:before {
    margin-right: 13px;
    content: "|";
}

Rendu

Localisation en français et problèmes

Le marché français souhaite une version du site dans sa langue et fournit donc les traductions pour la localisation. Après injection, notre code HTML ressemble à ça :

HTML5 en français

<!DOCTYPE html>
<html lang="fr-fr">
    <head>
        <meta charset="utf-8" />
        <title>Exemple de localisation</title>
    </head>
    <body>
        <div class="logo">
            <h1><a href="javascript:;" title="Retour à l'accueil">Titre</a></h1>
        </div>
        <nav class="main-nav">
            <ul>
                <li><a href="javascript:;" title="Accueil">Accueil</a></li>
                <li><a href="javascript:;" title="Produits">Produits</a></li>
                <li><a href="javascript:;" title="Soins et Conseils">Soins et Conseils</a></li>
                <li><a href="javascript:;" title="Divertissements et Jeux">Divertissements et Jeux</a></li>
                <li><a href="javascript:;" title="Actualités">Actualités</a></li>
            </ul>
        </nav>
    </body>
</html>

et CSS3 identique à la précédente feuille

Rendu

Et là votre mise en page est mise à mal. Une première solution serait de forcer la traduction à ne pas avoir de caractères en plus que la version anglaise. Parfois c'est envisageable... et parfois pour certaine langue c'est impossible.

Note : j'ai volontairement forcé la largeur du composant à 560px (désactivation de la responsivité) pour que vous puissiez constater le problème quelque soit votre périphérique d'affichage.

Localisation de la feuille CSS

C'est là que vous vous demandez s'il ne va pas falloir maintenir une feuille différente qui sera chargée ou non sur la page en fonction de la localisation. Le problème est que vous allez par exemple faire une feuille différente pour la localisation française et espagnole alors que pour un peu les ajustements seront identiques pour ces deux localisations. Puis, par la suite, le texte va changer et vous ferez la chasse aux ajustements à travers tous vos fichiers.

Pour ma part, je vous propose de gérer toutes les localisations dans les CSS initiales ; par exemple tout en bas. Effectivement, quand le site sera affiché dans telle ou telle langue il y aura des instructions chargées inutilement. Mais dans la masse de propriétés « non localisée » la taille devient négligeable.

Modifions donc notre feuille CSS initiale :

/*-------------------*\
    $SOMMAIRE
\*-------------------*/

/**
 * SOMMAIRE..........c'est moi !
 * STANDARD..........les propriétés CSS de la page.
 * LOCALISATION......les propriétés variants en fonction de la langue.
 */





/*-------------------*\
    $STANDARD
\*-------------------*/

/* Séparateurs haut et bas de l'exemple */
body {
     border-bottom: 1px solid #aaa;
     border-top: 1px solid #aaa;
     padding: 8px 0;
}

/* Maintenir le titre à gauche */
.logo {
    position: absolute;
    font-size: 1.7em;
    font-weight: bold;
    padding: 0 16px;
}

/* Placer la zone menu à droite */
.main-nav {
    float: right;
}

/* Rendre le ul « transparent » */
.main-nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Permettre aux items d'êtres en ligne */
.main-nav li {
    display: inline-block;
    padding-left: 0;
}

/* Inclure de l'espace à gauche de tous les items sauf le premier */
.main-nav li + li { /*** localisé ***/
    padding: 8px 0 8px;
    padding-left: 11px; /* Propriété sortie pour être surchargée seule plus loin */
}

/* Créer un séparateur entre chaque item */
.main-nav li + li:before {
    margin-right: 13px;
    content: "|";
}




/*-------------------*\
    $LOCALISATION
\*-------------------*/
.main-nav li + li:lang(fr) {
    padding-left: 4px;
}
.main-nav li + li:before:lang(fr) {
    margin-right: 6px;
}

Rendu

La pseudo-classe :lang() et l'attribut lang

Grâce à la pseudo-classe :lang() vous pouvez conditionnellement appliquer du style à un élément HTML en HTML5 en fonction du contenu de l'attribut lang de la balise <html>.

Dans le cas de figure ci-dessous :

<html lang="fr-fr">
    ...
    <div class="content">Texte</div>
    ...
</html>

les sélecteurs 1 et 2 ci-après seront appliqués, mais pas le 3 :

.content:lang(fr-fr) {
    color: #ccc;
}

.content:lang(fr) {
    font-weight: bold;
}

.content:lang(en-us) {
    color: #f00;
}

Support xHTML/HTML4 ou CSS 2.1

Cette technique peut également être utilisée sans la pseudo-classe :lang() qui a l'avantage de matcher avec « fr-fr » si on spécifie simplement « fr » mais de ne pas fonctionner tout cours avec les navigateurs les plus inutiles vieux.

Seulement avec du CSS 2.1

Il convient de remplacer :lang(fr-fr) en fin d'instruction par [lang=fr-fr] en début. Ainsi le code précédent devient :

[lang=fr-fr] .content {
    color: #ccc;
}

[lang=fr-fr] .content {
    font-weight: bold;
}

[lang=en-us] .content {
    color: #f00;
}

Sans HTML5

L'idée est ici de remplacer <html lang="fr-fr"> qui n'est pas valide xHTML/HTML4 par <html class="fr-fr"> et de manager côté Back-end la présence ou non de cette classe en fonction de la langue. Le code CSS devient alors :

.fr-fr .content {
    color: #ccc;
}

.fr-fr .content {
    font-weight: bold;
}

.en-us .content {
    color: #f00;
}

Note : n'hésitez pas, même en HTML5, à ajouter cette classe pour améliorer les performances de vos sélecteurs.

Gérer la localisation avec le remplissage de contenu

Il est également possible de ne pas piéger les règles dans les fichiers CSS et de laisser vos remplisseurs de contenu se charger de gérer les problèmes CSS liés à la taille du contenu.

Dans ce cas il suffit d'ajouter une balise <style></style> en début de contenu et de changer le comportement avec celui-ci, ainsi les modifications de design liée au contenu son attaché à celui-ci.

Gérer par les Front-end Developper

p {
    width: 50%;
}

Gérer par les Content Filler

<style>
.on-the-fly-behavior {
    width: 60%;
}
</style>
<p class="on-the-fly-behavior">Je suis un peu trop large pour tenir sur une ligne</p>

Quand le texte est chargés depuis une autre langue, c'est une autre instruction qui vient avec la balise <style>, il n'est donc même plus nécessaire d'ajouter à ses sélecteurs HTML :lang().

Le mot de la fin

Au final vous maintiendrez beaucoup moins de fichier et pourrez gérer les différentes tailles de contenu par groupe de pays !

.header:lang(es),
.header:lang(de),
.header:lang(fr) {
    /* Propriété communes */
}
.header:lang(us) {
    /* Propriété communes */
}
.content:lang(de),
.content:lang(fr) {
    /* Propriété communes */
}
.content:lang(es),
.content:lang(us) {
    /* Propriété communes */
}
.footer:lang(fr) {
    /* Propriété communes */
}
.footer:lang(de),
.footer:lang(es),
.footer:lang(us) {
    /* Propriété communes */
}