Anecdotes sur meta charset

La ligne de code suivante vous est probablement familière :

<meta charset="utf-8">

Elle est un moyen d’indiquer aux user agents l’encodage du document HTML dans lequel elle se trouve.

Aussi familière qu’elle soit, saviez-vous ce qui suit ?

Elle est rétrocompatible

Il peut sembler étrange d’avoir introduit un nouvel attribut charset plutôt que d’utiliser name et content ; c’est en fait un moyen ingénieux de raccourcir la déclaration

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

http-equiv avait à l’origine pour but de définir des directives pour le serveur, mais cela n’est jamais arrivé. Définir l’encodage est donc devenu le problème du navigateur, en plus du support de syntaxes invalides. L’absence de guillemets autour d’un attribut contenant une espace par exemple :

<meta http-equiv=Content-Type content=text/html; charset=utf-8>

charset devient alors un attribut ! S’il est déjà supporté, il ne reste qu’à le rendre valide. On supprime également les attributs http-equiv et content devenus inutiles, et paf.

utf-8 est obligatoire

Suite à une pull request fusionnée fin 2017, utf-8 devient la seule valeur (insensible à la casse) admise pour toute déclaration d’encodage. En conséquence, UTF-8 est maintenant le seul encodage valide pour un document.

Cette décision se base sur deux arguments :

Dans un soucis de rétrocompatibilité, le WHATWG a spécifié quels encodages doivent continuer à être supportés — et comment — dans son Encoding standard. La promotion d’UTF-8 rend toutefois leur utilisation invalide.

Il y a mieux à faire

Imaginez un livre d’anaglyphes dans lequel se trouve une paire de lunettes 3D. Vous allez tout d’abord devoir feuilleter le livre pour les trouver, puis vous pourrez les utiliser pour profiter de son contenu.

Anaglyph of Saguaro National Park at dusk.
Un anaglyphe est une image imprimée pour être vue en relief.

De la même façon, déclarer l’encodage d’un document dans ce même document force le user agent à trouver cette déclaration avant de pouvoir parser le document. Dans un soucis d’optimisation, vous êtes tenus de la faire tenir dans les 1 024 premiers octets, ce qui permet d’abandonner la recherche cette limite passée.

Si nous filons la métaphore, cela revient à ne chercher les lunettes qu’entre un nombre limité de pages. Il serait plus judicieux de les placer en dehors du livre, afin de pouvoir les chausser avant son ouverture. En l’occurence nous pouvons spécifier l’encodage d’un document via le paramètre charset de l’en-tête Content-Type. Ainsi le user agent pourra commencer le parsing dès réception du document.

Si vous n’êtes pas en mesure de configurer le serveur, deux solutions :

charset n’est pas character set

Mais ces deux termes peuvent désigner une character map. La faute à un manque de discernement entre les différentes composantes d’un encodage.

Heureusement, une terminologie a été proposée par le Consortium Unicode en 1998 et standardisée en 2004 dans son Unicode Technical Report #17 : le Unicode Character Encoding Model. Quelles sont ses définitions ?

Character set

Ce terme vient de la terminologie d’IBM : CDRA où il désigne un ensemble de caractères à encoder. Par exemple ASCII définit un jeu de 128 caractères fermé, ce qui signifie qu’aucun caractère ne lui sera ajouté. En comparaison celui d’Unicode (le Universal Character Set) est ouvert et compte 137 928 caractères dans sa version 12 (où 554 ont été ajoutés).

Pour éviter toute ambiguïté, on préfèrera parler d’Abstract Character Repertoire.

Charset

L’origine de sa définition actuelle vient de la proposition de terminologie de l’IAB : la mise en correspondance d’une séquence de caractères vers une séquence d’octets sérialisée. En d’autres termes, il s’agît de l’ensemble des transformations qui permettent l’encodage des caractères d’un répertoire :

  1. Un Coded Character Set assigne des points de code (code points) aux caractères. Par exemple le Universal Coded Character Set assigne le point de code U+1F635 au caractère 😵.
  2. Une Character Encoding Form transforme ces points de code en séquences de bits de taille fixe (code units). Par exemple UTF-16 transformera U+1F635 en 0xD83D 0xDE35 ; deux code units de… 16 bits !
  3. Un Character Encoding Scheme transforme ces code units en séquences d’octets. Par exemple UTF-16LE (pour Little Endian) transformera 0xD83D 0xDE35 en 0x3D 0xD8 0x35 0xDE.

Le charset de notre exemple transforme 😵 — une séquence d’un caractère — en 0x3D 0xD8 0x35 0xDE — une séquence d’octets sérialisée.

Pour éviter toute ambiguïté, on préfèrera parler de Character Map.