Convertir un site en UTF-8

Je m'interrogeais il y a quelques temps sur l'encodage à adopter lors de la création d'un site. Après avoir passé 2 journées à essayer de convertir l'intégralité d'un site en UTF-8 je commence à comprendre la bête.

Pour vous éviter de galérer je vous ai fait un petit "How to", avec des explications :-) .

A savoir

Le problème de l'encodage c'est que ça intervient à plusieurs niveaux :

Pas simple. Je ne vais pas vous expliquer ce que c'est exactement l'encodage... (je suis bien trop flemmard), par contre je vais vous expliquer en détails comment réussir à convertir un site, et également comment configurer votre serveur pour ne plus avoir ces satanés problèmes d'encodage.

Apache

Apache est configuré par défaut pour fournir du latin1 (ISO-8859-1) donc il faut lui dire de donner de l'UTF-8 :

Dans votre fichier de configuration
(sous Debian : /etc/apache2/apache2.conf
 sous Windows : apache2/conf/httpd.conf) modifiez cette ligne :

AddDefaultCharset	ISO-8859-1
Par :
AddDefaultCharset	UTF-8

Si vous n'avez pas accès à votre configuration Apache vous pouvez toujours
demander à PHP de fournir un autre header :

header('Content-Type: text/html; Charset=UTF-8');

Après un redémarrage Apache fournira un header spécifiant l'encodage en UTF-8.

MySQL

MySQL supporte l'UTF-8 depuis sa version 4.1, si vous avez une version inférieure pensez à mettre à jour ;-) (la 5 est sortie et stable quand même...).
Il n'y a pas vraiment de configuration à faire pour MySQL, il faut juste créer correctement la base et spécifier l'encodage pour les champs qui en ont besoins (les champs de texte).

