Encoding et alphabets internationaux : comprendre et debugger (1ere partie)

Lors de l'affichage de pages avec des mots dans des alphabets différents il peux arriver que des problèmes d'affichages apparaissent avec des � et des é

Dans le cas de Gisgraphy (exemple pour Pékin), j'ai voulu vérifier que tout s'affiche correctement et que ce qui est affiché correspond bien à l'équivalent ASCII.

Je me suis donc rendu sur cette page et j'ai testé 北京, ce qui m'a donne 'bei jing'.ouah ça marche! et mon navigateur affiche le japonais! essayons en arabe avec بكين , cela me donne "bkyn" qui se rapproche de Beijing et Pékin en phonétique, et là aussi ça s'affiche correctement.

Cela m'a donné l'idée d'un post pour démystifier l'encoding par une suite de questions-réponses. (Finalement je le ferai en deux posts car c'est un peu long, et je risque de perdre et décourager pas mal de lecteurs si je n'en fais qu'un). Le premier expliquera ce qu'est l'encoding :

  • Qu'est ce qu'Unicode ?
  • Qu'est-ce que l'encoding, le BOM, le Little et le Big Endian
  • Quel est le meilleur encoding ?
  • Comment fonctionne une police de caractère ?
  • Comment gérer les sens d'écriture différents (par exemple, l'arabe qui s'écrit de droite à gauche)

Le second expliquera comment résoudre les problèmes fréquents avec de cas concrets et des extraits de code :

  • Comment gérer l'encoding dans un environnement web en java (Java, Apache, Tomcat, ModJK, base de données, navigateurs)
  • Comment gérer et débugger des problèmes d'encoding en java

Après ces deux posts vous ne devriez plus jamais voir ça : � sur vos sites, ou si c'est le cas vous saurez comment résoudre le problème :)

Qu'est ce qu'Unicode

Unicode attribut à tout caractère, symbole, ponctuation de n’importe quel système d’écriture, de langue, ou d'alphabet, un nom et un identifiant numérique, quelle que soit la plate-forme informatique.

On représente un caractère Unicode par la lettre U suivi d'un '+' et d'un nombre en représentation hexadécimal. Exemple pour la lettre A : U+0041. Le nom 'Unicode' associé à la lettre A est 'LATIN CAPITAL LETTER A'. On remarquera qu'il s'agit de la lettre 'A' en majuscule et que son équivalent en minuscule est différent (U+0061 et 'LATIN SMALL LETTER A'). Une liste de tous les noms classés par ordre alphabétique est consultable ici

Qu'est-ce que l'encoding, le BOM, le Little et le Big endian

L'encoding est une façon binaire de représenter un caractère Unicode. Si on prend l'exemple d'un fichier, l'encoding permet de stocker de façon binaire les caractères Unicode.

Les codages sont nombreux : (UTF-8, UTF-16, UTF-32, ISO-8859, Windows-1252 ...) et souvent déclinés en plusieurs versions. Pour L’iso-latin, il en existe plusieurs versions :

  • ISO-8859-1 où certains caractères comme 'œ' ont tout simplement été oubliés, au même titre que le 'ÿ'. Si vous vendez des œufs à Haÿ-les-roses, votre site risque de souffrir de problèmes d'encoding s'il est en ISO-8859-1 (tout savoir sur cette sombre histoire)
  • ISO-8859-15 où le symbole euro € à été ajouté ainsi que œ et Ÿ.

Pour l'UTF-16, c'est la même chose, il existe :

  • UTF-16
  • UTF-16BE pour 'Big Endian'
  • UTF-16LE pour 'Little Endian'

Little Endian et big Endian correspondent au sens de lecture des octets. On peut prendre la même analogie qu'entre l'arabe qui se lit de droite à gauche, et l'anglais qui se lit de gauche à droite. Pour les ordinateurs c'est pareil, il faut leur indiquer quel est le sens de lecture. il n'y a pas de règle, le but étant de permettre au programme qui affichera les caractères, de connaitre le sens de lecture.

Pour Windows, à chaque région du monde correspond un codage:

  • Windows-1252 (connu sous le nom de ANSI ou CP1252) pour l'alphabet latin
  • Windows-1256 pour les pays arabes
  • Windows-1257 pour les pays baltes
  • ...

Le cyrillique et le japonais ont aussi plusieurs versions, il existe deux versions du chinois,...L'encoding, c'est un peu la tour de babel de l'informatique

Cela vous parait abstrait? Prenons un exemple pour mieux comprendre : Ouvrons un éditeur de texte, créons un nouveau fichier et écrivons un simple 'é', puis sauvegardons. Avant de sauvegarder, il nous est demandé de spécifier l'encoding, choisissons UTF-8. et UTF-8.txt en nom de fichier. Recommençons l'opération avec ISO-8859-1,UTF-16, UTF-16BE et UTF-16LE.

Maintenant ouvrons les fichiers avec un éditeur hexadécimal, afin de voir comment ils ont été sauvegardés. Voici le contenu Hexadécimal de chaque fichier:

ISO-8859-1 : E9 0A
UTF-8.txt : C3 A9 0A
UTF-16.txt : FF FE E9 00 FF FE 0A 00
UTF-16BE.txt: 00 E9 00 0A
UTF-16LE.txt : E9 00 0A 00

On peux faire les observations suivantes :

  • Pour une même lettre, le fichier est enregistré différemment. C'est ça l'encoding !
  • Le caractère 0A est ajouté à la fin de chaque fichier pour en marquer la fin.
  • Tous les UTF-16 prennent plus de place que l'UTF-8 puisqu'ils sont stockés sur 16 bits
  • L'UTF-16 commence par FFFE, il s'agit du BOM ou byte order mark. Le BOM sert à spécifier le type d'endian utilisé. Lorsque vous sauvegardez un fichier en UTF-16 vous ne spécifiez pas s'il s'agit de Big Endian ou de Little Endian, à la différence des UTF-16BE et UTF-16-LE. Un BOM égal à FFFE signifie qu'il s'agit d'UTF-16 en Little Endian, et un BOM égal à FEFF signifie qu'il s'agit d'UTF-16 en Big Endian. J'en veux pour preuve que le caractère 'é' est représenté de la même façon (E9 00) en UTF-16LE qu'en UTF-16 avec le BOM FFFE, tandis que l'ordre des octets est inversé en Big Endian (00 E9)
  • L'UTF-16BE et l'UTF-16LE prennent moins de place car il ne faut pas spécifier l'endian via le BOM

Quel est le meilleur encodage

Par expérience je pense que l'UTF-8 est souvent le plus adapté (il permet de coder tout les caractères unicode, et son support est répandu), mais bien sur il n'y a pas de bonne ou mauvaise réponse, sinon on n'utiliserai que le meilleur et on jetterai les autres. Les avantages et les inconvénients résident dans la place qu'ils occupent et le traitement qu'il faut faire pour les afficher.

L’Unicode n’assigne pas un nombre d'octets prédéfini par caractère, on l’appelle "multi-octet" : Ainsi, chaque caractère peut occuper entre 1 et 6 octets en UTF-8, ce qui optimise la taille de stockage mais augmente la difficulté de traitement. l'UTF-16 code tout les caractères sur 2 * 16 bits (un pour spécifier l'endian, l'autre pour le caractère), ce qui prend plus de place mais minimise les traitements.

Enfin L'UTF-16BE et UTF-16LE occupe moins de place, mais comme on ne connait pas L'endian utilisé il faut le spécifier par un autre moyen : par exemple dans le charset mime type du protocole HTTP, l'attribut encoding d'un fichier XML, etc. S'il est impossible de spécifier le type d'endian, il faudra se rabattre sur de l'UTF-16 avec BOM ou espérer que le logiciel qui affichera l'unicode a un algorithme de détection de l'encoding performant...plutôt hasardeux.

Qu'est-ce qu'une police de caractère

Maintenant que l'on a compris comment L'Unicode est représenté en binaire, voyons comment il est affiché, cette représentation est appelée 'glyphe'. C'est le rôle des polices de caractères Unicode. Souvent on pense à tord, qu'a un caractères glyphe correspond un Unicode. C'est malheureusement faux et plus complexe : un glyphe peut être associé à un ou plusieurs caractères Unicode et il n'y a aucune bijection.

Exemple avec le glyphe ê qui il peux être représenté de plusieurs façons en Unicode :

  • Par le caractère unicode U+00EA (C3 AA en encoding UTF-8)
  • Soit en précédent la lettre e par un autre caractère Unicode stipulant que le caractère suivant aura un accent circonflexe.

le même glyphe sera affiché.

On dira du premier caractère qu’il est précomposé, du second que c’est une composition (deux caractères forment un seul glyphe composé de deux caractères Unicode)

Démonstration : Sachant que l'accent circonflexe en Unicode est égal à U+0302 et CC 82 en UTF-8.

J'ouvre un éditeur hexadécimal et met CC 82 65 C3 AA A0 CC 82 : COMBINING CIRCUMFLEX ACCENT 65 : LATIN SMALL LETTER E C3 AA : LATIN SMALL LETTER E WITH CIRCUMFLEX A0 : ferme le fichier je sauvegarde, puis j'ouvre le fichier avec GEdit : deux ê apparaissent. L'encoding a été détecté en UTF-8 par GEdit et c'est bien la preuve qu'un glyphe n'est pas associé qu'a un seul caractère Unicode

Comment gérer les sens d'écritures différents

En général les logiciels modernes le détectent automatiquement : si vous copier coller 'الرباط' dans différents logiciels, le sens d'écriture changera automatiquement :

  • Sous firefox ou internet explorer, si vous le copier-coller dans un champs de saisie puis que vous appuyez plusieurs fois sur la barre d'espace, vous verrez les espaces s'afficher à la gauche du texte : le sens de l'écriture a changé.
  • Sous Windows : Avec wordpad (Démarrer->executer->tapez wordpad, ne pas confondre avec Word). une liste déroulant affichera 'arabe' et en appuyant sur la flèche gauche du clavier, elle se déplacera à droite :) un peu troublant mais logique.
  • Sous Ubuntu / Linux : Avec GEdit c'est encore plus fort, le texte s'affichera directement à droite.

