sql >> Databasteknik >  >> RDS >> Database

Databasdesign för flerspråkiga applikationer

Medan vissa mjukvarusystem används av ett begränsat antal användare som talar samma språk, behöver de flesta organisationer förena och centralisera sina applikationer för att användas av människor som talar olika språk runt om i världen. Flerspråkiga databaser ger en extra svårighetsgrad när det gäller att designa och implementera datamodeller. I den här artikeln föreslår vi några tillvägagångssätt för att hantera denna utmaning.

Vilken information behöver vi lagra på flera språk?

På ytan kan all stränginformation verka rimlig för översättning till flera språk. Detta är dock vanligtvis inte fallet. Kundrelaterad information som CompanyName eller Address kan översättas, men det kanske inte är en bra idé.

Ta en företagskund i Storbritannien som heter "Riverside Trucks" med kontor på "123 Upper Castle Road." Du vill inte att en spansktalande användare ska skriva ut och skicka ett brev till "Camiones Orilla" som ligger på "123 Calle Castillo Superior." Royal Mail (Storbritanniens posttjänst) kommer inte att hitta det! Du vill förmodligen bara översätta de kolumner som innehåller beskrivande information, inte egennamn.

När man designar ett system för att hantera översättningar är det inte alltid känt i förväg exakt vilka kolumner som är översättningsbara och vilka som inte kräver översättningar. Att välja ett flexibelt tillvägagångssätt sparar mycket tid i design och utveckling. Ta en titt på artikeln "Hur man designar ett lokaliseringsfärdigt system" för att se några exempel.

Vilka tillvägagångssätt överväger vi?

I den här artikeln beskriver vi tre tillvägagångssätt för flerspråkig databasdesign. Vi börjar med det enklaste som inte är lika flexibelt och går sedan över till att överväga andra alternativ och förklarar för- och nackdelarna för varje.

Både syntax- och databasmodellerna (tillgängliga på Vertabelo webbaserade datamodellerare) som används i den här artikeln är för SQL Server. Men de är lätta att anpassa till vilken databasmotor som helst.

Tillvägagångssätt 1:Skapa ytterligare kolumner för översatt innehåll

Detta är det enklaste sättet att implementera, om än inte särskilt flexibelt. Det består av att lägga till en kolumn för varje kolumn och språk vi behöver använda i vårt system, som visas i följande Vertabelo-diagram:

Även om detta kan verka som en väldigt enkel lösning, har den några nackdelar. Vi förklarar nedan.

Kon:kodkomplexitet

Detta tillvägagångssätt gör koden mer komplex. Det kräver att vi antingen skriver en annan fråga för varje språk eller använder en CASE konstruktion för att hämta översättningen för lämpligt språk baserat på användarkonfigurationen. Se till exempel följande kod:

VÄLJ Produkt-ID, CASE @Language NÄR 'ES' DÅ PRODUKTNAMN_ES NÄR 'DE' DÅ Produktnamn_DE NÄR 'FR' DÅ Produktnamn_FR ANNAT Produktnamn SLUTAR SOM Produktnamn, CASE @Språk NÄR 'ES' DÅ ProductDescription_ES NÄR 'DE' DÅ Produktbeskrivning_DE ' FR' THEN ProductDescription_FR ELSE ProductDescription END AS ProductDescription, Price, Weight, ProductCategoryIDFROM ProductWHERE …

Obs! I exemplet använder vi variabeln @Language för att behålla språket vi vill använda. Du kan överväga att använda SESSION_CONTEXT() (eller Application Context i Oracle) för att ställa in och läsa språket för varje användare.

Nackdel:Brist på flexibilitet

Detta tillvägagångssätt saknar flexibilitet. Om vi ​​behöver implementera ett nytt språk måste vi modifiera vår datamodell genom att lägga till en kolumn för det nya språket för varje översättbar kolumn i vårt system. Vi måste också skapa en ny språkfråga för varje tabell (eller redigera den befintliga genom att lägga till en ny CASE WHEN sats som använder det nya språket för varje översättbar kolumn).