Pour la création d'une base précisez le CHARSET (l'encodage à adopter) et la COLLATION (l'attitude que MySQL doit avoir par rapport aux données) :

CREATE DATABASE base CHARACTER SET utf8 COLLATE utf8_bin;

Cette requête permet de créer une base nommée "base" qui stockera ces données
en UTF-8 (attention à spécifier utf8 et non utf-8) et qui comparera
les données sensiblement à la casse.

Pour rendre ces données insensibles à la casse il suffit d'adopter
une collation utf8_general_ci (par défaut).

Pour la création d'une table c'est sensiblement la même chose :

CREATE TABLE table (
    id TINYINT NOT NULL,
    texte VARCHAR(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
) CHARACTER SET utf8;

L'ordre d'importance des CHARSET & COLLATE est la suivante :
- S'il y a une définition pour un champs, MySQL l'applique ;
- Sinon il opte pour la définition de la table ;
- Sinon pour la définition de la base.

Si vous voulez forcer MySQL à utiliser l'UTF-8 pour une requête il suffit
de faire cette requête :

SET NAMES 'utf8';

Jusque là c'est facile, maintenant on va attaquer le plus important : PHP (et pas le plus simple).

PHP

PHP ne supporte pas nativement l'UTF-8 et ça pose de sérieux problèmes...
Le support natif est prévu pour la version 6, mais il reste toujours une alternative pour rendre son PHP compatible UTF-8 : mbstring.

mbstring est une extension (un "plugin" pour PHP si vous voulez) permettant de gérer l'encodage des caractères de manière très complète.

Tout d'abord il faut activer l'extension, rendez vous dans votre fichier de
configuration (/etc/php5/apache2/php.ini sous Debian, php/php.ini sous Windows)
et cherchez l'endroit ou les extensions sont chargés (mbstring est activé par
défaut sous Debian, sous Windows il faut décommenter la ligne
"extension=php_mbstring.dll").

Ensuite cherchez [mbstring] pour arriver à la configuration de l'extension,
il vous suffit d'appliquer cette configuration :
mbstring.language=UTF-8
mbstring.internal_encoding= UTF-8
mbstring.http_input=UTF-8
mbstring.http_output=UTF-8
mbstring.detect_order= auto

Il reste quand même un problème : UTF-8 n'est pas codé sur un nombre fixe de bits. Ca a comme répercutions de fausser des simples fonctions comme strlen() qui se base sur le nombre d'octets pour renvoyer la taille d'une chaîne. Heureusement il y a une alternative : mbstring est capable d'intercepter toute les fonctions de traitement de chaîne et de les remplacer par l'équivalent mbstring (remplacer strlen() par mb_strlen()). Pour ce faire il suffit de changer un paramètre de la configuration :

mbstring.func_overload = 7
La valeur 7 permet de changer toute les fonctions qui ont quelque chose
à voir avec les chaînes de caractères.

0 désactive l'interception ;
1 intercepte uniquement mail() ;
2 intercepte str*() ;
4 intercepte ereg*().
Vous pouvez également combiner ces valeurs.

N'oubliez pas de redémarrer Apache, et pensez à vérifier que mbstring est correctement lancé (avec un phpinfo() par exemple).

xHTML

Pas grand chose à faire ici :

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

Si votre page comporte un prologue XML il peut être intéressant de
préciser l'encodage également :

<?xml version="1.0" encoding="UTF-8" ?>

Le navigateur

Les navigateurs ont des priorités sur l'encodage, les voici :

  • Préférence utilisateur (pour forcer l'encodage)
  • Header HTTP
  • Prologue XML (si présent)
  • meta

Ce qui veut dire que si vous précisez au moins une information en UTF-8 le navigateur "interprétera" comme tel, cela veut également dire que si vous spécifiez un meta en UTF-8 alors que le header spécifie que la page contient de l'ISO-8859-1, elle sera affichée en UTF-8 (a vérifier quand même...).

L'encodage des fichiers

Dernier point à soulever : lorsque vous éditez un fichier et que vous l'enregistrez il est sauvegardé suivant un encodage également. Si vous avez un fichier avec un mauvais encodage PHP pourrait ne rien comprendre (surtout pour les textes en dur).
Heureusement la plupart des éditeurs permettent de choisir l'encodage à adopter, choisissez UTF-8 sans BOM (parfois noté "sans signature" ou "UTF-8 sans Cookie"). Si vous avez un fichier PHP comportant une signature (il s'agit de quelque octets en début de fichier spécifiant que le contenu est en UTF-8) vous pouvez rencontrer des erreurs du type "headers already sent".

Convertir un site

Si vous voulez convertir un site il vous suffit d'appliquer ces configurations. Mais avec l'encodage rien n'est simple, si votre base de données était en latin1 il vous faut la convertir :

  • Faites un dump (une sauvegarde de la BDD, séparez les données de la structure si vous voulez) ;
  • Créez une nouvelle base en UTF-8 ;
  • Changez les charset et collate, si vous avez un gros dump cette commande peut vous aider (sous Linux, je n'ai pas l'équivalent Windows) :
    sed -i 's/CHARSET=latin1/CHARACTER SET utf8 COLLATE utf8_bin/' dump.sql, bien sûr remplacez latin1 par les charset actuel de votre structure...) ;
  • Convertir le fichier en UTF-8 (toujours sous Linux) :
    iconv -f ISO8859-1 -t UTF-8 -o dump_encoded.sql dump.sql (bien sûr changez -f ISO8859-1 par l'encodage de base de votre fichier) ;
  • Importez dump_encoded.sql dans votre base en UTF-8 :-)
    (si vous rencontrez un problème pendant l'importation il peut s'agir de caractères ' (apostrophe) qui viennent directement de Windows [encodage WINDOWS-1252], heureusement sed est là pour nous : sed -i "s/’/\\\'/g" dump.sql).

Normalement vous aurez un site correctement encodé en UTF-8. Si jamais vous voyez des caractères incorrects c'est qu'il vous reste des sed à faire (il peut s'agir de "oe" propres à Windows, cherchez les caractères qui posent problème et faites des sed pour modifier directement le dump, une fois que vous aurez identifié tous les caractères qui posaient problème vous pourrez passer en production)

Pour reconnaître les problèmes d'encodage c'est simple :

  • Si vous voyez des "é", "î", "Ã", etc. c'est que les données sont en UTF-8 et que le navigateur les affiche en ISO ;
  • Si vous voyez des "�" c'est que les données sont en ISO et que le navigateur les affiche en UTF-8 ;
  • Si vous voyez des "￾" c'est que les données sont en WINDOWS-1252 (le plus souvent) et que le navigateur les affiche en UTF-8.

Voila, vous devriez vous en sortir, si vous avez des problèmes, si j'ai fait une erreur ou que vous voyez quelque chose à rajouter n'hésitez pas à commenter ;-)

Merci à Thierry Sottani pour son excellent tutoriel.

Commentaires

Merci pour l'ensemble de ses conseils. J'utilise aussi le format UTF-8 et je ne connaissais pas toutes les astuces pour configuer Apache et MySQL ! Maintenant faut que je trouve le temps pour me plonger là dedans :D

Lol ça a fait bugger GReader le dernier "?" ^^

J'ai un doute cependant, tu es sûr que les utf8_bin sont case-insensitive ? J'aurais juré le contraire ;)

