Supprimer les accents d’une chaîne en PHP

Le problème

Si vous lisez ceci, vous ne voulez probablement pas supprimer que les accents, mais également les diacritiques d’une chaîne. (Par exemple la cédille de « ç » est un diacritique.)

Vous connaissez sans doutes les « solutions » à base de regex, de strtr ou même d’iconv, mais aucune n’est réellement satisfaisante, n’est-ce pas ? Et c’est normal car aucune n’est faite pour ça.

La solution

Heureusement, PHP 5.4 et intl 2 ont changé la donne avec l’introduction du Transliterator. Ainsi pour supprimer les diacritiques d’une chaîne UTF-8, écrivez

<?php

$transliterator = Transliterator::create(
    'NFD; [:Nonspacing Mark:] Remove; NFC;'
);

// Affiche « Hehe, ca marche ! »
echo $transliterator->transliterate(  
    'Héhé, ça marche !'
);

Comment ça marche ?

La translittération est l’opération qui consiste à substituer à chaque graphème d’un système d’écriture un graphème ou un groupe de graphèmes d’un autre système[…].

Dans notre cas, nous devons substituer les graphèmes avec diacritique avec les graphèmes correspondants, sans diacritique.

Pour cela, nous utilisons la chaîne

NFD; [:Nonspacing Mark:] Remove; NFC;  

qui est un identifiant de translittération composé. En l’occurrence, il est composé des identifiants NFD, [:Nonspacing Mark:] Remove et NFC, chacun correspondant à une transformation.

NFD

Il s’agit d’un identifiant basique qui va permettre la normalisation de la chaîne au format NFD. La particularité de cette norme vient du fait qu’elle décompose les caractères ; par exemple « ç », deviendra « c » + « ¸ ». Notez toutefois qu’ils resteront visuellement identique, c’est ce qu’on appelle l’équivalence canonique.

[:Nonspacing Mark:] Remove

Maintenant que nous avons séparé les diacritiques des caractères, cet identifiant va servir à les supprimer.
C’est un single identifier, constitué d’un filtre et d’une transformation.

Le filtre « Nonspacing Mark » agira en ne conservant que les caractères appartenant à ladite catégorie Unicode, puis la transformation « Remove » supprimera ces caractères.

On se retrouve alors avec notre chaîne débarassée de ses diacritiques. Ne reste plus qu’une chose à faire !

NFC

Notre chaîne suit pour le moment la norme NFD. Seulement, cette norme est déconseillée par le W3C qui lui préfère NFC pour des raisons historiques.

Nous allons donc nous fier à eux et retransformer notre chaîne selon cette norme, après quoi nous obtiendrons notre chaîne débarassée de ses diacritiques, et validée par le W3C !

Cas d’utilisation

En pratique on devra rarement ne faire que supprimer les diacritiques d’une chaîne, ce qui veut dire que vous n’utiliserez probablement pas le code plus haut.

Adresse postale

Idéalement, le texte d’une adresse postale ne devrait être composé que de majuscules sans diacritiques (même si la norme Afnor NF Z10-011 est plus laxiste, cela reste obligatoire pour le nom de la ville). Nous devrions donc transformer « Châtillon-sur-Seine » en « CHATILLON SUR SEINE » par exemple.

Une telle transformation peut se noter

::Latin-ASCII; ::Upper; [^[:L:]]+ > ' '

Si la syntaxe est différente c’est que nous n’avons plus affaire à un identifiant mais à des règles. On devra donc écrire

<?php

$transliterator = Transliterator::createFromRules(
    "::Latin-ASCII; ::Upper; [^[:L:]]+ > ' ';"
);

// Affiche « CHATILLON SUR SEINE »
echo $transliterator->transliterate(  
    'Châtillon-sur-Seine'
);

Slug

L’intérêt d’un slug est de rester lisible dans une URL. Généralement, on le compose à partir de lettres minuscules sans diacritiques, de chiffres et de tirets. Par exemple notre « Héhé, ça marche ! » donnerait le slug « hehe-ca-marche ».

On se retrouve alors avec une chaîne quasi-similaire à la précédente :

::Latin-ASCII; ::Lower; [^[:L:][:N:]]+ > '-'

Cependant si vous la testez, vous obtiendrez « hehe-ca-marche- ». En effet pour des raisons de simplicité, il est plus pertinent de supprimer les caractères en début et fin de chaîne à l’aide de trim.

<?php

$transliterator = Transliterator::createFromRules(
    "::Latin-ASCII; ::Lower; [^[:L:][:N:]]+ > '-';"
);

// Affiche « hehe-ca-marche »
echo trim(  
    $transliterator->transliterate('Héhé, ça marche !'),
    '-'
);

Aller plus loin

Je ne me suis volontairement pas attardé sur les exemples précédents car les possibilités de translittérations constituent un sujet à part entière ; je vous invite donc à consulter la documentation d’ICU pour l’approfondir.

Vous pouvez trouver la liste des classes de caractères Unicode sur la documentation PHP.

Pour conclure, n’oubliez pas qu’aussi puissant soit l’outil, il fera toujours mieux ce pour quoi il est conçu !