Passer du latin1 à l’unicode

logo site du zéro

L’inconvénient du latin1 est qu’il utilise un encodage de caractère basé sur 1 octet, soit 256 possibilités (1 caractère = 1 octet = 8 bits, dans chaque bit il peut y avoir soit 0 soit 1 ; 2 ^ 8 = 256, d’où les 256 possibilités). Ainsi, dans la page, il ne sera possible de placer que 256 caractères différents. Avec cette norme, on ne peut jamais placer des caractères accentués et des caractères chinois sur la même page par exemple.

Liste des caractères de la norme ISO-8859-1 (les mots en italiques sont des caractères de contrôles ajoutés pour son utilisation sur Internet) :

tableau_ISO-8859-1

Pour résoudre cette contrainte, l’unicode a été développé. Cela permet d’afficher des caractères accentués et des caractères d’un autre alphabet (russe, grec etc.) ou d’autres caractères spéciaux (signes chinois, japonais etc.).

Le fait que l’unicode soit codé sur plusieurs octets posera certains problèmes, notamment avec les fonctions PHP gérant les chaines de caractères. En effet, certaines fonctions se basent sur les bits de cette chaine, ce qui posera problème, par exemple, pour mesurer celle-ci.

Encoder les fichiers

Pour passer son site internet du latin1 à l’unicode, il va falloir modifier l’environnement de travail, c’est lui qui va encoder les caractères du fichier et les sauvegarder. La majorité des éditeurs encodent, par défaut, les fichiers en latin1.

Ce n’est pas tout. Tous les fichiers déjà créés sont encodés en latin1 : il va donc falloir les modifier pour que les caractères utilisent la norme d’encodage de l’unicode.

L’environnement de travail

Il faut configurer l’outil de travail de telle sorte que lors de la création de tout nouveau fichier, l’encodage des caractères soit par défaut réglé à UTF-8. Avec Notepad, aller dans Paramétrage, Préférences, sélectionner l’onglet Nouveau document / Répertoire puis cocher UTF-8 sans BOM.

Le BOM est le marquage d’ordre des octets du fichier, complètement inutile en UTF-8. En choisissant cette option, l’éditeur de texte insérera cet ordre d’octets au tout début du fichier. Il sera donc impossible d’appeler la fonction header de PHP. Des caractères auront déjà été envoyés au navigateur : c’est la marque d’ordre des octets.

Encoder les fichiers déjà créés

Deux solutions.

1/ Modifier les fichiers un par un. Editer le fichier avec Notepad++, aller dans Format, cliquer sur Convertir en UTF-8 (sans BOM). Enregistrer.

OU

2/ Modifier tous les fichiers d’un répertoire pour les encoder en UTF-8 grâce à PHP. En faisant appel à la fonction utf8_encode. Cette fonction permet d’encoder une chaine de caractères donnée (encodée en latin1, soit ISO-8859-1) en UTF-8.

Placer ce fichier dans le répertoire à encoder et le lancer. Une fois que le script a terminé de travailler, tous les fichiers du répertoire seront encodés.

Code : PHP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php
    $dossier = opendir ('.');

    while ($fichierAEncoder = readdir ($dossier))
    {
        if (is_file ($fichierAEncoder))
        {
            $contenu = file_get_contents ($fichierAEncoder);
            $fichier = fopen ($fichierAEncoder, 'w');
            fputs ($fichier, html_entity_decode (utf8_encode ($contenu)));
            fclose ($fichier);
        }
    }

    closedir ($dossier);
?>

Les fichiers contenus dans les sous-répertoires ne seront donc pas encodés.

Pour cela, exécuter ce code (tous les fichiers contenus dans le même répertoire que le script ainsi que dans tous les sous-répertoires seront encodés) :

Code : PHP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
    function encoderDossier ($dossierAEncoder)
    {
        $dossier = opendir ($dossierAEncoder);

        while ($fichierAEncoder = readdir ($dossier))
        {
            if ($fichierAEncoder != '.' AND $fichierAEncoder != '..')
            {
                if (is_file ($fichierAEncoder))
                {
                    $contenu = file_get_contents ($fichierAEncoder);
                    $fichier = fopen ($fichierAEncoder, 'w');
                    fputs ($fichier, html_entity_decode (utf8_encode ($contenu)));
                    fclose ($fichier);
                }

                else
                    encoderDossier ($dossierAEncoder . $fichierAEncoder . '/');
            }
        }

        closedir ($dossier);
    }

    encoderDossier ('./');
?>