Con:Utmaningar i att hantera okänd information

Föreställ dig det här:en användare lägger till en produkt men vet inte hur den ska översättas och lämnar de översatta kolumnerna tomma. Användare som talar dessa språk ser NULL eller tom information i kolumnerna som kan krävas.

Tillvägagångssätt 2:Isolera översättbara kolumner i en separat tabell

Detta tillvägagångssätt grupperar kolumnerna i en tabell i översättbara och icke-översättbara kolumner. Icke-översättbara kolumner stannar i den ursprungliga tabellen. Däremot finns de översättbara i en separat tabell, med en främmande nyckel till originaltabellen och en språkindikator. Se nedan:

Diagrammet visar originaltabellerna (utan översättningsbara data) i vitt. Tabellerna som innehåller översättningarna är i ljusblått, och huvudtabellen som innehåller språkinformationen är i gult.

Detta har enorma flexibilitetsfördelar jämfört med att använda flera kolumner som diskuterats tidigare. Denna metod kräver inte att datamodellen ändras när ett nytt språk behövs. Dessutom är syntaxen för att fråga informationen enklare:

VÄLJ p.ProductID, pt.ProductName, pt.ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product VÄLJ JOIN ProductTranslation pt ON pt.ProductID =p.ProductID AND pt.LanguageID =@LanguageWHERE … 

Det finns dock fortfarande några nackdelar, som vi diskuterar nedan.

Kon:Utmaningar när ytterligare kolumner behöver översättas

Om vi ​​behöver konvertera en icke-översättbar kolumn till en översättningsbar (eller vice versa), måste vi modifiera vår datamodell och flytta kolumnen från en tabell till en annan. Detta innebär vanligtvis större kostnader när systemet väl är implementerat och i bruk.

Con:Utmaningar i att hantera okänd information

Liksom det första tillvägagångssättet har detta tillvägagångssätt utmaningar när man hanterar okänd information. Återigen, om en användare lägger till en produkt men inte vet hur den ska översättas och lämnar de översatta kolumnerna tomma, ser användare som talar dessa språk NULL eller tom information i kolumner som kan krävas. Dessutom kräver frågan en LEFT JOIN om översättningen för den aktuella användarens språk ännu inte har skapats så att den icke-översättbara informationen fortfarande visas.

Tillvägagångssätt 3:Lägga till ett översättningsundersystem

Översättning kan betraktas som en funktion som är helt oberoende av den datamodell som kräver översättning. I ett idealiskt system kan vi aktivera eller inaktivera översättning för valfri kolumn utan att behöva modifiera datamodellen. Tyvärr är detta lättare sagt än gjort.

Vi presenterar en metod som inte har någon inverkan på den befintliga datamodellen och som är helt flexibel. Även om det ökar komplexiteten vid tidpunkten för sökning av data, kräver det ingen ytterligare förändring av datamodellen förutom några tabeller. Detta kan vara ett utmärkt val om du behöver lägga till möjligheten att hålla översättningar till en befintlig datamodell.

Låt oss ta en titt på modellen och se hur den fungerar:

Det första att lägga märke till är att den ursprungliga datamodellen inte har några förändringar alls. Det finns heller inget direkt samband mellan den modellen och översättningsundersystemet.

Översättningsundersystemet inkluderar en liten dataordbok med tabeller och kolumner som kräver översättning. Denna dataordbok kan modifieras genom att bara lägga till/ta bort rader utan att ändra datamodellen. Översättningar lagras i en separat tabell, där varje värde identifieras av följande tre kolumner:

  • ColumnID :Identifierar unikt kolumnen (och tabellen) vi översätter.
  • KeyID :Lagrar ID (primärnyckel) för den specifika raden vi översätter.
  • LanguageID :Identifierar språket för översättningen.

