Gérer l'UTF8 sur vos pages web

 

Si comme moi vous avez créé vos premiers sites il y a plus de 15 ans, vous utilisiez sans doute à l'époque le charset iso-8859-1. Vos fichiers ? Sous Windows en français, ils sont encodés en ANSI. Ce jeu de caractère est une extension de la norme ISO/CEI 8859-1 (également connue sous le nom de « Latin-1 »). Quant à vos tables, elles étaient probablement en latin1 également.

Sauf que… depuis la version 7.3 de php, l'UTF-8 est devenu la norme obligatoire et voilà que des losanges (ou diamonds) fleurissent partout sur vos pages. WordPress a anticipé depuis longtemps (pages en UTF8, tables en utf8_general_ci ou en utf8mb4_unicode_ci voire utf8mb4_unicode_520_ci pour les blogs les plus récents), mais si vous avez fait vos sites web à la main, il va falloir repasser sur chaque page.

Si par chance votre site web est en langue anglaise, vous ne devriez pas être trop ennuyés, puisque vous rencontrerez rarement des caractères accentués. Sinon, prévoyez un peu (beaucoup) de temps. Car changer votre balise meta <meta charset=iso-8859-1″ /> en <meta charset= »UTF-8″> ne va pas suffire ! En effet, dans le cas d'un site dynamique, le navigateur se réfère à l'en-tête renvoyé par le serveur.

Ne parlons pas des header(‘Content-Type: text/html; charset=ISO-8859-1'); et ini_set( ‘default_charset', ‘ISO-8859-1' ); qui nous avaient bien sauvé la mise jusqu'à php 7.2. Il ne fonctionnent plus à partir de 7.3.

Important

Avant toute modification, SAUVEGARDEZ ! Les pages, les scripts, la base de données… Le passage en UTF8 est typiquement le genre de manip dont on ne s'aperçoit que trop tard qu'il y a eu perte de données…

Cas des textes « en dur »

Encoder en HTML

Une solution pour les sites statiques peut consister à encoder tous les caractères accentués et autres caractères spéciaux en HTML (par exemple, é devient &eacute;). Dreamweaver fait ça très bien quand vous collez un texte dans le mode création.

Les diamants ���

Ces losanges noirs avec un point d'interrogation se manifestent quand on veut afficher de l'iso-8859-1 en UTF8. Ils apparaissent par exemple quand votre page est encodée en ASCII et que vous cherchez à l'afficher sur un serveur qui tourne sous php 7.3.

Une solution consiste à convertir l'encodage de votre page web (et ses includes, scripts et css) en UTF8 grâce à Notepad++, Dreamweaver ou UTF8Cast Express. Voir un peu plus bas…

Les caractères pénibles type été

C'est ce qu'on obtient quand on essaie d'afficher de l'UTF8 en iso-8859-1. Par exemple, vous avez utilisé un utf8_encode() sur une chaîne de caractères qui était déjà en UTF8.

Une solution consiste à enlever le utf8_encode() ou s'il n'y en avait pas, à essayer avec utf8_decode().

Convertir l'encodage des pages

Pour des petits sites, je procède une page après l'autre dans Dreamweaver (ce qui me permet de repasser dessus pour d'autres modifs mineures). Modifier > Propriétés de la Page (ou Ctrl+J), onglet Titre/codage.

Changement de l'encodage dans Dreamweaver

Vous pouvez aussi le faire sous Notepad ++ mais attention à bien passer par Encodage > Convertir en UTF-8 et non pas Encoder en UTF-8, sinon il va vous flinguer tous vos caractères spéciaux, comme dans l'exemple ci-dessous.

Notepad++ a flingué tous les caractères accentués

Mais dans le cas de gros sites, ça devient vite fastidieux.

Pour du traitement en lot, on peut alors avoir recours à UTF8Cast Express. Attention, ce logiciel a tendance à considérer un fichier UTF8 sans BOM comme de l'ASCII… Et parfois il essaie d'encoder d'autres fichiers que vos pages html (comme des images gif). Bref, restez attentif.

 

Les bases de données

Ce n'est pas simple. On n'y comprend rien, déjà parce qu'on peut définir l'encodage au niveau de la base, de la table ou même… d'une seule colonne !

Consultation de données

Récupérons des données à l'aide d'un SELECT…

Que votre table soit en latin1 ou en UTF8, vous obtiendrez des diamants si vous ne définissez pas le jeu de caractères par défaut :

mysqli_set_charset($connect_db, "utf8mb4");

Autre solution :

mysqli_query($connect_db,"SET character_set_results=utf8mb4");

Même si elles produisent le même résultat, ces 2 commandes ne sont pas exactement équivalentes, comme vous pouvez le voir si vous faites un mysqli_character_set_name(). Dans le premier cela continuera à renvoyer latin, dans le 2e vous aurez bien utf8mb4.

Insertion de données

J'ai effectué des requêtes INSERT à partir de scripts php encodés ou non en UTF8, dans une table dont les colonnes étaient ou non avec interclassement UTF8, en balançant des phrases truffées de caractères spéciaux (accès, guillemets, tirets longs…) voire même de diamonds histoire de déconner un coup :

