Database
 sql >> Database >  >> RDS >> Database

Progettazione di database per applicazioni multilingue

Sebbene alcuni sistemi software siano utilizzati da un numero limitato di utenti che parlano la stessa lingua, la maggior parte delle organizzazioni ha bisogno di unificare e centralizzare le proprie applicazioni affinché possano essere utilizzate da persone che parlano lingue diverse in tutto il mondo. I database multilingue presentano un ulteriore livello di difficoltà nella progettazione e nell'implementazione di modelli di dati. In questo articolo, suggeriamo alcuni approcci per affrontare questa sfida.

Quali informazioni dobbiamo archiviare in più lingue?

In superficie, tutte le informazioni sulle stringhe possono sembrare plausibili per la traduzione in più lingue. Tuttavia, questo di solito non è il caso. Informazioni relative al cliente come CompanyName o Address potrebbe essere tradotto, ma potrebbe non essere una buona idea.

Prendi un cliente aziendale nel Regno Unito chiamato "Riverside Trucks" con un ufficio in "123 Upper Castle Road". Non vuoi che un utente di lingua spagnola stampi e invii una lettera a "Camiones Orilla" che si trova in "123 Calle Castillo Superior". Royal Mail (il servizio postale del Regno Unito) non lo troverà! Probabilmente vuoi tradurre solo le colonne contenenti informazioni descrittive, non nomi propri.

Quando si progetta un sistema per gestire le traduzioni, non è sempre noto in anticipo esattamente quali colonne sono traducibili e quali non richiedono traduzioni. La scelta di un approccio flessibile consente di risparmiare molto tempo nella progettazione e nello sviluppo. Dai un'occhiata all'articolo "Come progettare un sistema pronto per la localizzazione" per vedere alcuni esempi.

Quali approcci prendiamo in considerazione?

In questo articolo vengono descritti tre approcci alla progettazione di database multilingue. Iniziamo con quella più semplice che non è così flessibile, quindi passiamo a considerare altre opzioni, spiegando i pro e i contro di ciascuna.

Sia la sintassi che i modelli di database (disponibili nel modellatore di dati basato sul Web di Vertabelo) utilizzati in questo articolo sono per SQL Server. Tuttavia, si adattano facilmente a qualsiasi motore di database.

Approccio 1:creazione di colonne aggiuntive per contenere i contenuti tradotti

Questo è l'approccio più semplice da implementare, anche se non molto flessibile. Consiste nell'aggiungere una colonna per ogni colonna e lingua che dobbiamo usare nel nostro sistema, come mostrato nel seguente diagramma Vertabelo:

Sebbene possa sembrare una soluzione molto semplice, presenta alcuni inconvenienti. Spieghiamo di seguito.

Con:complessità del codice

Questo approccio rende il codice più complesso. Ci richiede di scrivere una query diversa per ogni lingua o di utilizzare un CASE build per recuperare la traduzione per la lingua appropriata in base alla configurazione dell'utente. Vedi ad esempio il codice seguente:

SELECT ProductID,
    CASE @Language WHEN ‘ES’ THEN ProductName_ES
                   WHEN ‘DE’ THEN ProductName_DE
                   WHEN ‘FR’ THEN ProductName_FR
                   ELSE ProductName
    END AS ProductName,
    CASE @Language WHEN ‘ES’ THEN ProductDescription_ES
                   WHEN ‘DE’ THEN ProductDescription_DE
                   WHEN ‘FR’ THEN ProductDescription_FR
                   ELSE ProductDescription
    END AS ProductDescription,
    Price,
    Weight,
    ProductCategoryID
FROM Product
WHERE …

Nota: Nell'esempio, utilizziamo la variabile @Language per mantenere la lingua che vogliamo utilizzare. Potresti prendere in considerazione l'utilizzo di SESSION_CONTEXT() (o Application Context in Oracle) per impostare e leggere la lingua per ciascun utente.

Con:mancanza di flessibilità

Questo approccio manca di flessibilità. Se dobbiamo implementare una nuova lingua, dobbiamo modificare il nostro modello di dati aggiungendo una colonna per la nuova lingua per ogni colonna traducibile nel nostro sistema. Dobbiamo anche creare una nuova query di lingua per ogni tabella (o modificare quella esistente aggiungendo un nuovo CASE WHEN clausola che utilizza la nuova lingua per ogni colonna traducibile).