Bien vu Julien, j'ai inversé, c'est corrigé (quel distrait je fais ^^)...

Bonjour,

J'ai suivi tous ces conseils mais je n'arrive pas à résoudre mon problème d'encodage. En fait, mon problème est le suivant : je n'arrive pas à sauvegarder des symboles euro € dans ma base mysql. Ils sont automatiquement remplacé par des "?". :(
C'est d'autant plus bizard que je teste mon appli sur 2 machines différentes, configurées de la mm façon, à partir des mêmes scipts :(

Merci d'avance pour votre aide.

Il faut faire attention à plusieurs choses pour la base de données.

Déjà la base doit être en UTF-8, si elle l'est il faut vérifier que les données le soit bien également.

Pour ça je pense que la meilleure manière est de se connecter en ligne de commande au serveur MySQL (sous linux c'est beaucoup mieux, on peut vérifier l'encodage de l'affichage, qui est par défaut en UTF-8). Si avec un SELECT sur une table qui contient (ou doit contenir) un symbole € donne un '?' c'est que l'importation n'a pas été pas fonctionné pour ce caractère. Bien sûr si tu vois un € c'est que l'importation est bonne et que cela ne vient pas de la base de données.

Le problème pourrait venir de l'encodage WINDOWS-1252 (une fois de plus), si tu as convertit ton dump d'ISO-8859-1 en UTF-8 en utilisant iconv et qu'au final tu te retrouve avec des caractères incorrects, c'est que le fichiers contenait des caractères non ISO. C'est ce que j'explique pour les apostrophes de WINDOWS-1252.

Pour résoudre cela, ou même identifier les caractères il n'y a pas 36 solutions : afficher le caractère en question sous plusieurs encodage (toujours grâce au terminal de linux).

Moi je repère du texte à coté du symbole (par exemple si le texte est "le prix est de : 1400€"), je fais un grep de "le prix est de" (il affichera toute la ligne). Après je n'ai plus qu'a "switcher" l'encodage du terminal jusqu'à trouver le bon encodage du caractère.

Une fois trouvé il suffit de faire un copier/coller du caractère (toujours avec le bon encodage dans le terminal), puis de le remplacer par celui qu'il faut (avec sed -ie "s/le_caractere/le_bon_caractere/g" dump.sql).

Après il faut vider la BDD, puis réimporter... (oui c'est assez long...)

Pour le symbole € ça peut être long, mais pour des caractères moins courants (comme les "oe") un remplacement manuel sera plus rapide ;) .

Bonjour,

Merci pour ces conseils mais je ne pense pas que le problème se trouve au niveau de l'insertion en base. Les JSP que j'utilise sont encodées en UTF-8 ainsi que mes tables. Quand j'affiche mes données avant insertion j'ai bien le symbole "€", un fois sauvegardée en base, j'ai systématiquement des "?". Comme je tourne un peu en rond :(, pourrais-je avoir un peu plus de détails notamment sur cette partie du précédent post :

"Si avec un SELECT sur une table qui contient (ou doit contenir) un symbole € donne un '?' c'est que l'importation n'a pas été pas fonctionné pour ce caractère."

Merci par avance pour votre aide.

Quand j'affiche mes données avant insertion j'ai bien le symbole "€"

> Attention justement. Tu peux avoir un € même si ton fichier n'est pas en UTF-8, il suffit que le programme que tu utilise l'affiche dans le bon encodage. Par exemple si tu vois un € et que ton programme affiche le fichier en ISO-8859-1 cela voudra dire que le symbole vient de ISO-8859-1. Iconv permet de convertir un fichier d'un encodage donné à un autre. Si après un iconv il y a des caractères incorrects c'est qu'ils n'étaient pas dans l'encodage spécifié.

Schématiquement :
- J'ai un fichier en ISO-8859-1
- Je demande à iconv de convertir mon fichier en UTF-8 (je lui indique que le fichier de base est en ISO-8859-1)
- iconv me fourni un fichier en UTF-8
- J'ouvre ce fichier en vérifiant bien que mon éditeur affiche de l'UTF-8
- S'il y a des caractères incorrects c'est qu'ils n'étaient pas en ISO-8859-1. Dans ce cas il faut faire des 'sed' avant iconv pour remplacer les caractères d'encodage différents.

Après pour ta base de données c'est pareil, quand tu demandes d'afficher quelque chose (je ne sais pas ce que tu utilises) tu obtiens un résultat suivant un certain encodage. Si tu demandes d'afficher des données ISO en UTF-8 ça sera incompréhensible (plein de signes qui font saigner les yeux).

Donc il faut systématiquement vérifier l'encodage d'affichage du programme que tu utilises :) .

Et moi je suis condamné à faire de l'iso-8859-1 pour la todolist :(

à noter qu'à défaut de l'extension mb_string, qui n'est pas forcément disponible partout parce que pas activée par défaut dans php, il y a l'extension iconv fr.php.net/iconv (plus simple à utiliser d'ailleurs).

"cela veut également dire que si vous spécifiez un meta en UTF-8 alors que le header spécifie que la page contient de l'ISO-8859-1, elle sera affichée en UTF-8 (a vérifier quand même...)."

Merci pour tes infos bien pratiques. Si je suis tombé sur ton billet, c'est parceque justement j'ai lancé des recherche car je n'obtenais pas de bons résultats avec des fchiers encodés en UTF-8 et un meta UTF-8 (avec IE, FF et Opéra, la détection automatique restait bloquée sur ISO-8859-1). J'ai donc suivit tes informations sur la config Apache en changeant le header, ça venait de là. Je vérifie donc, mais le contraire de ce qui est dit, avec un header en ISO, la page sera considérée ISO ;-)
Donc merci bien pour les informations, qui s'avèrent capitales pour une bonne convertion de site en UTF-8.

Ca pux l'utf-8 ! Moi jarrète, na!

Très bon article.
Je me suis retrouvé confronté il y a quelques mois au même problème.
J'ai fait globalement pareil, mais dans certains cas (notamment les JDBC), il fallait également tenir compte de l'encodage de la communication entre le serveur d'application et le serveur de base de données.

Enfin je rajouterais que : "Le problème, c'est pas l'UTF8, ce sont les gens qui ne l'utilisent pas". ;)

J'ai un problème lorsque j'importe mon dump dans ma BDD. Mes fichiers, ma base, mon serveur etc sont bien configurés en utf8 et j'ai testé tout ce qui est proposé sur cette page.
Quand je regarde les données en base ça s'affiche bien mais depuis le site ça me fait '?'. Le problème vient bien du dump parce que les caractères en dur dans les fichiers s'affichent bien et si je fais un insert en BDD depuis le site ça s'affiche correctement quand je récupère la donnée. Par contre quand je regarde cette donnée en base ça me mets les caractères bizarres typiques d'utf8 : ddééùùù et si je change l'affiche de ma page en iso1 ça m'encode à nouveau ces caractères en utf8 : ddééùùù. Du coup je comprends plus rien et je ne sais pas à quel moment mes données sont mal encodées ni comment corriger le problème...

Pour info, je mets ça en entête de mon dump :
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

Pour la création de ma BDD, pour chaque table & chaque varchar je spécifie "CHARACTER SET utf8 COLLATE utf8_general_ci"

Je suis pas sure que ça soit très clair mais si quelqu'un a une idée ^^'

> Quand je regarde les données en base
Avec quoi ?

>> Quand je regarde les données en base
>Avec quoi ?
phpmyadmin ou en ligne de cmd sous windows

J'utilise un CMS pour réaliser des sites qui utilise l'encodage utf-8 et je n'ai aucun problème quant à l'affichage des données dans les pages vues par les navigateurs. Cependant, quand je passe l'adresse du site sur spidersimulator, les caractères sont affichés en iso du type 'délégation'.
Utiliser utf-8 pourrait il géner pour les moteurs de recherche ?

Bonjour,

J'ai un probleme lors de l'ecriture d'un fichier avec fwrite (initialement ouvert par fopen).
Sur une machine linux ou l'encodage (locale) est spécifié en UTF-8 ca marche, mais sur un serveur windows, le fichier de sortie est au format ISO...?
Je ne vois pas comment utiliser mb_strings pour résoudre mon probleme ?
J'aimerais que mon appli genere tout le temps de l'UTF-8 quelque soit la configuration de l'OS.

Pour info, j'aiinstallé mb_string et mis les proprietes suivantess dans mon php.ini :

mbstring.language=UTF-8
mbstring.internal_encoding= UTF-8
mbstring.http_input=UTF-8
mbstring.http_output=UTF-8
mbstring.detect_order= auto


Merci par avance

Je ferme les commentaires sur ce billet.

Un blog n'est pas un forum, et je n'ai pas de temps à consacrer à vos problèmes (bah oui, je consacre mon temps à mes problèmes d'abord...).

Laissez le vôtre !

Les commentaires pour ce billet sont fermés.

À propos du billet

mardi 6 mars 2007 à 12:36

Classé dans :

19 commentaires

Navigation inter-billets