Aperçu des tests INSERT

 

Script ANSI, table Latin1, texte contenant des diamonds :

  • avec mysqli_set_charset : caractères spéciaux remplacés par des points d'interrogation
  • sans mysqli_set_charset : caractères spéciaux remplacés par des �

Script ANSI, table Latin1, texte sans diamonds :

  • avec mysqli_set_charset : impec
  • sans mysqli_set_charset : caractères spéciaux remplacés par des é

Script ANSI, table UTF8, texte contenant des diamonds :

  • avec mysqli_set_charset : caractères spéciaux remplacés par des �
  • sans mysqli_set_charset : caractères spéciaux remplacés par des �

Script ANSI, table UTF8, texte sans diamonds :

  • avec mysqli_set_charset : impec
  • sans mysqli_set_charset : caractères spéciaux remplacés par des é et ’

Script UTF8, table Latin1, texte sans diamonds :

  • avec mysqli_set_charset : impec
  • sans mysqli_set_charset : caractères spéciaux remplacés par des é et ’

Script UTF8, table UTF8, texte sans diamonds :

  • avec mysqli_set_charset : impec
  • sans mysqli_set_charset : caractères spéciaux remplacés par des é et ’

Conclusion : le mysqli_set_charset() est indispensable. En revanche, les résultats sont les mêmes, que la table soit UTF8 ou Latin1.

Conversion des tables

Choix du type d'interclassement pour une colonne

Mais bon, pas de miracle, il vous faudra quand même passer vos tables en UTF8 colonne par colonne. Surtout, faites un backup des données au préalable pour les réinjecter après conversion, car vous allez perdre vos caractères spéciaux, voire même parfois ce qui était écrit derrière.

C'est super fastidieux car le déroulant des encodages dans phpMyAdmin est très long. Si vous avez beaucoup de tables et de champs texte, ça va vous prendre beaucoup de temps… Voici un exemple de requête SQL pour un champ de type text.

ALTER TABLE 'matable' CHANGE 'macolonne' 'macolonne' TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;

Petit bonus : je me suis fait un script php (sans doute perfectible) pour éplucher les colonnes d'une table et générer mes requêtes.

$q = query("DESCRIBE ".$table);

    while($row = fetch($q)) {

    $type=strtoupper($row['Type']);
    $flag="";
    if($row['Null']=="NO") { $flag=strtoupper("not null"); }
    
    if(mb_eregi("VARCHAR",$type) || mb_eregi("CHAR",$type) || $type=="TEXT"){
    $sql="<pre>ALTER TABLE '".$table."' CHANGE '".$row['Field']."' '".$row['Field']."' ".$type." CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ".$flag.";</pre>";
  }
  echo $sql;

}

La création dynamique de fichiers

Par défaut, un fichier créé avec fopen() sera en ANSI.

Rétrocompatibilité

Détail intéressant constaté en local sous Wamp : si je repasse sous une ancienne version de php (5.6 avec default_charset = « ISO-8859-1 » dans le php.ini), un fichier encodé en UTF8 s'affichera avec des caractères type é, alors qu'un fichier en UTF8 avec BOM s'affichera correctement.

Si je rajoute ceci

<?php header( 'Content-Type: text/html; charset=utf-8' );?>

Les caractères accentués du fichier encodé en UTF8 s'affichent correctement, comme si on était en php 7.3…

Bref, si vous voulez que vos scripts restent compatibles avec d'anciennes versions de php dont le charset est toujours ISO-8859-1, il ne faut pas oublier cet appel à header().

Scripts php divers

Quand je dois supprimer tous les caractères accentués, j'utilise cette fonction remove_accents() comme expliqué dans ce topic de Stackoverflow Suppression des caractères accentués.

Par ailleurs, si utf8_encode() ne suffit par pour les caractères Windows-1252 tels que le signe euro (€) et les guillemets bouclés (“ ”), on a alors recours à iconv().

$contenu=iconv("UTF-8", "CP1252", $contenu);

$contenu=mb_convert_encoding($contenu, "UTF-8", "ISO-8859-1");

En résumé

Conversion de tous les scripts, CSS et js en UTF8

Pour la base de données (après connexion à la base)

mysqli_set_charset($connect_db, "utf8mb4");

Pour la rétrocompatibilité

<?php header( 'Content-Type: text/html; charset=utf-8' );?>

Si vous devez stocker des caractères étrangers (pinyin, sinogrammes), convertir les seules colonnes concernées en effectuant au préalable un backup des données existantes puis en les réimportant après conversion, car elles risquent fort de se trouver endommagées (les réimporter ensuite). Cela évitera les erreurs de type « Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation ‘like' »

Bon courage !

Sources

Encodage UTF-8 : tout pour mener votre projet à bien

Comment diagnostiquer un problème d'encodage ISO ou UTF-8 ?

Passez à l'UTF-8 sans manquer une étape

Karine SANCHE

Partager cet article