sql >> Database >  >> RDS >> Database

Databaseontwerp voor meertalige toepassingen

Hoewel sommige softwaresystemen worden gebruikt door een beperkt aantal gebruikers die dezelfde taal spreken, moeten de meeste organisaties hun applicaties verenigen en centraliseren zodat ze kunnen worden gebruikt door mensen die verschillende talen over de hele wereld spreken. Meertalige databases vormen een extra moeilijkheidsgraad bij het ontwerpen en implementeren van datamodellen. In dit artikel stellen we enkele benaderingen voor om met deze uitdaging om te gaan.

Welke informatie moeten we in meerdere talen opslaan?

Op het eerste gezicht lijkt alle tekenreeksinformatie plausibel voor vertaling in meerdere talen. Dit is echter meestal niet het geval. Klantgerelateerde informatie zoals CompanyName of Address kan worden vertaald, maar dat is misschien geen goed idee.

Neem een ​​zakelijke klant in het Verenigd Koninkrijk genaamd "Riverside Trucks" met een kantoor op "123 Upper Castle Road". U wilt niet dat een Spaanssprekende gebruiker een brief afdrukt en stuurt naar "Camiones Orilla" op "123 Calle Castillo Superior". Royal Mail (de Britse postdienst) zal het niet vinden! U wilt waarschijnlijk alleen de kolommen vertalen die beschrijvende informatie bevatten, niet de eigennamen.

Bij het ontwerpen van een systeem om vertalingen af ​​te handelen, is niet altijd vooraf precies bekend welke kolommen vertaalbaar zijn en welke geen vertalingen nodig hebben. Kiezen voor een flexibele aanpak scheelt veel tijd in ontwerp en ontwikkeling. Bekijk het artikel "Een systeem ontwerpen dat klaar is voor lokalisatie" om enkele voorbeelden te zien.

Welke benaderingen overwegen we?

In dit artikel beschrijven we drie benaderingen voor het ontwerpen van meertalige databases. We beginnen met de eenvoudigste die niet zo flexibel is en gaan dan verder met het overwegen van andere opties, waarbij we de voor- en nadelen voor elke optie uitleggen.

Zowel de syntaxis als de databasemodellen (beschikbaar op de webgebaseerde datamodeller van Vertabelo) die in dit artikel worden gebruikt, zijn voor SQL Server. Ze zijn echter gemakkelijk aan te passen aan elke database-engine.

Aanpak 1:extra kolommen maken om vertaalde inhoud te bevatten

Dit is de eenvoudigste aanpak om te implementeren, zij het niet erg flexibel. Het bestaat uit het toevoegen van een kolom voor elke kolom en taal die we in ons systeem moeten gebruiken, zoals weergegeven in het volgende Vertabelo-diagram:

Hoewel dit een heel eenvoudige oplossing lijkt, heeft het enkele nadelen. We leggen het hieronder uit.

Con:Code Complexiteit

Deze aanpak maakt de code complexer. Het vereist dat we ofwel een andere zoekopdracht voor elke taal schrijven of een CASE . gebruiken construct om de vertaling voor de juiste taal op te halen op basis van de gebruikersconfiguratie. Zie bijvoorbeeld de volgende code:

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 …

Opmerking: In het voorbeeld gebruiken we de variabele @Language om de taal te behouden die we willen gebruiken. U kunt overwegen om SESSION_CONTEXT() (of Application Context in Oracle) te gebruiken om de taal voor elke gebruiker in te stellen en te lezen.

Con:gebrek aan flexibiliteit

Deze aanpak mist flexibiliteit. Als we een nieuwe taal moeten implementeren, moeten we ons gegevensmodel aanpassen door een kolom voor de nieuwe taal toe te voegen voor elke vertaalbare kolom in ons systeem. We moeten ook een nieuwe taalquery voor elke tabel maken (of de bestaande bewerken door een nieuwe CASE WHEN toe te voegen clausule die de nieuwe taal gebruikt voor elke vertaalbare kolom).

Con:uitdagingen bij het omgaan met onbekende informatie

Stel je voor:een gebruiker voegt een product toe maar weet niet hoe hij het moet vertalen en laat de vertaalde kolommen leeg. Gebruikers die deze talen spreken, zien NULL of blanco informatie in de kolommen die mogelijk vereist zijn.

