![]() | |||
Joel on Software Joël à propos de logiciel
| |||
|
D'autres articles de "Joel on Software" en Français D'autres articles de "Joel on Software" en Anglais |
Par Joël Spolsky Traduit par Freddy Hardy Vérifié par Serge Wautier Mercredi 8 octobre 2003 Vous ne vous êtes jamais posé de questions sur cette mystérieuse balise Content-Type ? Vous savez, celle que vous êtes supposé mettre en HTML, et dont vous n'avez absolument jamais su ce qu'elle veut dire ? Vous n'avez jamais reçu un email de vos amis en Bulgarie avec pour objet "???? ?????? ??? ????" ?
Mais il ne disparaîtra pas. Quand j'ai découvert que l'outil de développement web le plus populaire, PHP, avait une ignorance à peu près totale de ces questions d'encodage de caractères, qu'il utilisait d'une manière insouciante 8 bits pour les caractères, rendant de fait presque impossible le développement d'applications web internationales, je me suis dit "trop, c'est trop". J'ai donc une annonce à vous faire : si vous êtes un programmeur qui travaille en 2003 et que vous ne possédez pas les bases des caractères, des jeux de caractères, de l'encodage et d'Unicode, et que je vous attrape, je vais vous punir en vous faisant éplucher des oignons pendant 6 mois dans un sous-marin. Je vous jure que je le ferai. Et encore une chose : CE N'EST PAS SI DIFFICILE. Dans cet article je vais vous donner exactement ce que tout programmeur en activité devrait savoir. Tout ce mélange "texte à plat = ascii = tout caractère est sur 8 bits" n'est pas seulement faux, c'est sans espoir, et si vous êtes encore en train de programmer de cette façon, vous ne valez pas mieux qu'un docteur qui ne croit pas aux microbes. Faites moi le plaisir de ne pas écrire une ligne de code de plus jusqu'à ce que vous ayez fini de lire cet article. Avant de commencer, je devrais vous prévenir que si vous êtes l'une de ces rares personnes qui savent tout de l'internationalisation, vous allez trouver mon exposé un petit peu trop simplifié. Je veux vraiment juste donner ici un minimum afin que tout le monde puisse comprendre ce qui se passe et puisse écrire du code qui ait un espoir de fonctionner avec du texte dans n'importe quelle autre langue qu'une version d'anglais n'incluant aucun mot accentué. J'attire également votre attention sur le fait que la manipulation des caractères est seulement une minuscule partie de tout ce qu'il faut pour créer des logiciels qui fonctionnent dans toutes les langues, malheureusement je ne peux écrire que sur une chose à la fois, alors aujourd'hui c'est les jeux de caractères. Un point de vue historique La façon la plus facile de comprendre tout ça est d'avancer chronologiquement. Vous vous dites probablement que là je vais parler de ces très vieux jeux de caractères comme EBCDIC. Hé bien non. EBCDIC n'est pas vital pour vous. Nous n'avons pas à remonter si loin dans le temps.
Et tout cela était bel et bon, tant que vous êtes anglophone.
Cet OEM ouvert à tous finit par être codifié par le standard ANSI. Dans le standard ANSI, on était d'accord sur ce qu'il fallait faire en dessous de 128, qui était vraiment très proche de l'ASCII, mais on gérait les caractères à partir de 128 de plein de façons différentes, selon où on vivait. Ces différents systèmes furent appelés des pages de codes. Par exemple en Israël DOS utilisait une page de codes appelée 862, alors que les utilisateurs grecs utilisaient la 737. C'était la même chose en dessous de 128, mais différent au dessus, là où les lettres rigolotes habitaient. Les versions nationales de MS-DOS avaient des douzaines de ces pages de codes, capables de prendre en compte n'importe quoi de l'anglais à l'islandais, et il y avait même quelques pages de code "multilangues" qui pouvaient faire de l'esperanto et du galicien sur le même ordinateur! Houah! Mais avoir, disons, de l'hébreu et du grec sur le même ordinateur était une impossibilité totale à moins d'écrire son propre programme capable d'afficher n'importe quoi à l'aide de bitmaps graphiques, parce que l'hébreu et le grec réclamaient des pages de codes ayant des interprétations différentes des nombres du haut. Pendant ce temps, en Asie, des choses encore plus dingues étaient faites pour prendre en compte le fait que les alphabets asiatiques avaient des milliers de lettres, qui ne pourraient jamais tenir sur 8 bits. En fait, la solution trouvée fut un système bordélique appelé DBCS, "double byte character set", le jeu de caractères à double octet, dans lequel certaines lettres étaient stockées sur un octet, et d'autres sur deux. Il était facile de se déplacer vers l'avant à l'intérieur d'une chaîne, mais quasi impossible de reculer. Les programmeurs étaient encouragés à ne pas utiliser s++ et s-- pour se déplacer en avant et en arrière, mais plutôt d'appeler des fonctions, comme les AnsiNext et AnsiPrev sous Windows, qui savaient comment se débrouiller avec tout ce foutoir. Mais même alors, beaucoup de gens se contentaient de prétendre qu'un octet était un caractère et qu'un caractère c'était 8 bits, et que du moment que vous ne déplaciez jamais une chaîne d'un ordinateur vers un autre, ou que vous ne parliez pas plus d'une langue, en gros ça marchait toujours. Mais bien sûr, dès qu'Internet apparu, déplacer une chaîne d'un ordinateur vers un autre devint un lieu commun, et tout ce merdier se cassa la figure. Heureusement, Unicode avait été inventé. Unicode Unicode fut un effort courageux pour créer un jeu de caractères unique qui inclurait tout les systèmes d'écriture raisonnables de la planète et quelques autres fictifs comme le Klingon, également. Certaines personnes sont persuadées à tort qu'Unicode est simplement un code 16-bits où chaque caractère tient sur 16 bits et qu'il y a donc 65536 caractères possibles. En fait, ce n'est pas correct. C'est le mythe le plus commun à propos d'Unicode, donc si vous pensiez ça, ne vous en faites pas. En fait, Unicode possède une façon différente d'appréhender les caractères, et vous devez comprendre cette façon qu'Unicode a d'appréhender les choses, ou rien ne vous semblera clair. Jusqu'à maintenant, nous sommes partis du principe qu'à une lettre correspondaient des bits qu'on pouvait stocker sur disque ou en mémoire. A -> 0100 0001 En Unicode, une lettre correspond à quelque chose appelé un point de code (ou numéro de caractère, ou valeur scalaire Unicode) qui n'est qu'un concept théorique. Comment ce point de code est représenté en mémoire ou sur disque est une tout autre histoire. En Unicode, la lettre A est un idéal platonicien. Elle flotte dans les cieux : A Ce A platonicien est différent de B, et différent de a, mais le même que A et A, et A. L'idée que A dans la police Times New Roman est le même caractère que le A dans la police Helvetica, mais différent de "a" en minuscule, ne semble pas tellement sujet à discussion, mais dans certaines langues, se demander simplement ce qu'une lettre est peut entraîner des controverses. Est-ce que la lettre allemande ß est une véritable lettre, ou juste une façon tordue d'écrire ss ? Si la forme d'une lettre change à la fin d'un mot, est-ce une lettre différente ? La langue hébreue répond oui, l'arabe non. Enfin bon, quoi qu'il en soit, les brillants membres du consortium Unicode ont réfléchi à tout cela durant la dernière décennie ou presque, à grand renfort de débats hautement politiques. Vous n'avez pas donc plus à vous en préoccuper. Ils ont déjà pensé à tout. Toute les lettres platoniciennes de tous les alphabets se sont vues attribuer un nombre magique par le consortium Unicode qui s'écrit comme ceci : U+0645. Ce nombre magique est appelé un point de code. Le U+ signifie "Unicode", et les nombres derrière sont en héxadécimal. U+FEC9 est la lettre arabe Ain. La lettre anglaise A est la U+0041. Vous les trouverez toutes à l'aide de l'utilitaire charmap sous Windows 2000/XP, ou en visitant le site web d'Unicode. Il n'y a pas de véritable limite au nombre de lettres qu'Unicode peut définir et ils sont réellement allés au delà de 65536, ce qui fait que toutes les lettres Unicode ne peuvent pas tenir sur deux octets, mais de toute façon c'était un mythe, alors... OK, alors disons que nous avons une chaîne : Hello qui, en Unicode, correspond aux cinq points de code : U+0048 U+0065 U+006C U+006C U+006F. Juste une poignée de points de code. Des nombres, en fait. Jusque là nous n'avons rien dit sur comment stocker ou représenter ça en mémoire ou dans un message email. Encodages Et c'est là que les encodages interviennent. La première idée pour l'encodage d'Unicode, celle qui a abouti au mythe des deux octets, fut : Hé, on n'a qu'à stocker ces nombres sur deux octets chacun! Hello devient donc : 00 48 00 65 00 6C 00 6C 00 6F D'accord? Pas si vite ! Est-ce que ça ne pourrait pas être : 48 00 65 00 6C 00 6C 00 6F 00 ? Hé bien, techniquement, oui, je pense bien que ça pourrait, et, en fait, les premiers implémenteurs voulaient pouvoir stocker leurs points de code Unicode en mode "poids fort derrière" [high-endian] ou "poids fort devant" [low-endian], selon que leur CPU était rapide ou lente dans l'un ou l'autre, et c'était bonnet blanc et blanc bonnet, et cela faisait déjà deux façons de stocker de l'Unicode. Alors les gens furent obligés d'en arriver à la convention bizarre de placer les caractères FE FF au début de toutes les chaînes Unicode; on appela ça une marque d'ordre des octets Unicode (Unicode Byte Order Mark) et si vous intervertissiez vos octets de poids faible et fort ça donnait FF FE, et les gens qui lisaient votre chaîne savaient qu'ils devraient intervertir tous les autres octets. Pfff. Et toutes les chaînes Unicode en liberté dans la nature ne possédent pas cette marque d'ordre des octets au début.
Pendant un moment il semblait que tout cela aurait pu suffire, mais les programmeurs se plaignaient toujours. "Regarde moi tous ces zéros !", disaient-ils, parce qu'ils étaient américains et qu'ils regardaient des textes en anglais qui utilisaient rarement des points de code au dessus de U+00FF. Et puis c'était des hippies libéraux en Californie qui voulaient préserver (sarcasme). Si ç'avait été des Texans, peu leur aurait importé de bâfrer deux fois la quantité d'octets. Mais ces mauviettes de Californiens ne pouvaient pas supporter l'idée de doubler la quantité de stockage nécessaire pour des chaînes, et puis, de toute façon, il y avait ces foutus documents, dehors, qui utilisaient tous ces jeux de caractères ANSI et DBCS, et qui allait convertir tout ça ? Little Old Me ? Pour cette seule raison, un tas de gens décidèrent d'ignorer Unicode pendant plusieurs années et pendant ce temps les choses empirèrent. Aussi fut inventé le brillant concept UTF-8. UTF-8 était un autre système pour stocker en mémoire vos chaînes de points de code Unicode, ces nombres U+ magiques, en utilisant des mots de 8 bits. en UTF-8, chaque point de code de 0 à 127 est stocké sur un seul octet. Seuls les points de code à partir de 128 et au delà sont stockés en utilisant 2, 3, en fait jusqu'à 6 octets.
Tout ceci a pour effet de bord très net qu'un texte anglais en UTF-8 ressemble exactement à sa version en ASCII, alors pour les américains il n'y a pas le moindre problème. Seul le reste du monde doit endurer le gage. Pour reprendre notre exemple, Hello, qui était U+0048 U+0065 U+006C U+006C U+006F, sera stocké comme 48 65 6C 6C 6F, ce qui est, le croiriez-vous, exactement comme si c'était stocké en ASCII, et en ANSI, et en n'importe quel jeu de caractères OEM de la planète. Maintenant, si vous êtes assez intrépide pour utiliser des lettres accentuées, ou des lettres grecques, ou des lettres Klingon, vous devrez utiliser plusieurs octets pour stocker un seul point de code, mais les américains ne s'en apercevront jamais. (UTF-8 a aussi cette agréable propriété qu'un vieux code de traitement de chaînes voulant utiliser un octet 0 en tant que null-terminateur ne tronquera pas les chaînes). Je vous ai déjà indiqué trois façons d'encoder de l'Unicode. Les méthodes traditionnelles "stockez-moi-ça-sur-deux-octets" sont appelées UCS-2 (à cause des deux octets) ou UTF-16 (à cause des 16 bits), et vous devez toujours vous demander si c'est de l'UCS-2 à poids fort derrière [high-endian] ou de l'UCS-2 à poids fort devant [low-endian]. Et puis il y a la nouvelle et populaire norme UTF-8 qui possède en plus l'agréable propriété d'être respectueuse si vous avez l'heureuse coïncidence de posséder à la fois des textes anglais et des programmes lobotomisés qui ne sont absolument pas au courant qu'il existe autre chose que l'ASCII. Il y a en fait une poignée d'autres manières d'encoder Unicode. Il y a quelque chose qui s'appelle UTF-7, qui ressemble beaucoup à UTF-8 mais qui garantit que le bit le plus haut est toujours à 0, de manière à ce que si vous devez passer Unicode à travers une sorte de système email totalitaire qui pense que 7 bits c'est bien assez, merci, il pourrait encore se faufiler sain et sauf. Il y a UCS-4, qui stocke chaque point de code en 4 octets, qui a l'agréable propriété que tous les points de code sont stockés sur le même nombre d'octets, mais, bon sang, même un Texan ne serait pas assez intrépide au point de gaspiller autant de mémoire. Et en fait, à partir du moment où vous vous figurez les choses en terme de lettres platoniciennes idéales représentées par des points de code Unicode, ces points de code Unicode peuvent être encodés dans n'importe quel schéma d'encodage de la vieille école ! Par exemple, vous pourriez encoder la chaîne Unicode pour Hello (U+0048 U+0065 U+006C U+006C U+006F) en ASCII, ou avec le vieil encodage OEM grec, ou avec l'encodage ANSI hébreu, ou avec n'importe lequel des centaines d'encodages déjà inventés, à un détail près : certaines lettres pourraient bien ne pas s'afficher! S'il n'y a pas d'équivalent pour le point de code Unicode que vous essayez de représenter avec l'encodage que vous utilisez, vous obtenez généralement un petit point d'interrogation : ? ou, si vous êtes vraiment bon, une boîte. Alors, vous obtenez quoi? -> � Il y a des centaines d'encodages traditionnels qui ne peuvent stocker que quelques points de code correctement et qui transforment tous les autres points de code en points d'interrogation. Quelques exemples d'encodages de textes anglais : Windows-1252 (le standard Windows 9x pour les langues ouest-européennes), et ISO-8859-1, alias Latin-1 (utilisable également pour n'importe quelle langue d'europe de l'ouest). Mais essayez de stocker du russe ou de l'hébreu dans ces encodages, et vous obtiendrez une poignée de points d'interrogation. UTF 7, 8, 16, et 32 ont tous la propriété sympa d'être capables de stocker n'importe quel point de code correctement. Le point le plus important à propos des encodages Même si vous oubliez tous ce que je viens de vous expliquer, faites-moi le plaisir de vous rappeler un fait extrêmement important. Ca n'a aucun sens d'avoir une chaîne sans savoir quel encodage elle utilise. Vous ne pouvez plus enfouir votre tête dans le sable et prétendre que c'est du texte ASCII "à plat". Le texte à plat, ça n'existe pas. Si vous avez une chaîne, en mémoire, dans un fichier, ou dans un message email, vous devez savoir de quel encodage il s'agit, ou vous ne pourrez pas l'interpréter ou l'afficher correctement à l'utilisateur. Pratiquement tous les problèmes stupides du genre "mon site web ressemble à du charabia" ou "elle ne peut pas lire mes emails quand j'utilise des accents" viennent d'un programmeur naïf qui n'a pas compris ce simple fait que si vous ne me dites pas si une chaîne particulière est encodée en UTF-8 ou ASCII ou ISO 8859-1 (Latin 1) ou Windows 1252 (europe de l'ouest), vous ne pourrez tout simplement pas l'afficher ou même savoir où elle finit. Il y a plus de cent encodages, et au dessus du point de code 127, tous les paris sont perdus d'avance. Comment préserver cette information sur l'encodage utilisé par une chaîne? Hé bien il y a des normes pour ça. Pour un message email, vous devez normalement avoir une chaîne de cette forme dans l'entête :
Pour une page web, l'idée de départ était que le serveur web retournerait un entête HTTP similaire, Content-Type, avec la page elle même -- pas dans l'HTML lui-même, mais comme l'une des entêtes de réponse envoyées avant la page HTML. Ceci pose des problèmes. Imaginez un gros serveur web avec beaucoup de sites et des centaines de pages écrites par beaucoup de gens dans beaucoup de langues différentes et utilisant tous l'encodage que leur copie de Microsoft FrontPage a jugé bon de générer. Le serveur lui-même ne pourrait pas réellement savoir avec quel encodage est écrit chaque fichier, il ne pourrait donc pas envoyer l'entête Content-Type. Il serait pratique de pouvoir placer le Content-Type HTML directement dans le fichier HTML lui-même, en utilisant un tag spécial. Bien sûr ça rendrait fou un puriste... Comment voulez vous lire le fichier HTML tant que vous ne savez pas quel encodage est à l'intérieur?! Par chance, pratiquement tous les encodages communément utilisés font tous la même chose avec les caractères entre 32 et 127, ce qui fait que vous pouvez toujours avoir ce qui suit dans une page HTML sans commencer à utiliser aucune lettre bizarre :
Mais ce méta tag doit vraiment être la première chose dans la section <head> parce que dès que le navigateur voit ce tag, il arrête de parser la page et recommence son interprétation depuis le début en utilisant l'encodage que vous avez spécifié. Que font les navigateurs web s'ils ne trouvent aucun Content-Type, ni dans les entêtes HTTP, ni dans le méta tag? Internet Explorer fait vraiment quelque chose d'intéressant : il essaie de deviner, en se basant sur la fréquence d'apparition de différents caractères dans des textes courants et dans des encodages courants de différentes langues, quel encodage et quelle langue sont utilisés. Comme les diverses anciennes pages de codes avaient tendance à placer leurs lettres nationales dans différentes plages entre 128 et 255, et comme les histogrammes d'utilisation des lettres de chaque langue humaine ont des caractéristiques différentes, ceci a réellement une chance de fonctionner. C'est vraiment spéc', mais il semble que ça marche suffisament bien pour que les auteurs de pages web naïfs qui n'ont jamais su qu'ils avaient besoin d'une entête Content-Type regardent leur page dans un navigateur, et trouvent que ça à l'air bon, jusqu'à ce qu'un jour ils écrivent quelque chose qui ne se conforme pas exactement à la fréquence de distribution des lettres de leur langue native, et Internet Explorer décide que c'est du Coréen et l'afiche comme tel, démontrant, je pense, qu'être "strict dans ce que [vous] émettez et tolérant dans ce que [vous] recevez" comme le note Larry Wall's n'est peut-être pas franchement un bon principe de conception. En tout cas, que fait le pauvre lecteur de ce site web, qui était écrit en bulgare, mais ressemble à du coréen (et même pas du coréen cohérent) ? Il utilise le menu Affichage | Codage et essaie une poignée d'encodages différents (il y en a au moins une douzaine pour les langues d'europe de l'est) jusqu'à ce que l'image devienne nette. Et si seulement il sait faire ça, ce qui n'est pas le cas pour la plupart des gens.
Pour la dernière version de CityDesk, le logiciel de gestion de site web publié par ma société, nous avons décidé de tout faire en interne en Unicode UCS-2 (2 octets), qui est ce que Visual Basic, COM, et Windows NT/2000.XP utilisent pour leur type chaîne de manière native. En C++, nous déclarons simplement les chaînes comme wchar_t ("wide char") au lieu de char et nous utilisons les fonctions wcs au lieu des str (par exemple wcscat et wcslen au lieu de strcat et strlen). Pour créer un littéral UCS-2 dans le code C, vous mettez simplement un L devant, comme ça : L"Hello". Quand CityDesk publie la page web, il la convertit dans l'encodage UTF-8, qui est bien supporté par les browsers web depuis des années. C'est de cette manière que les 29 versions traduites de Joel on Software sont encodées et je n'ai encore jamais entendu une seule personne qui aurait eu des problèmes pour les visualiser. Cet article est déjà assez long, et je ne peux pas couvrir tout ce qu'il y a à savoir sur les encodages de caractères et Unicode, mais j'espère que si déjà vous avez lu ça, vous en savez assez pour retourner à votre boulot de programmeur et utiliser des antibiotiques plutôt que des poupées vaudou et des envoûtements, une tâche à laquelle je vais maintenant vous abandonner. Cet article est paru en version originale anglaise sous le titre The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) | ||
![]() Joël Spolsky est le fondateur de Fog Creek Software, une petite société éditrice de logiciel située à New York. Il est diplômé de l'Université de Yale et a travaillé comme programmeur et manager chez Microsoft, Viacom et Juno. | |||
Le contenu de ces pages représentent l'opinion d'une personne.
Contenu protégé par le copyright ©1999-2005 par Joël Spolsky. Tous droits réservés.