Désormais, tous les fichiers sont encodés en UTF-8, et l’environnement de travail correctement configuré.


Encoder la base de données

Pour la base de données (C’est le SGBDR MySql qui est utilisé ici) il faut l’encoder en utf8_general_ci.

Possibilité non offerte dans le cas d’un hébergement sur serveur mutualisé.

Il faudra modifier l’encodage de la BDD en appelant mysql_set_charset dans un script PHP juste après s’être connecté. Exemple :

Code : PHP

1
2
3
4
<?php
    mysql_connect ('localhost', 'root', '');
    mysql_set_charset ('UTF8');
?>

Si la version de PHP est inférieure à la version 5, cette fonction ne sera pas disponible. Il faudra exécuter une simple requête grâce à mysql_query en utilisant SET NAMES ‘encodage’:

Code : PHP

1
2
3
4
<?php
    mysql_connect ('localhost', 'root', '');
    mysql_query ('SET NAMES \'UTF8\'');
?>

Ces deux méthodes reviennent au même.

Dorénavant, pour créer une table, il faudra impérativement choisir un interclassement commençant par utf8_, comme utf8_general_ci par exemple (champ multilingue insensible à la casse).

Exemple pour créer une table, voici le type de code SQL à exécuter :

Code : SQL

1
2
3
4
5
CREATE TABLE ma_table (
  id int(11) NOT NULL auto_increment,
  champ varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY  (id)
) DEFAULT CHARSET=utf8;

Ainsi, le champ champ sera encodé en UTF-8.

Si la table a déjà été créé et que tous les champs sont encodés en latin1, alors il va falloir les modifier de telle sorte à ce qu’ils soient encodés en UTF-8. Deux solutions. La première est de les modifier un par un via phpMyAdmin. La seconde, plus rapide, est d’exécuter une simple requête sur la table :

Code : SQL

1
ALTER TABLE `ma_table` CONVERT TO CHARACTER SET utf8

Modifier les entêtes

L’encodage des fichiers n’est pas suffisant pour qu’ils soient correctement interprétés par le navigateur : il faudra indiquer quelle norme d’encodage utilise la page.

Modifier l’entête envoyée par le serveur

La première chose consiste à modifier les entêtes envoyées par le serveur. Par défaut, Apache travaille avec la norme ISO-8859-1, il va donc falloir lui dire que l’on veut qu’il travaille avec la norme UTF-8.

Dans le cas d’un accès à la configuration d’Apache (en local ou avec un serveur dédié par exemple), il est possible de modifier le fichier de configuration d’Apache (httpd.conf). Il faut ajouter une des deux lignes suivantes :

Code : Apache

1
2
3
AddDefaultCharset Off
# ou alors
AddDefaultCharset UTF-8

La première instruction indique au serveur de se renseigner sur l’entête de la page HTML pour savoir quelle norme elle utilise, il sera ainsi toujours en cohérence avec la norme utilisée par la page demandée.

Si pas d’accès au fichier httpd.conf, Il est possible d’ajouter une de ces deux lignes dans le fichier .htaccess.

Modifier l’entête de la page

Après la modification des entêtes du serveur, on vient à la modification des entêtes de la page.

Deux solutions.

1/ Passer par PHP en utilisant la fonction header
.

Il faut spécifier en paramètre le type de la page ainsi que son encodage (dans notre cas, UTF-8).

Code : PHP

1
2
3
<?php
    header ('Content-type:text/html; charset=utf-8');
?>

Ainsi, le navigateur saura que cette page est encodée en UTF-8 et pourra afficher les caractères correctement.

2/ Placer une balise meta entre les balises <head> </head>
des pages.

Code : HTML

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

Attention, en utilisant ces deux techniques sur la même page, la fonction header prendra le dessus.

En faisant un header ('Content-type:text/html; charset=iso-8859-1'); puis en ajoutant la balise meta suivante dans l’entête de la page HTML <meta http-equiv="Content-type" content="text/html; charset=utf-8" />, le navigateur interprétera la page comme si elle était encodée en latin1 !

Utilité de modifier les entêtes envoyées par le serveur puis les entêtes du fichier, sachant qu’en ne modifiant que les entêtes du fichier sans modifier celles envoyées par le serveur, la page s’affiche correctement :

pour que la page soit bien interprétée au cas où le fichier ne spécifie aucun encodage. C’est donc celui du serveur qui sera utilisé.

Si la page est encodée en UTF-8 et que le serveur envoie une entête avec un ISO-8859-1 en guise de norme d’encodage, certains caractères ne s’afficheront pas comme il faut (notamment les caractères accentués).