Aanpak 2:Vertaalbare kolommen isoleren in een aparte tabel

Deze aanpak groepeert de kolommen in een tabel in vertaalbare en niet-vertaalbare kolommen. Niet-vertaalbare kolommen blijven in de oorspronkelijke tabel. De vertaalbare daarentegen staan ​​in een aparte tabel, met een vreemde sleutel naar de originele tabel en een taalindicator. Zie hieronder:

Het diagram toont de originele tabellen (zonder vertaalbare gegevens) in het wit. De tabellen met de vertalingen zijn lichtblauw en de hoofdtabel met de taalinformatie is geel.

Dit heeft enorme flexibiliteitsvoordelen ten opzichte van het gebruik van meerdere kolommen zoals eerder besproken. Deze methode vereist geen wijziging van het gegevensmodel wanneer een nieuwe taal nodig is. Ook is de syntaxis om de informatie op te vragen eenvoudiger:

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 …

Er zijn echter nog steeds enkele nadelen, zoals we hieronder bespreken.

Con:uitdagingen wanneer extra kolommen moeten worden vertaald

Als we een niet-vertaalbare kolom moeten converteren naar een vertaalbare (of vice versa), moeten we ons gegevensmodel wijzigen, waarbij de kolom van de ene tabel naar de andere wordt verplaatst. Dit brengt meestal hogere kosten met zich mee als het systeem eenmaal is geïmplementeerd en in gebruik is.

Con:uitdagingen bij het omgaan met onbekende informatie

Net als de eerste benadering heeft deze benadering uitdagingen bij het omgaan met onbekende informatie. Nogmaals, als een gebruiker een product toevoegt maar niet weet hoe hij het moet vertalen en de vertaalde kolommen leeg laat, zien gebruikers die die talen spreken NULL of blanco informatie in kolommen die mogelijk vereist zijn. De zoekopdracht vereist ook een LEFT JOIN in het geval dat de vertaling voor de taal van de huidige gebruiker nog niet is gemaakt, zodat de niet-vertaalbare gegevens nog steeds worden weergegeven.

Aanpak 3:Een vertaalsubsysteem toevoegen

Vertaling kan worden beschouwd als een functie die volledig onafhankelijk is van het gegevensmodel dat moet worden vertaald. In een ideaal systeem kunnen we vertaling voor elke kolom in- of uitschakelen zonder dat het gegevensmodel hoeft te worden gewijzigd. Helaas is dit makkelijker gezegd dan gedaan.

We presenteren een methode die geen impact heeft op het bestaande datamodel en volledig flexibel is. Hoewel het complexiteit toevoegt op het moment dat de gegevens worden opgevraagd, vereist het geen extra wijziging aan het gegevensmodel, behalve voor een paar tabellen. Dit kan een goede keuze zijn als u de mogelijkheid wilt toevoegen om vertalingen vast te houden aan een bestaand gegevensmodel.

Laten we het model eens bekijken en zien hoe het werkt:

Het eerste dat opvalt, is dat het oorspronkelijke datamodel helemaal geen wijzigingen heeft ondergaan. Er is ook geen directe relatie tussen dat model en het vertaalsubsysteem.

Het vertaalsubsysteem omvat een kleine datadictionary met de tabellen en kolommen die moeten worden vertaald. Deze datadictionary kan worden gewijzigd door alleen rijen toe te voegen/te verwijderen zonder het datamodel te wijzigen. Vertalingen worden opgeslagen in een aparte tabel, waarbij elke waarde wordt geïdentificeerd door de volgende 3 kolommen:

  • ColumnID :Identificeert op unieke wijze de kolom (en de tabel) die we vertalen.
  • KeyID :Slaat de ID (primaire sleutel) op van de specifieke rij die we vertalen.
  • LanguageID :identificeert de taal van de vertaling.

Met dit ontwerp kunnen gegevens worden ingevoerd en opgeslagen in de originele tabellen, waarbij alleen vertalingen worden toegevoegd als en wanneer dat nodig is. De vertaalde informatie wordt gebruikt bij het ophalen van gegevens, waarbij de originele gegevens (in de oorspronkelijke taal) onaangeroerd blijven.