Con:sfide nella gestione di informazioni sconosciute

Immagina questo:un utente aggiunge un prodotto ma non sa come tradurlo e lascia vuote le colonne tradotte. Gli utenti che parlano queste lingue vedono NULL o informazioni vuote nelle colonne che potrebbero essere richieste.

Approccio 2:isolamento delle colonne traducibili in una tabella separata

Questo approccio raggruppa le colonne di una tabella in colonne traducibili e non traducibili. Le colonne non traducibili rimangono nella tabella originale. Al contrario, quelli traducibili sono in una tabella separata, con una chiave straniera alla tabella originale e un indicatore di lingua. Vedi sotto:

Il diagramma mostra le tabelle originali (senza dati traducibili) in bianco. Le tabelle che contengono le traduzioni sono in azzurro e la tabella principale che contiene le informazioni sulla lingua è in giallo.

Ciò ha enormi vantaggi in termini di flessibilità rispetto all'utilizzo di più colonne come discusso in precedenza. Questo metodo non richiede la modifica del modello di dati quando è necessaria una nuova lingua. Inoltre, la sintassi per interrogare le informazioni è più semplice:

SELECT p.ProductID,
    pt.ProductName,
    pt.ProductDescription,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
LEFT JOIN ProductTranslation pt ON pt.ProductID = p.ProductID
                               AND pt.LanguageID = @Language
WHERE …

Tuttavia, ci sono ancora alcuni svantaggi, come discutiamo di seguito.

Con:sfide quando è necessario tradurre colonne aggiuntive

Se dobbiamo convertire una colonna non traducibile in una traducibile (o viceversa), dobbiamo modificare il nostro modello di dati, spostando la colonna da una tabella all'altra. Questo di solito implica costi maggiori una volta che il sistema è implementato e in uso.

Con:sfide nella gestione di informazioni sconosciute

Come il primo approccio, questo approccio presenta sfide quando si tratta di informazioni sconosciute. Anche in questo caso, se un utente aggiunge un prodotto ma non sa come tradurlo e lascia vuote le colonne tradotte, gli utenti che parlano quelle lingue vedono NULL o informazioni vuote nelle colonne che potrebbero essere richieste. Inoltre, la query richiede un LEFT JOIN nel caso in cui non sia stata ancora creata la traduzione per la lingua dell'utente corrente in modo che i dati non traducibili vengano comunque visualizzati.

Approccio 3:aggiunta di un sottosistema di traduzione

La traduzione può essere considerata come una caratteristica completamente indipendente dal modello di dati che richiede la traduzione. In un sistema ideale, possiamo abilitare o disabilitare la traduzione per qualsiasi colonna senza richiedere modifiche al modello di dati. Sfortunatamente, è più facile a dirsi che a farsi.

Presentiamo un metodo che non ha alcun impatto sul modello di dati esistente ed è completamente flessibile. Sebbene aggiunga complessità al momento dell'interrogazione dei dati, non richiede alcuna modifica aggiuntiva al modello di dati ad eccezione di alcune tabelle. Questa potrebbe essere un'ottima scelta se devi aggiungere la capacità di conservare le traduzioni a un modello di dati esistente.

Diamo un'occhiata al modello e vediamo come funziona:

La prima cosa da notare è che il modello di dati originale non ha alcuna modifica. Inoltre, non esiste una relazione diretta tra quel modello e il sottosistema di traduzione.

Il sottosistema di traduzione include un piccolo dizionario di dati con le tabelle e le colonne che richiedono la traduzione. Questo dizionario dati può essere modificato semplicemente aggiungendo/rimuovendo righe senza alterare il modello dati. Le traduzioni sono archiviate in una tabella separata, con ciascun valore identificato dalle seguenti 3 colonne:

  • ColumnID :identifica in modo univoco la colonna (e la tabella) che stiamo traducendo.
  • KeyID :Memorizza l'ID (chiave primaria) della riga specifica che stiamo traducendo.
  • LanguageID :Identifica la lingua della traduzione.

Questo design consente di inserire e memorizzare i dati nelle tabelle originali, aggiungendo traduzioni solo se e quando richiesto. Le informazioni tradotte vengono utilizzate durante il recupero dei dati, mantenendo inalterati i dati originali (nella lingua originale).

