Mens nogle softwaresystemer bruges af et begrænset antal brugere, der taler det samme sprog, er de fleste organisationer nødt til at forene og centralisere deres applikationer, så de kan bruges af folk, der taler forskellige sprog over hele verden. Flersprogede databaser præsenterer en ekstra sværhedsgrad ved design og implementering af datamodeller. I denne artikel foreslår vi nogle tilgange til at håndtere denne udfordring.
Hvilke oplysninger har vi brug for at gemme på flere sprog?
På overfladen kan al strenginformation virke plausibel til oversættelse til flere sprog. Dette er dog normalt ikke tilfældet. Kunderelaterede oplysninger såsom CompanyName
eller Address
kan blive oversat, men det er måske ikke en god idé.
Tag en virksomhedskunde i Storbritannien ved navn "Riverside Trucks" med et kontor på "123 Upper Castle Road." Du ønsker ikke, at en spansktalende bruger skal udskrive og sende et brev til "Camiones Orilla", som ligger på "123 Calle Castillo Superior." Royal Mail (det britiske postvæsen) vil ikke finde det! Du vil sandsynligvis kun oversætte de kolonner, der indeholder beskrivende information, ikke egennavne.
Når man designer et system til at håndtere oversættelser, ved man ikke altid på forhånd præcis, hvilke kolonner der kan oversættes, og hvilke der ikke kræver oversættelse. At vælge en fleksibel tilgang sparer en masse tid i design og udvikling. Tag et kig på artiklen "Sådan designer du et lokaliseringsklar system" for at se nogle eksempler.
Hvilke tilgange overvejer vi?
I denne artikel beskriver vi tre tilgange til flersproget databasedesign. Vi starter med den enkleste, der ikke er så fleksibel, og går derefter videre til at overveje andre muligheder og forklarer fordele og ulemper for hver.
Både syntaksen og databasemodellerne (tilgængelige på Vertabelo webbaseret datamodeler), der bruges i denne artikel, er til SQL Server. De kan dog nemt tilpasses til enhver databasemotor.
Fremgangsmåde 1:Oprettelse af yderligere kolonner til at indeholde oversat indhold
Dette er den enkleste fremgangsmåde at implementere, omend ikke en meget fleksibel. Det består af at tilføje en kolonne for hver kolonne og sprog, vi skal bruge i vores system, som vist i følgende Vertabelo-diagram:
Selvom dette kan virke som en meget simpel løsning, har det nogle ulemper. Vi forklarer nedenfor.
Con:Kodekompleksitet
Denne tilgang gør koden mere kompleks. Det kræver, at vi enten skriver en anden forespørgsel for hvert sprog eller bruger en CASE
konstruktion for at hente oversættelsen til det relevante sprog baseret på brugerkonfigurationen. Se f.eks. følgende kode:
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, ProductCategoryIDFROM ProductWHERE …
Bemærk: I eksemplet bruger vi variablen @Language til at beholde det sprog, vi ønsker at bruge. Du kan overveje at bruge SESSION_CONTEXT() (eller Application Context i Oracle) til at indstille og læse sproget for hver bruger.
Con:Mangel på fleksibilitet
Denne tilgang mangler fleksibilitet. Hvis vi skal implementere et nyt sprog, skal vi ændre vores datamodel ved at tilføje en kolonne for det nye sprog for hver oversættelig kolonne i vores system. Vi skal også oprette en ny sprogforespørgsel for hver tabel (eller redigere den eksisterende ved at tilføje en ny CASE WHEN
klausul, der bruger det nye sprog for hver oversættelig kolonne).
Con:Udfordringer ved håndtering af ukendte oplysninger
Forestil dig dette:en bruger tilføjer et produkt, men ved ikke, hvordan det skal oversættes og lader de oversatte kolonner stå tomme. Brugere, der taler disse sprog, kan se NULL
eller tomme oplysninger i de kolonner, der kan være nødvendige.
Fremgangsmåde 2:Isolering af oversættelige kolonner i en separat tabel
Denne tilgang grupperer kolonnerne i en tabel i oversættelige og ikke-oversættelige kolonner. Ikke-oversættelige kolonner forbliver i den originale tabel. Derimod er de oversættelige i en separat tabel med en fremmednøgle til den originale tabel og en sprogindikator. Se nedenfor:
Diagrammet viser de originale tabeller (uden data, der kan oversættes) i hvidt. Tabellerne med oversættelserne er i lyseblåt, og mastertabellen med sprogoplysningerne er i gult.
Dette har enorme fleksibilitetsfordele sammenlignet med at bruge flere kolonner som diskuteret tidligere. Denne metode kræver ikke ændring af datamodellen, når der er behov for et nyt sprog. Syntaksen til at forespørge oplysningerne er også enklere:
VÆLG p.ProductID, pt.ProductName, pt.ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product PLEFT JOIN ProductTranslation pt ON pt.ProductID =p.ProductID AND pt.LanguageID =@LanguageWHERE …Der er dog stadig nogle ulemper, som vi diskuterer nedenfor.
Con:Udfordringer, når yderligere kolonner skal oversættes
Hvis vi skal konvertere en ikke-oversættelig kolonne til en oversættelsesbar (eller omvendt), skal vi ændre vores datamodel og flytte kolonnen fra den ene tabel til den anden. Dette medfører normalt større omkostninger, når først systemet er implementeret og i brug.
Con:Udfordringer ved håndtering af ukendte oplysninger
Ligesom den første tilgang har denne tilgang udfordringer, når man håndterer ukendt information. Igen, hvis en bruger tilføjer et produkt, men ikke ved, hvordan det skal oversættes og lader de oversatte kolonner være tomme, vil brugere, der taler disse sprog, se
NULL
eller tomme oplysninger i kolonner, der kan være nødvendige. Forespørgslen kræver også enLEFT JOIN
i tilfælde af, at oversættelsen til den aktuelle brugers sprog endnu ikke er blevet oprettet, så de ikke-oversættelige data stadig vises.Fremgangsmåde 3:Tilføjelse af et oversættelsesundersystem
Oversættelse kan betragtes som en funktion, der er fuldstændig uafhængig af den datamodel, der kræver oversættelse. I et ideelt system kan vi aktivere eller deaktivere oversættelse for enhver kolonne uden at kræve ændring af datamodellen. Det er desværre lettere sagt end gjort.
Vi præsenterer en metode, der ikke har indflydelse på den eksisterende datamodel og er fuldstændig fleksibel. Selvom det tilføjer kompleksitet på tidspunktet for forespørgsel til dataene, kræver det ingen yderligere ændring af datamodellen bortset fra nogle få tabeller. Dette kan være et godt valg, hvis du har brug for at tilføje muligheden for at holde oversættelser til en eksisterende datamodel.
Lad os tage et kig på modellen og se, hvordan den virker:
Den første ting at bemærke er, at den originale datamodel ikke har nogen ændringer overhovedet. Der er heller ikke noget direkte forhold mellem denne model og oversættelsesundersystemet.
Oversættelsesundersystemet inkluderer en lille dataordbog med tabeller og kolonner, der kræver oversættelse. Denne dataordbog kan ændres ved blot at tilføje/fjerne rækker uden at ændre datamodellen. Oversættelser gemmes i en separat tabel, hvor hver værdi identificeres af følgende 3 kolonner:
ColumnID
:Identificerer entydigt kolonnen (og tabellen), vi oversætter.KeyID
:Gemmer ID'et (primær nøgle) for den specifikke række, vi oversætter.LanguageID
:Identificerer oversættelsens sprog.
Dette design gør det muligt at indtaste og lagre data i de originale tabeller og tilføjer kun oversættelser, hvis og når det er nødvendigt. Den oversatte information bruges til at hente data, så de originale data (på originalsproget) holdes uberørte.
Data kan forespørges ved hjælp af en mere kompleks syntaks end eksemplerne ovenfor. Det kræver en ekstra JOIN
for hver oversættelig kolonne som vist nedenfor:
SELECT p.ProductID, ISNULL(t1.TranslationValue, p.ProductName) AS ProductName, ISNULL(t2.TranslationValue, p.ProductDescription) AS ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product vLEFT JOIN Oversættelse t1 ON t1.ColumnID =<> AND t1.Key =p.ProductID AND t1.LanguageID =@LanguageLEFT JOIN Oversættelse t2 ON t2.ColumnID =< > OG t2.Key.Nøgle-ID =p.LanguageLEFT JOIN =@LanguageWHERE …;
Bemærk: "<<ProductName_ColumnID>>
” og “<<ProductDescription_ColumnID>>
” skal erstattes med ID'erne for de kolonner, der skal oversættes som gemt i ColumnInformation
bord. Overvej at generere oversættelsesvisninger for hver tabel, der kræver oversættelse for at skjule kompleksiteten af JOIN'erne for slutbrugerne. Du kan endda automatisere dette trin med et script, der genererer hver visning. Dette script kan forespørge databasens dataordbog for at vælge tabellerne og kolonnerne og tilføje oversættelseslogikken for de kolonner, der findes i ColumnInformation
tabel.
Ekstra tip #1
Du kan også forenkle syntaksen. Erstat hver JOIN med et kald til en funktion, der håndterer (og skjuler) oversættelsesaspektet, som vist nedenfor:
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.ProductCategoryIDFROM Product pWHERE …;
Funktionen kan læse det ønskede sprog fra konteksten, eller du kan tilføje det som en ekstra parameter. I dette eksempel bruger funktionen tabel- og kolonnenavnene angivet som parametre plus rækketasten (også angivet som en parameter) til at søge efter og returnere den ønskede oversættelse.
Kaldning af en funktion indebærer en yderligere indvirkning på ydeevnen på grund af kontekstskifte mellem SQL og proceduresprog. Det kan dog være en enklere løsning til databaser eller tabeller, hvor mængden af data, der bliver oversat, tillader det.
Begge eksempler – det ene med JOIN og det med en funktion – bruger ISNULL() SQL Server-funktionen. Så når oversættelsen til det ønskede sprog ikke eksisterer, viser den stadig den oprindelige værdi, der er gemt i kolonnerne Produktnavn og Produktbeskrivelse i stedet for tomme felter eller NULL.
Generelle overvejelser
Den tredje tilgang er normalt den bedste til at implementere større designs. Det giver mulighed for fleksibilitet både ved design og når systemet er i brug. Der er dog specifikke overvejelser, der kan gøre de andre tilgange nyttige. Uanset dit valg, så overvej følgende for at spare tid både ved design og ved udvikling/implementering.
Tilføj et lag af abstraktion
Som nævnt tidligere kan du overveje at oprette visninger, der tager sig af oversættelseslogikken, f.eks. at vælge en kolonne blandt flere oversættelseskolonner eller at slutte sig til bestemte rækker. Dette holder specifikke implementeringsdetaljer skjult for programmører. De bruger simpelthen disse visninger i stedet for at skulle konstruere komplekse SQL-sætninger, hver gang de skal have adgang til en tabel med oversættelig information.
Brug kontekst til at filtrere data
Som nævnt i det første eksempel kan du overveje at bruge kontekstfunktioner, der er tilgængelige i de fleste databasemotorer. Brug dem til at gemme oplysninger om brugersprog, når du er logget ind på systemet, og filtrer derefter resultaterne automatisk i de visninger, der tager sig af oversættelsen.
Automatiser
Moderne systemer kan have hundredvis og endda tusindvis af borde. Tag dig tid til at automatisere genereringen af oversættelsesvisningerne i stedet for at skrive forespørgslerne én efter én. Det kan tage dig noget tid at nå frem til et script, der virker, men så kan du altid oprette nye visninger eller genskabe eksisterende på mindre end et sekund!