UTF-8, internazionalizzare il proprio web

Ricordate questo articolo a proposito della gestione corretta di un sito codificato in ISO-8859-1? (Se non sapete di che parlo, leggetevelo, visto che almeno l’introduzione vi sarà utile per comprendere questo articolo)

Oggi parlerò di come estendere il proprio supporto alle lingue di tutto il mondo tramite UTF8 (Unicode Transformation Format, 8 bit), ossia uno dei charset che permette di codificare tutti i caratteri di qualsiasi alfabeto, dal cinese al suomi, e qualche posticino avanza pure per vari caratteri speciali (mai visto ❤ o ♫?)
Anche se UTF8 non è l’unica codifica a permettere ciò, è il charset raccomandato dall’IETF (Internet Engineering Task Force), quello standard per XML e JSON (i principali formati per il data exchange). Presenta anche altri vantaggi: essendo un charset multibyte a lunghezza variabile, codifica alcuni caratteri con 1, altri con 2, 3 o 4 byte. Ciò significa che se io uso solo i primi 256 caratteri, ossia alfabeto latino e poco più, le codifiche Latin 1 e UTF8 sono identiche, garantendo la stessa dimensione della stringa e la compatibilità con sistemi limitati all’ASCII (se usassi una stringa UTF8 in ANSI C non avrei problemi finchè rimango in quel range). Inoltre, tutti gli alfabeti conosciuti (compreso quello cuneiforme e quello fenicio) riescono a essere codificati con massimo 3 byte.
Per una comparazione con UTF16, UTF7 e UTF32, il sito UTF-8 Everywhere fornisce un bello scorcio della situazione.

Detto ciò, per un sito internazionalizzato, che abbia pochi problemi di codifica, che non sprechi memoria e che sia compatibile con i maggiori standard web, la giusta codifica è UTF8.

Grazie, ma come la implemento?
Niente paura! C’è da lavorare un po’, ma è possibile. Parlando di una configurazione PHP – MySQL, abbiamo le solite tre cose da equalizzare: codifica dell’output HTML, codifica del sorgente PHP, codifica del database MySQL.
Per quanto riguarda HTML, le solite cose da fare: tag <meta> per le pagine e impostazione dell’header Content-Type per gli script AJAX.

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

e

//il MIME-type dipende da che tipo di output volete servire: questo è per il JSON, text/html per semplice HTML, text/plain per testo semplice o application/xml per XML
header('Content-type: application/json; charset=utf8');

L’altra cosa di cui tenere conto è la codifica dei sorgenti PHP: anche quella dev’essere UTF8, per fare in modo che le stringhe hard-coded servite siano codificate con il giusto charset!
Se usate blocco note di Windows (oltre a cambiare editor), dovrete risalvarvi ogni file sorgente selezionando la codifica UTF8 (File > Salva con nome… > Codifica: UTF8). Se invece come me usate Notepad++, la cosa è estremamente più facile: andate in Configurazione > Preferenze…, selezionate la tab Nuovo documento/Directory predefinita e nel riquadro Codifica scegliete UTF-8 senza BOM (BOM: Byte Mark Order, serve a indicare al decoder l’endianness dei byte della stringa, il che crea problemi in PHP) e spuntate la casella Applica all’apertura di file ANSI. In questo modo aprendo un file sorgente in ANSI (che dovrebbe essere la codifica predefinita su Notepad++, quindi circa ogni file che avete creato) lo convertirete automaticamente in UTF8, e basterà aprire tutti i vostri sorgenti per convertirli tutti alla giusta codifica. Ottimo!

Passiamo alle gatte da pelare: MySQL.
Anche se gestire le codifiche è (per fortuna!) molto facile, il difficile è convertire un database che avete già creato con un altro charset in UTF8.
Il problema è che dovrete andare a cambiarvi la collation di ogni campo che la usa, in ogni tabella che avete creato.
Il modo più veloce per farlo è creare un dump in SQL del proprio database (su phpMyAdmin basta selezionare il database, cliccare sulla tab Esporta e subito su Esegui), aprirlo con un editor avanzato (tipo Notepad++) e rimpiazzare tutte le dichiarazioni della collation in latin1_swedish_ci (o altre, se ne avevate impostate di nuove) con una dichiarazione per utf8_unicode_ci. La cosa è molto rapida se usate le comode funzioni di replace dell’editor.
Dopodichè, svuotate il database da tutte le tabelle, settate charset e collation del database a UTF8 (eseguendo la query ALTER DATABASE nome_database DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci), e importate il dump modificato (su phpMyAdmin, tab Importa, seleziona il file di dump e Esegui).
Se tutto va bene dovreste avere la giusta codifica dei dati nel database.