Problème

Comme dit dans la première partie, le fait que l’unicode encode ses caractères sur un ou plusieurs octets peut poser problème.

Voici le cas de la fonction strlen. Cette fonction renvoie le nombre d’octets de la chaine qu’on lui passe en paramètres.

Un caractère normal (a, z, e, r, t, y etc.) est codé sur un octet (soit 8 bits, d’où le nom de UTF-8), mais, par exemple, les caractères accentués sont codés sur 2 octets, soit 16 bits. Ainsi, la fonction suivante renverra 3 :

Code : PHP

1
2
3
<?php
    echo strlen ('éa'); // é = 2 octets et a = 1 octet
?>

Cette norme encodant ses caractères sur plusieurs octets (4 maximum), il va falloir utiliser les fonctions sur les chaînes de caractères multi-octets. Toutes ces fonctions commencent par le préfixe mb_ (multi bytes).

Avant tout, il faudra indiquer le type d’encodage que l’on souhaite utiliser avec ces fonctions en utilisant mb_internal_encoding. Par défaut, l’encodage utilisé est ISO-8859-1, il va donc falloir lui envoyer en paramètre UTF-8. Si rien n’est spécifié en paramètre, la fonction retourne l’encodage utilisé.

Code : PHP

1
2
3
4
5
6
7
<?php
    echo mb_internal_encoding(); // affiche ISO-8859-1
    echo mb_strlen ('éa'); // affiche 3 car le serveur utilise la norme UTF-8

    mb_internal_encoding ('UTF-8'); // on utilise la norme UTF-8
    echo mb_strlen ('éa'); // affiche 2
?>

On peut aussi spécifier en deuxième paramètre de mb_strlen l’encodage à utiliser si celui-ci n’est pas le même que celui actuellement utilisé. Exemple :

Code : PHP

1
2
3
<?php
    echo mb_strlen ('éa', 'UTF-8'); // affiche 2
?>

Voilà, il est désormais possible de mesurer les chaines en utilisant la norme UTF-8.

La version 6 de PHP travaillera par défaut avec la norme UTF-8 et non plus avec la norme ISO-8859-1.


Annexes

Annexes du tutoriel. Cette sous-partie répertorie les différentes erreurs susceptibles d’être rencontrées.

Caractères bizarres

Image utilisateur

Il est possible de rencontrer des caractères « bizarres » qui n’ont rien à faire à cet endroit. Il y a deux sortes de caractères « bizarres » :

Flèche« � » => la chaine de caractères est encodée en ISO-8859-1. Il y a de fortes chances que le navigateur croit avoir affaire à de l’UTF-8 (probablement parce que la page dit au navigateur qu’elle utilise l’UTF-8). Utiliser la fonction utf8_encode sur la chaine de caractères posant problème si elle est issue d’une variable PHP. Ce genre de caractère peut aussi s’afficher si le caractère qui devrait s’afficher ne fait pas parti de la table de caractères de cette norme (exemple : le ÿ majuscule). Pour cela, utiliser l’entité HTML correspondante (exemple : &Yuml;) ;

Flèche« Ã© », « Ã® », « Ã » etc. => la chaine de caractères est encodée en UTF-8 mais le navigateur croit avoir affaire à de l’ISO-8859-1 (probablement parce que la page dit au navigateur qu’elle utilise l’ISO-8859-1). Utiliser la fonction utf8_decode sur la chaine de caractères posant problème si elle est issue d’une variable PHP.

Fonctions sur les chaines de caractères

Ne jamais utiliser la fonction htmlentities.

Cette fonction convertit la chaine en entitées HTML de la norme ISO, ce qui ne plaira pas au navigateur dès lors que l’on lui a indiqué que la page utilise la norme UTF-8. Dans le cadre de l’utilisation de cette fonction, il faut alors indiquer la norme d’encodage à utiliser en 3ème paramètre :

Code : PHP

1
2
3
<?php
    htmlentities ('azéoràèndjùd', ENT_COMPAT, 'UTF-8');
?>

Cependant, il est préférable d’utiliser la fonction htmlspecialchars.

Les fonctions manipulant les chaines de caractères sont aussi une grosse source d’erreurs (strlen, substr, strpos etc.).

Pour remédier aux problèmes qu’elles impliquent, utiliser les fonctions sur les chaines de caractères multi-octets.

Un dernier point important utile est celui des expressions régulières avec les options de celles-ci. Cliquez ici pour en savoir plus.