Database
 sql >> база данни >  >> RDS >> Database

Проектиране на база данни за многоезични приложения

Докато някои софтуерни системи се използват от ограничен брой потребители, говорещи един и същ език, повечето организации трябва да унифицират и централизират своите приложения, за да бъдат използвани от хора, говорещи различни езици по целия свят. Многоезичните бази данни представляват допълнително ниво на трудност при проектирането и внедряването на модели на данни. В тази статия предлагаме някои подходи за справяне с това предизвикателство.

Каква информация трябва да съхраняваме на няколко езика?

На повърхността цялата информация за низовете може да изглежда правдоподобна за превод на множество езици. Това обаче обикновено не е така. Информация, свързана с клиентите, като CompanyName или Address може да се преведе, но това може да не е добра идея.

Вземете бизнес клиент в Обединеното кралство на име „Riverside Trucks“ с офис на „123 Upper Castle Road“. Не искате испаноговорящ потребител да отпечата и изпрати писмо до „Camiones Orilla“, намиращо се на „123 Calle Castillo Superior“. Royal Mail (пощенската услуга на Обединеното кралство) няма да го намери! Вероятно искате да преведете само колоните, съдържащи описателна информация, а не собствените имена.

Когато се проектира система за обработка на преводи, не винаги се знае предварително точно кои колони са преводими и кои не изискват превод. Изборът на гъвкав подход спестява много време при проектиране и разработка. Разгледайте статията „Как да проектираме система, готова за локализация“, за да видите някои примери.

Какви подходи обмисляме?

В тази статия описваме три подхода към дизайна на многоезична база данни. Започваме с най-простата, която не е толкова гъвкава, след което преминаваме към разглеждане на други опции, обяснявайки плюсовете и минусите за всяка.

Както синтаксисът, така и моделите на база данни (достъпни в уеб-базиран модел на данни Vertabelo), използвани в тази статия, са за SQL Server. Въпреки това, те лесно се адаптират към всяка база данни.

Подход 1:Създаване на допълнителни колони за съхраняване на преведено съдържание

Това е най-простият подход за прилагане, макар и не много гъвкав. Състои се от добавяне на колона за всяка колона и език, който трябва да използваме в нашата система, както е показано на следната диаграма Vertabelo:

Въпреки че това може да изглежда като много просто решение, то има някои недостатъци. Обясняваме по-долу.

Против:Сложност на кода

Този подход прави кода по-сложен. Това изисква от нас или да напишем различна заявка за всеки език, или да използваме CASE конструкция за извличане на превода за съответния език въз основа на потребителската конфигурация. Вижте например следния код:

SELECT ProductID, CASE @Language WHEN 'ES' THEN ProductName_ES WHEN 'DE' THEN ProductName_DE WHEN 'FR' THEN ProductName_FR ELSE ProductName END AS Product Name, CASE @Language WHEN 'ES' THEN ProductDescription_ES THEN ProductDescription_ES WHEN WHENDescription' FR' THEN ProductDescription_FR ELSE ProductDescription END AS ProductDescription, Price, Weight, ProductCategoryIDFROM ProductWHERE …

Забележка: В примера използваме променливата @Language, за да запазим езика, който искаме да използваме. Може да помислите да използвате SESSION_CONTEXT() (или контекст на приложението в Oracle), за да зададете и прочетете езика за всеки потребител.

Против:Липса на гъвкавост

На този подход липсва гъвкавост. Ако трябва да внедрим нов език, трябва да модифицираме нашия модел на данни, като добавим колона за новия език за всяка колона за превод в нашата система. Също така трябва да създадем нова езикова заявка за всяка таблица (или да редактираме съществуващата, като добавим нов CASE WHEN клауза, която използва новия език за всяка колона за превод).

Против:Предизвикателства при боравене с неизвестна информация

Представете си това:потребителят добавя продукт, но не знае как да го преведе и оставя преведените колони празни. Потребителите, говорещи тези езици, виждат NULL или празна информация в колоните, които може да са необходими.