Ultime due cose: impostare la codifica anche nei file di configurazione di MySQL e PHP.
Per il primo, aprite il file di configurazione (my.conf, my.ini o simili) e nella sezione [mysqld] aggiungete
collation-server=utf8_unicode_ci
character-set-server=utf8

mentre in quella [client] va aggiunto
character-set-server=utf8
In quanto a PHP, trovate sempre il file di configurazione (solitamente php.ini) e nella sezione principale [PHP] impostate default_charset = "utf-8".

Una volta cambiati entrambi i file di configurazione, riavviate il server web e godetevi la possibilità di scrivere in cirillico!

Utili

PHP e AJAX: come gestire le lettere accentate

Sto sviluppando un sito (un gran bel sito u.u) nel quale faccio una richiesta asincrona tramite AJAX a uno script PHP, il quale mi restituisce del codice HTML.
Ma se devo visualizzare delle lettere accentate… viene un gran guazzabuglio di strane à e simboli esoterici del genere che sembra che stia scrivendo in lappone.

A cosa è dovuto questo problema?
Alla codifica dei caratteri, o meglio al charset impostato per la pagina.
Il charset è l’insieme dei caratteri che scelgo di far visualizzare al browser. In base a questa informazione, il browser codifica la richiesta e trasforma quindi i byte nei caratteri corrispondenti.
Il charset più comune è l’ASCII, che prevede 128 caratteri, tra numeri, lettere, simboli e cartteri di utilità (tipo il carattere di new line). Tuttavia non rappresenta tutti i caratteri che esistono, considerando le centinaia di lettere diverse che ci sono in lingue orientali o anche solo in quelle nordiche o germaniche.
Pe rappresentare anche questi caratteri quindi si usa un charset chiamato ISO-8859-1, Latin 1 per gli amici, che prevede 256 simboli e comprende tutte le lettere “strane” degli alfabeti europei, oltre a qualche altro simbolo tipo lo ¥ o l’€.

Per impostare il charset della propria pagina web ci sono due modi:

  • Tramite un tag <meta> nell’head della pagina
  • Impostado l’header della richiesta HTTP

La prima opzione è indicata specialmente per pagine statiche o per script che producono un’intera pagina, mentre la seconda è l’ideale per script che producono un output per una richiesta AJAX, e quindi non possono inserire tag nell’head della pagina.

Quindi, per visualizzare correttamente i caratteri accentati, nella pagina principale che esegue la richiesta AJAX imposteremo il charset a ISO-8859-1 inserendo nell’head:
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
Mentre nella pagina PHP richiamata con AJAX imposteremo il charset nell’altro modo, ossia:
header("Content-type: text/html; charset=ISO-8859-1");
Ricordandoci che è possibile utilizzare la funzione header() solo prima di aver generato qualsiasi output con echo, print o simili, o verrà generato un errore.

Nello script AJAX sarà importante ricordarsi di codificare e decodificare i dati (siano essi GET o POST) inviati allo script con le funzioni encodeURIComponent e decodeURIComponent

data = encodeURIComponent("dati da inviare");
//...
//invio dei dati con AJAX
//...
document.getElementById("contenitore").appendChild(decodeURIComponent(response));

Ma siccome queste funzioni codificano l’output con il charset UTF-8, nello script PHP dovrò usare una funzione strategica: utf8_decode().
Passando i valori inviati in questa funzione, riconverto il tutto in ISO-8859-1, senza quindi vaere strane sorprese.

Dimenticavo:
Nel caso i dati vengano memorizzati in un database, anch’esso dovrà essere impostato con il charset Latin 1 (ISO-8859-1).
In poche parole, pagina, script e database devono avere lo stesso charset, qualunque esso sia.

Spero di essere stato utile, visto che il problema mi ha dato non poche grane…