Gegevens kunnen worden opgevraagd met behulp van een complexere syntaxis dan de bovenstaande voorbeelden. Het vereist een extra JOIN voor elke vertaalbare kolom zoals hieronder weergegeven:

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 …;

Opmerking:<<ProductName_ColumnID>> ” en “<<ProductDescription_ColumnID>> ” moet worden vervangen door de ID's van de kolommen die moeten worden vertaald zoals opgeslagen in de ColumnInformation tafel. Overweeg om vertaalweergaven te genereren voor elke tabel die moet worden vertaald om de complexiteit van de JOIN's voor de eindgebruikers te verbergen. U kunt deze stap zelfs automatiseren met een script dat elke weergave genereert. Dit script kan de datadictionary van de database opvragen om de tabellen en kolommen te kiezen en de vertaallogica toe te voegen voor de kolommen die bestaan ​​in de ColumnInformation tafel.

Extra tip #1

U kunt de syntaxis ook vereenvoudigen. Vervang elke JOIN door een aanroep van een functie die het vertaalaspect afhandelt (en verbergt), zoals hieronder weergegeven:

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 …;

De functie kan de gewenste taal uit de context lezen, of u kunt deze als extra parameter toevoegen. In dit voorbeeld gebruikt de functie de tabel- en kolomnamen die als parameters zijn opgegeven, plus de rijsleutel (ook als parameter opgegeven) om de gewenste vertaling te zoeken en terug te geven.

Het aanroepen van een functie heeft wel een extra impact op de prestaties vanwege het wisselen van context tussen SQL en proceduretaal. Het kan echter een eenvoudigere oplossing zijn voor databases of tabellen waar de hoeveelheid data die wordt vertaald dit toelaat.

Beide voorbeelden - die met een JOIN en die met een functie - gebruiken de ISNULL() SQL Server-functie. Dus als de vertaling naar de gewenste taal niet bestaat, wordt nog steeds de oorspronkelijke waarde weergegeven die is opgeslagen in de kolommen ProductName en ProductDescription in plaats van spaties of NULL.

Algemene overwegingen

De derde benadering is meestal de beste voor het implementeren van grotere ontwerpen. Het zorgt voor flexibiliteit, zowel bij het ontwerp als wanneer het systeem eenmaal in gebruik is. Er zijn echter specifieke overwegingen die de andere benaderingen nuttig kunnen maken. Ongeacht uw keuze, overweeg het volgende om tijd te besparen, zowel bij het ontwerp als bij de ontwikkeling/implementatie.

Een abstractielaag toevoegen

Zoals eerder vermeld, kunt u overwegen weergaven te maken die voor de vertaallogica zorgen, bijvoorbeeld één kolom selecteren uit meerdere vertaalkolommen of verbinding maken met specifieke rijen. Hierdoor blijven specifieke implementatiedetails verborgen voor programmeurs. Ze gebruiken gewoon deze weergaven in plaats van dat ze elke keer dat ze een tabel met vertaalbare informatie nodig hebben, complexe SQL-zinnen moeten construeren.

Context gebruiken om gegevens te filteren

Zoals vermeld in het eerste voorbeeld, kunt u overwegen om contextfuncties te gebruiken die beschikbaar zijn in de meeste database-engines. Gebruik ze om informatie over de taal van de gebruiker op te slaan zodra u bent ingelogd op het systeem en filter vervolgens de resultaten automatisch in de weergaven die voor de vertaling zorgen.

Automatiseren

Moderne systemen kunnen honderden en zelfs duizenden tabellen hebben. Neem de tijd om het genereren van de vertaalweergaven te automatiseren in plaats van de vragen één voor één te schrijven. Het kan even duren voordat u tot een script komt dat werkt, maar u kunt altijd in minder dan een seconde nieuwe weergaven maken of bestaande weergaven opnieuw maken!


  1. Waar u op moet letten als uw MySQL-replicatie achterblijft

  2. Gebruik sys.trigger_event_types om triggergebeurtenistypen in SQL Server weer te geven

  3. Entity Framework 6 - Timing-query's

  4. Vergelijk datums die zijn opgeslagen als string met behulp van Datetime