Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Come progettare siti multilingua in PHP con database

Progettare gli script PHP e la struttura del database nella costruzione di siti internazionali o multiligua: esempi pratici e situazioni d'uso
Progettare gli script PHP e la struttura del database nella costruzione di siti internazionali o multiligua: esempi pratici e situazioni d'uso
Link copiato negli appunti

Quando si decide di internazionalizzare le proprie applicazioni bisogna studiare sistemi per permettere di gestire tutte le risorse con le quali operiamo in modo che siano dipendenti dalla lingua scelta. Precedentemente abbiamo visto come utilizzare gettext e le librerie di conversione tra charset (Iconv e Mbstring) per gestire l'internazionalizzazione delle stringhe all'interno dei propri script o provenienti dall'esterno, oggi invece tratteremo in modo più specifico qualche soluzione pratica per internazionalizzare le informazioni presenti all'interno di un database.

Purtroppo l'internazionalizzazione dei campi non è gestita nativamente dalla maggior parte dei database, e quindi bisogna organizzare le proprie strutture in modo che risulti possibile localizzare i nostri dati senza complicare eccessivamente la struttura delle relazioni. I sistemi utilizzabili sono molti e spesso bisogna sviluppare soluzioni specifiche per le circostanze in cui ci si trova a lavorare, ma possiamo adattare degli accorgimenti standard che ci verranno incontro in molte di queste situazioni.

Ovviamente il database si occuperà di fornire una struttura capace di essere interrogata per ottenere dati in una determinata lingua, ma tutte le eventuali operazioni di conversione dei caratteri o formattazione del testo saranno demandate a PHP.

Alcune informazioni importanti

Prima di addentrarci praticamente nel codice necessario a strutturare correttamente la nostra applicazioni, è utile discutere di alcune informazioni importanti relative alla conversione dei valori, alla loro rappresentazione e all'eventuale perdita di informazioni legata al passaggio di internazionalizzazione.

L'obiettivo iniziale quando si decide di strutturare una base di dati è quello di avere dati uniformi, in modo che sia possibile mantenere meglio il codice e facilitare le interrogazioni; per questo motivo, all'interno di un database, è fondamentale mantenere il più possibile indipendente la rappresentazione dei valori dalle informazioni relative alla localizzazione, salvo eccezioni in cui i dati uniformati perderebbero informazioni rispetto a quelli iniziali. Possiamo evidenziare alcuni esempi di situazioni in cui utilizzare questa politica.

La rappresentazione testuale di numeri decimali a virgola fissa viene effettuata differentemente in base alla cultura ed alla lingua di un utente. Per esempio gli inglesi utilizzano la virgola per separare le migliaia ed il punto per separare la parte intera da quella decimale; al contrario noi italiani utilizziamo il punto per le migliaia e la virgola per i decimali. In questa situazione è necessario uniformare i valori decimali al formato accettato dal database (solitamente quello inglese) e successivamente apportare le trasformazioni per riadattarlo alla lingua del visitatore.

Le valute sono un'altra situazioni in cui è fondamentale procedere con le dovute precauzioni: basta perdere per strada poche informazioni per rischiare di compromettere l'integrità dei dati e la loro credibilità. Nel caso in cui ci si trovi a lavorare su applicazioni che devono fornire agli utenti prezzi con valute differenti, è assolutamente necessario salvare i dati nella valuta utilizzata dall'utente, affiancandovi informazioni sulla localizzazione. In questo modo si evitano problemi legati ad incomprensione delle valute. Nel caso si siano calcolati i valori partendo da una valuta di base a cui è stato applicato un indice di conversione, sarebbe opportuno salvare anche questo indice come identificativo della conversione stessa. La formattazione delle valute può essere successivamente effettuata in base alla valuta stessa.

Le informazioni relative a date e tempo sono solitamente convertite in UTC a cui viene affiancata l'informazione relativa alla time zone. La conversione al formato di visualizzazione corretto viene fatto successivamente senza perdita di informazioni.

Infine abbiamo le stringhe: il valore di una stringa è determinato dalla sequenza di caratteri in essa contenuti, non dalla sua codifica; per questo motivo salvare stringhe in differenti codifiche all'interno del database può portare ad una serie di problemi successivamente. Per questo motivo è buona norma scegliere una codifica unica (UTF-8 potrebbe essere un'ottima scelta dato il supporto per un gran numero di set di caratteri) e convertire i valori a questa prima di salvarli sul database, per poi riportarli alla codifica necessaria prima di restituirli all'utente.

Ovviamente tutte queste situazioni che abbiamo analizzato si riferiscono a situazioni tipiche; potrebbe risultare che, soprattutto per motivi commerciali o funzionali, risulti necessario procedere in modo differente.

Strutturare le tabelle