Подход 2:Изолиране на преводими колони в отделна таблица

Този подход групира колоните в таблица в преводими и непреводими колони. Колоните, които не могат да се превеждат, остават в оригиналната таблица. За разлика от тях, преводимите са в отделна таблица, с външен ключ към оригиналната таблица и езиков индикатор. Вижте по-долу:

Диаграмата показва оригиналните таблици (без данни за превод) в бяло. Таблиците, съдържащи преводите, са в светло синьо, а основната таблица, съдържаща езиковата информация, е в жълто.

Това има огромни предимства за гъвкавост в сравнение с използването на множество колони, както беше обсъдено по-рано. Този метод не изисква промяна на модела на данни, когато е необходим нов език. Освен това синтаксисът за запитване на информацията е по-опростен:

ИЗБЕРЕТЕ p.ProductID, pt.ProductName, pt.ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product PLEFT JOIN ProductTranslation pt ON pt.ProductID =p.ProductID И pt.LanguageID =@LanguageWHERE … 

Въпреки това, все още има някои недостатъци, както ще разгледаме по-долу.

Против:Предизвикателства, когато трябва да бъдат преведени допълнителни колони

Ако трябва да преобразуваме непреводима колона в преводима (или обратно), трябва да модифицираме нашия модел на данни, премествайки колоната от една таблица в друга. Това обикновено предполага по-големи разходи, след като системата е внедрена и се използва.

Против:Предизвикателства при боравене с неизвестна информация

Подобно на първия подход, този подход има предизвикателства при работа с неизвестна информация. Отново, ако потребител добави продукт, но не знае как да го преведе и остави преведените колони празни, потребителите, говорещи тези езици, виждат NULL или празна информация в колони, която може да се изисква. Също така, заявката изисква LEFT JOIN в случай че преводът за езика на текущия потребител все още не е създаден, така че данните, които не могат да се превеждат, все още се показват.

Подход 3:Добавяне на подсистема за превод

Преводът може да се разглежда като функция, която е напълно независима от модела на данни, изискващ превод. В идеална система можем да активираме или деактивираме превода за всяка колона, без да изискваме модификация на модела на данните. За съжаление това е по-лесно да се каже, отколкото да се направи.

Представяме метод, който не оказва влияние върху съществуващия модел на данни и е напълно гъвкав. Въпреки че добавя сложност в момента на запитване на данните, не изисква никаква допълнителна промяна в модела на данни, с изключение на няколко таблици. Това може да е чудесен избор, ако трябва да добавите възможност за задържане на преводи към съществуващ модел на данни.

Нека да разгледаме модела и да видим как работи:

Първото нещо, което трябва да забележите, е, че оригиналният модел на данни няма никакви промени. Освен това няма пряка връзка между този модел и подсистемата за превод.

Подсистемата за превод включва малък речник с данни с таблиците и колоните, изискващи превод. Този речник на данни може да бъде модифициран чрез просто добавяне/премахване на редове, без да се променя моделът на данни. Преводите се съхраняват в отделна таблица, като всяка стойност се идентифицира със следните 3 колони:

  • ColumnID :Уникално идентифицира колоната (и таблицата), която превеждаме.
  • KeyID :Съхранява идентификатора (първичния ключ) на конкретния ред, който превеждаме.
  • LanguageID :Идентифицира езика на превода.

Този дизайн позволява данните да се въвеждат и съхраняват в оригиналните таблици, като се добавят преводи само ако и когато е необходимо. Преведената информация се използва при извличане на данни, като оригиналните данни (на оригиналния език) се запазват недокоснати.

Данните могат да бъдат запитвани с помощта на по-сложен синтаксис от примерите по-горе. Изисква допълнителен JOIN за всяка превеждаема колона, както е показано по-долу:

SELECT p.ProductID, ISNULL(t1.TranslationValue, p.ProductName) AS ProductName, ISNULL(t2.TranslationValue, p.ProductDescription) AS ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product pLEFT1 JOIN Translation НА t1.ColumnID =<> AND t1.Key =p.ProductID И t1.LanguageID =@LanguageLEFT JOIN Translation t2 ON t2.ColumnID =<> AND AND tL.ang.ang. =@LanguageWHERE …;

Забележка:<<ProductName_ColumnID>> ” и „<<ProductDescription_ColumnID>> ” трябва да бъде заменен с идентификаторите на колоните, които ще бъдат преведени като съхранени в ColumnInformation маса. Помислете за генериране на изгледи за превод за всяка таблица, изискваща превод, за да скриете сложността на JOIN за крайните потребители. Можете дори да автоматизирате тази стъпка със скрипт, който генерира всеки изглед. Този скрипт може да отправя заявка към речника на данните на базата данни, за да избере таблиците и колоните и да добави логиката на превод за колоните, които съществуват в ColumnInformation таблица.

Допълнителен съвет №1

Можете също да опростите синтаксиса. Заменете всяко JOIN с извикване на функция, която обработва (и скрива) аспекта на превода, както е показано по-долу:

SELECT p.ProductID, ISNULL(fn_translate('Product','ProductName',ProductID), p.ProductName) КАТО ProductName, ISNULL(fn_translate('Product','ProductDescription',ProductID), p.ProductDescription) AS ProductName, p. Price, p. Weight, p.ProductCategoryIDFROM Product pWHERE …;

Функцията може да прочете желания език от контекста или да го добавите като допълнителен параметър. В този пример функцията използва имената на таблицата и колоните, предоставени като параметри, плюс ключа на реда (също предоставен като параметър), за да търси и връща желания превод.

Извикването на функция предполага допълнително въздействие върху производителността поради превключване на контекста между SQL и процедурен език. Въпреки това, може да е по-просто решение за бази данни или таблици, където количеството данни, които се превеждат, го позволява.

И двата примера – този с JOIN и този с функция – използват функцията ISNULL() SQL Server. Така че, когато преводът на желания език не съществува, той все още показва оригиналната стойност, съхранена в колоните ProductName и ProductDescription вместо празни места или NULL.

Общи съображения

Третият подход обикновено е най-добрият за внедряване на по-големи проекти. Той позволява гъвкавост както при проектирането, така и след като системата се използва. Въпреки това, има специфични съображения, които могат да направят другите подходи полезни. Независимо от избора си, вземете предвид следното, за да спестите време както при проектирането, така и при разработването/внедряването.

Добавяне на слой на абстракция

Както споменахме по-рано, помислете за създаване на изгледи, които се грижат за логиката на превода, например избиране на една колона измежду множество колони за превод или присъединяване към конкретни редове. Това запазва специфичните подробности за изпълнението скрити от програмистите. Те просто използват тези изгледи, вместо да се налага да конструират сложни SQL изречения всеки път, когато имат нужда от достъп до таблица с информация за превод.

Използвайте контекста за филтриране на данни

Както бе споменато в първия пример, помислете за използването на контекстни функции, налични в повечето машини за бази данни. Използвайте ги, за да съхранявате информация за езика на потребителя, след като сте влезли в системата, след което автоматично филтрирайте резултатите в изгледите, които се грижат за превода.

Автоматично

Съвременните системи могат да имат стотици и дори хиляди таблици. Отделете време да автоматизирате генерирането на изгледите за превод, вместо да пишете заявките една по една. Може да ви отнеме известно време, за да стигнете до скрипт, който работи, но тогава винаги можете да създадете нови изгледи или да пресъздадете съществуващи за по-малко от секунда!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Нотация със стрелка

  2. SQL SELECT SUM

  3. Вашето окончателно ръководство за SQL присъединяване:КРЪСТО ПРИСЪЕДИНЯВАНЕ – част 3

  4. Присъединете се към нас в Лас Вегас за SQLintersection и спестете $100

  5. Статистика за изчакване на коляното:SOS_SCHEDULER_YIELD