En aucun cas c'est à vous de gérer cette complexité, au pire, il faut le spécifier au logiciel qui affiche les glyphes, Unicode ne définit rien à ce sujet, il s'agit de l'affichage, pas de la représentation informatique.vous pouvez aller sur cette page pour écrire en différentes langues.

pour convertir d'Unicode en différents formats, j'ai trouvé cette page très intéressante : http://rishida.net/tools/conversion/

Le second expliquera comment résoudre les problèmes fréquents avec de cas concrets et des extraits de code :

  • Comment gérer l'encoding dans un environnement web en java (Java, Apache, Tomcat, ModJK, base de données, navigateurs)
  • Comment gérer et débugger des problèmes d'encoding en java

Commentaires

1. Le samedi 17 juillet 2010, 14:27 par Wilfrid

Bonjour,

Il existe ce qui ressemble à un bug dans l'encodage utf-8. La phrase "Je suis allé à Paris", une fois encodée en utf-8, donne "Je suis allé à Paris". Jusque là tout va bien.

Malheureusement, si on la décode on obtient "Je suis allé � Paris" ! Le a accent grave n'est pas converti correctement. J'ai essayé avec divers convertisseurs mais j'obtiens toujours un hiéroglyphe à la place du à.

Si vous avez une solution je suis preneur ! :-)

Wilfrid

2. Le mardi 20 juillet 2010, 14:02 par Wilfrid

C'est bon, j'ai trouvé la solution ! Il suffit de remplacer tous les "à" par la séquence \u00E0 plutôt que par leur code utf-8.

Ça n'empêche qu'on est bien en présence d'un bug du décodeur utf-8, parce qu'autrement on n'aurait pas besoin de procéder à cette manip.

La discussion continue ailleurs

URL de rétrolien : https://davidmasclet.gisgraphy.com/index.php?trackback/26

Fil des commentaires de ce billet