Dopo la premessa fatta precedentemente possiamo iniziare ad analizzare il sistema con cui salvare i dati nel nostro database. Ho visto adottare soluzioni differenti, tuttavia secondo me è fondamentale trovare una strada che ci permetta di poter lavorare sul numero di lingue e sul numero di campi che possono essere internazionalizzati senza dover lavorare eccessivamente sulla struttura del database.

Come esempio prenderemo una semplice situazione in cui è necessario visualizzare una lista di prodotti. La creazione della tabella è fondamentale per poter procedere correttamente; buona pratica è quella di creare due tabelle per rappresentare un nostro prodotto: in una tabella salveremo le informazioni che non richiedono l'internazionalizzazione, mentre nella seconda salveremo tutti quei dati che possono essere internazionalizzati con associata la lingua in cui sono scritti:

CREATE TABLE products (
        id INT UNSIGNED AUTO_INCREMENT NOT NULL,
        last_update INT UNSIGNED NOT NULL,

        PRIMARY KEY(id)
);

CREATE TABLE products_data (
        product_id INT UNSIGNED NOT NULL,
        language_id INT UNSIGNED NOT NULL,
        description TEXT NOT NULL,
        name VARCHAR(128) NOT NULL,
        
        INDEX(product_id, language_id)
);

CREATE TABLE languages (
        id INT UNSIGNED AUTO_INCREMENT NOT NULL,
        language CHAR(2) NOT NULL,
        country CHAR(2) NOT NULL,
        
        INDEX (language, country),
        PRIMARY KEY(id)
);

Come potete notare, nel semplice esempio precedente, è evidente come i dati siano stati separati e come sia semplice poter recuperare le informazioni relative ad una lingua specifica.

Vediamo un semplice script PHP per recuperare i dati che ci interessano:

<?php
  
/*  Nell'esempio seguente utilizzo l'estensione MySQLi
*  anche se le operazioni non variano con altri tipi di
*  database
*/
  
/*  Recuperiamo la lingua da utilizzare nella pagina.
*  La lingua è formata da un array: il primo valore rappresenta
*  la lingua, il secondo è relativo ad informazioni specifiche per
*  una tipologia specifica legata alla lingua (ex: en_uk, en_us)
*  Concettualmente il secondo valore potrebbe non esistere e dovrebbe
*  essere fornito di default in quel caso.
*/
function get_language($default=array('it', 'it'))
  {
    if(!isset($_POST['l']))
    {
      return $default;
    }
    
    $lang = $_POST['l'];
    if(!eregi('^[a-z]{2}(_[a-z]{2})?$', $lang))
    {
      return $default;
    }
    
    if(strpos('_', $lang) > -1)
    {
      return explode('_', $lang);
    }else
    {
      return ($lang, /* valore di default */ 'it');
    }
  }
  
 // Parametri di connessione
  $db = array(
    'host'  =>  'localhost',
    'user'  =>  'gabriele,
    'pass'  =>  'farina',
    'name'  =>  'html_it'
  );
  
 // Recuperiamo la lingua da qualche sorgente
  $lang = get_language();
  
  $mysqli = new mysqli(  $db['host'],
              $db['user'],
              $db['pass'],
              $db['name']);
  
  if (mysqli_connect_errno())
  {
     echo "Impossibile connettersi al server MySQL. Errorcode: %sn", mysqli_connect_error();
     exit;
  }
  
  $result = $mysqli->query(sprintf("
    SELECT
      P.id AS id,
      P.last_update AS last_update,
      PD.description AS description,
      PD.name AS name
    FROM
      products P,
      products_data PD,
      languages L
    WHERE
      P.id = PD.product_id AND
      PD.language_id = L.id AND
      L.language = %s AND
      L.country = %s
    ", $lang[0], $lang[1]));
  
  if ($result)
  {
    echo '<table><tr><th colspan="4">Products</th></tr>';
      while( $row = $mysqli->fetch_assoc($result) )
    {
        printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>",
        $row['id'],
        date("d/m/Y", $row['last_update']),
        $row['name'],
        $row['description']
      );
      }
    echo '</table>';
      $result->close();
  }
  
  $mysqli->close();
  
?>

L'esempio precedente accetta in query string le informazioni sulla lingua con cui visualizzare i dati, e successivamente recupera i prodotti con le informazioni inerenti nella lingua specificata. Questo approccio permette di aggiungere nuove lingue senza cambiare il nostro codice; ovviamente devono essere aggiunti sistemi per codificare e decodificare le stringhe e, se necessario, qualche modo per assicurarsi che un prodotto sia visualizzato nella lingua standard in caso non sia presente la traduzione nella lingua specificata.

Conclusioni

Abbiamo discusso di come strutturare le tabelle affinché la nostra applicazione si comporti correttamente durante il processo di internazionalizzazione. L'esempio che abbiamo visto non è sicuramente completo ed avanzato, ma la parte teorica introduttiva dovrebbe essere stata abbastanza completa per permettervi di operare correttamente anche in situazioni differenti come la gestione delle valute.

Ti consigliamo anche