I dati possono essere interrogati utilizzando una sintassi più complessa rispetto agli esempi precedenti. Richiede un ulteriore JOIN per ogni colonna traducibile come mostrato di seguito:

SELECT p.ProductID,
    ISNULL(t1.TranslationValue, p.ProductName) AS ProductName,
    ISNULL(t2.TranslationValue, p.ProductDescription) AS ProductDescription,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
LEFT JOIN Translation t1 ON t1.ColumnID = <>
                       AND t1.Key = p.ProductID
                       AND t1.LanguageID = @Language
LEFT JOIN Translation t2 ON t2.ColumnID = <>
                       AND t2.Key = p.ProductID
                       AND t2.LanguageID = @Language
WHERE …;

Nota:<<ProductName_ColumnID>> ” e “<<ProductDescription_ColumnID>> ” deve essere sostituito con gli ID delle colonne da tradurre come memorizzate in ColumnInformation tavolo. Prendi in considerazione la generazione di viste di traduzione per ogni tabella che richiede la traduzione per nascondere la complessità delle JOIN per gli utenti finali. Puoi persino automatizzare questo passaggio con uno script che genera ciascuna vista. Questo script può interrogare il dizionario dei dati del database per selezionare le tabelle e le colonne e aggiungere la logica di traduzione per le colonne che esistono in ColumnInformation tabella.

Suggerimento aggiuntivo n. 1

Puoi anche semplificare la sintassi. Sostituisci ogni JOIN con una chiamata a una funzione che gestisce (e nasconde) l'aspetto della traduzione, come mostrato di seguito:

SELECT p.ProductID,
    ISNULL(fn_translate(‘Product’,‘ProductName’,ProductID), p.ProductName)
         AS ProductName,
    ISNULL(fn_translate(‘Product’,‘ProductDescription’,ProductID),
         p.ProductDescription) AS ProductName,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
WHERE …;

La funzione può leggere la lingua desiderata dal contesto o aggiungerla come parametro aggiuntivo. In questo esempio, la funzione utilizza i nomi di tabella e colonna forniti come parametri più la chiave di riga (fornita anche come parametro) per cercare e restituire la traduzione desiderata.

La chiamata di una funzione implica un ulteriore impatto sulle prestazioni a causa del cambio di contesto tra SQL e linguaggio procedurale. Tuttavia, potrebbe essere una soluzione più semplice per database o tabelle in cui la quantità di dati tradotti lo consente.

Entrambi gli esempi, quello con JOIN e quello con una funzione, utilizzano la funzione ISNULL() di SQL Server. Pertanto, quando la traduzione nella lingua desiderata non esiste, viene comunque visualizzato il valore originale memorizzato nelle colonne ProductName e ProductDescription invece di spazi vuoti o NULL.

Considerazioni generali

Il terzo approccio è solitamente il migliore per l'implementazione di progetti più grandi. Consente flessibilità sia in fase di progettazione che una volta che il sistema è in uso. Tuttavia, ci sono considerazioni specifiche che possono rendere utili gli altri approcci. Indipendentemente dalla tua scelta, considera quanto segue per risparmiare tempo sia in fase di progettazione che di sviluppo/implementazione.

Aggiungi uno strato di astrazione

Come accennato in precedenza, prendi in considerazione la creazione di viste che si occupino della logica di traduzione, ad esempio selezionando una colonna tra più colonne di traduzione o unendosi a righe specifiche. Ciò mantiene i dettagli di implementazione specifici nascosti ai programmatori. Usano semplicemente queste viste invece di dover costruire frasi SQL complesse ogni volta che devono accedere a una tabella con informazioni traducibili.

Utilizza il contesto per filtrare i dati

Come menzionato nel primo esempio, considera l'utilizzo delle funzioni di contesto disponibili nella maggior parte dei motori di database. Usali per memorizzare le informazioni sulla lingua dell'utente una volta effettuato l'accesso al sistema, quindi filtra automaticamente i risultati nelle viste che si occupano della traduzione.

Automatizzare

I sistemi moderni possono avere centinaia e persino migliaia di tabelle. Prenditi del tempo per automatizzare la generazione delle viste di traduzione invece di scrivere le query una per una. Potrebbe volerci del tempo per arrivare a uno script che funzioni, ma puoi sempre creare nuove viste o ricreare quelle esistenti in meno di un secondo!