Denna design gör att data kan matas in och lagras i de ursprungliga tabellerna, och endast lägga till översättningar om och när det behövs. Den översatta informationen används när data hämtas, och originaldata (på originalspråket) hålls orörda.

Data kan efterfrågas med en mer komplex syntax än exemplen ovan. Det kräver ytterligare en JOIN för varje översättbar kolumn enligt nedan:

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 Translation t1 ON t1.ColumnID =<> AND t1.Key =p.ProductID AND t1.LanguageID =@LanguageLEFT JOIN Översättning t2 ON t2.ColumnID =<> OCH t2.Key.LanguAND ID =p.Product =@LanguageWHERE …;

Obs! "<<ProductName_ColumnID>> ” och “<<ProductDescription_ColumnID>> ” måste ersättas med ID:n för de kolumner som ska översättas som lagrade i ColumnInformation tabell. Överväg att generera översättningsvyer för varje tabell som kräver översättning för att dölja komplexiteten i JOINs för slutanvändarna. Du kan till och med automatisera detta steg med ett skript som genererar varje vy. Det här skriptet kan fråga databasens dataordbok för att välja tabeller och kolumner och lägga till översättningslogiken för kolumnerna som finns i ColumnInformation bord.

Extra tips #1

Du kan också förenkla syntaxen. Ersätt varje JOIN med ett anrop till en funktion som hanterar (och döljer) översättningsaspekten, som visas nedan:

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äsa det önskade språket från sammanhanget, eller så kan du lägga till det som en extra parameter. I det här exemplet använder funktionen tabell- och kolumnnamnen som anges som parametrar plus radnyckeln (även tillhandahållen som en parameter) för att söka efter och returnera den önskade översättningen.

Att anropa en funktion innebär en ytterligare inverkan på prestanda på grund av kontextväxling mellan SQL och procedurspråk. Det kan dock vara en enklare lösning för databaser eller tabeller där mängden data som översätts tillåter det.

Båda exemplen – det med JOIN och det med en funktion – använder funktionen ISNULL() SQL Server. Så när översättningen till det önskade språket inte finns, visar den fortfarande det ursprungliga värdet lagrat i kolumnerna ProductName och ProductDescription istället för tomma eller NULL.

Allmänna överväganden

Den tredje metoden är vanligtvis den bästa för att implementera större design. Det möjliggör flexibilitet både vid design och när systemet väl är i bruk. Det finns dock specifika överväganden som kan göra de andra tillvägagångssätten användbara. Oavsett ditt val, överväg följande för att spara tid både vid design och vid utveckling/implementering.

Lägg till ett abstraktionslager

Som nämnts tidigare, överväg att skapa vyer som tar hand om översättningslogiken, t.ex. att välja en kolumn bland flera översättningskolumner eller gå med i specifika rader. Detta håller specifika implementeringsdetaljer dolda för programmerare. De använder helt enkelt dessa vyer istället för att behöva konstruera komplexa SQL-meningar varje gång de behöver komma åt en tabell med översättbar information.

Använd kontext för att filtrera data

Som nämnts i det första exemplet, överväg att använda kontextfunktioner som är tillgängliga i de flesta databasmotorer. Använd dem för att lagra användarspråksinformation när du är inloggad i systemet och filtrera sedan resultaten automatiskt i vyerna som tar hand om översättningen.

Automatisera

Moderna system kan ha hundratals och till och med tusentals tabeller. Ta dig tid att automatisera genereringen av översättningsvyerna istället för att skriva frågorna en efter en. Det kan ta lite tid att komma fram till ett manus som fungerar, men då kan du alltid skapa nya vyer eller återskapa befintliga på mindre än en sekund!


  1. Hur implementerar man SQLite-databas för att lagra bitmappsbild och text?

  2. Flerkolumnindex på 3 fält med heterogena datatyper

  3. Hur man anger primärnyckelnamn i EF-Code-First

  4. JOIN (VÄLJ ... ) ue PÅ 1=1?