Я занимаюсь разработкой мультиязычного программного обеспечения. Что касается кода приложения, локализуемость не является проблемой. Мы можем использовать языковые ресурсы и иметь все виды инструментов, которые хорошо с ними работают.
Но каков наилучший подход при определении многоязычной схемы базы данных? Допустим, у нас много таблиц (100 или более), и каждая таблица может иметь несколько столбцов, которые можно локализовать (большинство столбцов nvarchar должны быть локализуемыми). Например, одна из таблиц может содержать информацию о продукте:
CREATE TABLE T_PRODUCT (
NAME NVARCHAR(50),
DESCRIPTION NTEXT,
PRICE NUMBER(18, 2)
)
Я могу подумать о трех подходах к поддержке многоязычного текста в столбцах ИМЯ и ОПИСАНИЕ:
Отдельная колонка для каждого языка
Когда мы добавляем новый язык в систему, мы должны создать дополнительные столбцы для хранения переведенного текста, например:
CREATE TABLE T_PRODUCT ( NAME_EN NVARCHAR(50), NAME_DE NVARCHAR(50), NAME_SP NVARCHAR(50), DESCRIPTION_EN NTEXT, DESCRIPTION_DE NTEXT, DESCRIPTION_SP NTEXT, PRICE NUMBER(18,2) )
Таблица перевода с колонками для каждого языка
Вместо хранения переведенного текста хранится только внешний ключ в таблице переводов. Таблица переводов содержит столбец для каждого языка.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID, TEXT_EN NTEXT, TEXT_DE NTEXT, TEXT_SP NTEXT )
Таблицы перевода со строками для каждого языка
Вместо хранения переведенного текста хранится только внешний ключ в таблице переводов. Таблица переводов содержит только ключ, а отдельная таблица содержит строку для каждого перевода на язык.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID ) CREATE TABLE T_TRANSLATION_ENTRY ( TRANSLATION_FK, LANGUAGE_FK, TRANSLATED_TEXT NTEXT ) CREATE TABLE T_TRANSLATION_LANGUAGE ( LANGUAGE_ID, LANGUAGE_CODE CHAR(2) )
У каждого решения есть свои плюсы и минусы, и я хотел бы узнать, как вы относитесь к этим подходам, что вы рекомендуете и как бы вы подошли к разработке мультиязычной схемы базы данных.
LANGUAGE_CODE
являются естественным ключом, избегайтеLANGUAGE_ID
.Ответы:
Что вы думаете о наличии связанной таблицы перевода для каждой переводимой таблицы?
Таким образом, если у вас есть несколько переводимых столбцов, потребуется только одно объединение, чтобы получить его +, поскольку вы не генерируете автоматический перевод, может быть проще импортировать элементы вместе с соответствующими переводами.
Отрицательной стороной этого является то, что если у вас есть сложный механизм восстановления языка, вам может потребоваться реализовать его для каждой таблицы перевода - если вы полагаетесь на какую-то хранимую процедуру для этого. Если вы сделаете это из приложения, это, вероятно, не будет проблемой.
Дайте мне знать, что вы думаете - я также собираюсь принять решение по этому поводу для нашего следующего заявления. До сих пор мы использовали ваш третий тип.
источник
T_PRODUCT
будет 1 миллион строк,T_PRODUCT_tr
будет 2 миллиона. Снизит ли это эффективность SQL?Это интересный вопрос, так что давай некромантом.
Давайте начнем с проблем метода 1:
Проблема: Вы денормализуете, чтобы сохранить скорость.
В SQL (кроме PostGreSQL с hstore) вы не можете передать язык параметров и сказать:
Итак, вы должны сделать это:
Это означает, что вы должны изменить ВСЕ ваши запросы, если вы добавите новый язык. Это естественным образом приводит к использованию «динамического SQL», поэтому вам не нужно изменять все ваши запросы.
Обычно это приводит к чему-то вроде этого (и, кстати, его нельзя использовать в представлениях или табличных функциях, что действительно является проблемой, если вам действительно нужно отфильтровать отчетную дату)
Проблема в том, что:
а) Форматирование даты очень специфично для языка, поэтому у вас возникает проблема, если вы не вводите данные в формате ISO (чего обычно не делает обычный программист, работающий в саду), а в случае отчет, который пользователь наверняка не сделает для вас, даже если ему явно дано указание сделать это).
и
б) самое главное , вы теряете любую проверку синтаксиса . Если
<insert name of your "favourite" person here>
схема изменится из-за внезапного изменения требований к крылу и создания новой таблицы, старая останется, но поле ссылки будет переименовано, вы не получите никакого предупреждения. Отчет даже работает, когда вы запускаете его без выбора параметра wing (==> guid.empty). Но внезапно, когда реальный пользователь фактически выбирает крыло ==>бум . Этот метод полностью нарушает любой вид тестирования.Метод 2:
В двух словах: «Отличная» идея (предупреждение - сарказм), давайте объединим недостатки метода 3 (медленная скорость при большом количестве записей) с довольно ужасными недостатками метода 1.
Единственное преимущество этого метода заключается в том, что вы сохраняете все переводы в одной таблице, и, следовательно, сделать обслуживание простым. Однако то же самое может быть достигнуто с помощью метода 1 и хранимой процедуры динамического SQL, а также (возможно, временной) таблицы, содержащей переводы, и имени целевой таблицы (и это довольно просто, если вы назвали все свои текстовые поля тем же).
Метод 3:
Одна таблица для всех переводов: Недостаток: Вы должны хранить n внешних ключей в таблице продуктов для n полей, которые вы хотите перевести. Следовательно, вы должны сделать n объединений для n полей. Когда таблица перевода является глобальной, в ней много записей, и объединения становятся медленными. Кроме того, вы всегда должны присоединиться к таблице T_TRANSLATION n раз для n полей. Это довольно накладные расходы. Теперь, что вы делаете, когда вы должны приспособить индивидуальные переводы для каждого клиента? Вам придется добавить еще 2x n объединений на дополнительную таблицу. Если вам нужно объединить, скажем, 10 таблиц с 2x2xn = 4n дополнительных объединений, что за беспорядок! Кроме того, этот дизайн позволяет использовать тот же перевод с 2 таблицами. Если я изменю имя элемента в одной таблице, действительно ли я хочу изменить запись в другой таблице также КАЖДЫЙ ОДИН РАЗ?
Кроме того, вы больше не можете удалять и повторно вставлять таблицу, потому что в ТАБЛИЦЕ ПРОДУКТА теперь есть внешние ключи ... вы, конечно, можете опустить установку FK, а затем
<insert name of your "favourite" person here>
можете удалить таблицу и повторно вставить все записи с newid () [или указанием идентификатора в вставке, но с идентификационной вставкой OFF ], и это очень скоро приведет (и приведет) к мусору данных (и исключениям нулевой ссылки).Способ 4 (не указан): Сохранение всех языков в поле XML в базе данных. например
Затем вы можете получить значение с помощью XPath-Query в SQL, где вы можете поместить строковую переменную в
И вы можете обновить значение следующим образом:
Где вы можете заменить
/lang/de/...
на'.../' + @in_language + '/...'
В некотором роде, как в PostGre hstore, за исключением того, что из-за накладных расходов на синтаксический анализ XML (вместо чтения записи из ассоциативного массива в PG hstore) он становится слишком медленным, а кодирование xml делает его слишком болезненным, чтобы быть полезным.
Метод 5 (в соответствии с рекомендацией SunWuKung, который вы должны выбрать): одна таблица перевода для каждой таблицы «Продукт». Это означает одну строку на язык и несколько «текстовых» полей, поэтому требуется только ОДНО (слева) соединение по N полям. Затем вы можете легко добавить поле по умолчанию в «Product» -таблицу, вы можете легко удалить и повторно вставить таблицу перевода, и вы можете создать вторую таблицу для пользовательских переводов (по запросу), которую вы также можете удалить и вставьте заново), и у вас все еще есть все внешние ключи.
Давайте сделаем пример, чтобы увидеть это работает:
Сначала создайте таблицы:
Затем заполните данные
А затем запросите данные:
Если вы ленивы, то вы также можете использовать ISO-TwoLetterName ('DE', 'EN' и т. Д.) В качестве первичного ключа языковой таблицы, тогда вам не нужно искать идентификатор языка. Но если вы сделаете это, вы, возможно, захотите использовать вместо этого тег IETF-language , что лучше, потому что вы получаете de-CH и de-DE, что на самом деле не одинаково для орфографии (double s вместо ß везде) , хотя это тот же базовый язык. Это как маленькая деталь, которая может быть важна для вас, особенно если учесть, что en-US и en-GB / en-CA / en-AU или fr-FR / fr-CA имеют схожие проблемы.
Цитата: нам это не нужно, мы делаем наши программы только на английском языке.
Ответ: Да, но какой?
В любом случае, если вы используете целочисленный идентификатор, вы гибки и можете изменить свой метод в любое время.
И вы должны использовать это целое число, потому что нет ничего более раздражающего, разрушительного и хлопотного, чем неудачный дизайн Db.
См. Также RFC 5646 , ISO 639-2 ,
И, если вы все еще говорите «мы» только сделать наше приложение для «только одной культуры» (как EN-US обычно) - поэтому мне не нужно , что дополнительное число, это было бы хорошо время и место , чтобы упомянуть Языковые теги IANA , не так ли?
Потому что они идут так:
и
(в 1996 году была проведена реформа орфографии ...) Попробуйте найти слово в словаре, если оно написано с ошибкой; это становится очень важным в приложениях, связанных с правовыми и общественными сервисными порталами.
Что еще более важно, есть регионы, которые переходят от кириллицы к латинским алфавитам, что может быть просто более неприятным, чем поверхностные неудобства некоторых неясных реформ в области орфографии, поэтому это может быть важным фактором, в зависимости от того, в какой стране вы живете. Так или иначе, лучше иметь это целое число, на всякий случай ...
Изменить:
и добавив
ON DELETE CASCADE
послеВы можете просто сказать:
DELETE FROM T_Products
и не получить никакого нарушения внешнего ключа.Что касается сопоставления, я бы сделал это так:
A) Имейте свой собственный DAL
B) Сохраните желаемое имя сопоставления в языковой таблице
Возможно, вы захотите поместить параметры сортировки в их собственную таблицу, например:
C) Имейте имя сопоставления в вашей информации auth.user.language
D) Напишите свой SQL следующим образом:
E) Затем вы можете сделать это в своем DAL:
Который затем даст вам этот прекрасно составленный SQL-запрос
источник
Третий вариант является лучшим по нескольким причинам:
-Адам
источник
Посмотрите на этот пример:
Я думаю, что нет необходимости объяснять, структура описывает себя.
источник
Я обычно хотел бы пойти на этот подход (не фактический sql), это соответствует вашему последнему варианту.
Потому что все переводимые тексты в одном месте значительно облегчают обслуживание. Иногда переводы передаются в бюро переводов, поэтому вы можете отправить им только один большой файл экспорта и импортировать его обратно так же легко.
источник
Translation
таблица илиTranslationItem.translationitemid
столбец?Прежде чем перейти к техническим деталям и решениям, вам следует остановиться на минутку и задать несколько вопросов о требованиях. Ответы могут оказать огромное влияние на техническое решение. Примеры таких вопросов:
- Будут ли все языки использоваться постоянно?
- Кто и когда будет заполнять колонки версиями на разных языках?
- Что происходит, когда пользователю понадобится определенный язык текста, а в системе его нет?
- Только тексты должны быть локализованы или есть и другие элементы (например, PRICE можно хранить в $ и €, потому что они могут отличаться)
источник
Я искал несколько советов по локализации и нашел эту тему. Мне было интересно, почему это используется:
Таким образом, вы получаете что-то вроде user39603 предлагает:
Разве вы не можете просто оставить перевод стола, чтобы вы получили это:
источник
ProductItem
стол что-то вродеProductTexts
илиProductL10n
хотя. Имеет больше смысла.Я согласен с рандомизатором. Я не понимаю, зачем вам таблица "перевод".
Я думаю, этого достаточно
источник
Будет ли жизнеспособным следующий подход? Допустим, у вас есть таблицы, в которых нужно перевести более 1 столбца. Таким образом, для продукта у вас может быть как название продукта, так и описание продукта, которые необходимо перевести. Не могли бы вы сделать следующее:
источник
«Какой из них лучший» зависит от ситуации в проекте. Первый легко выбрать и поддерживать, а также производительность является наилучшей, поскольку при выборе объекта не требуется объединять таблицы. Если вы подтвердили, что ваш проект поддерживает только 2 или 3 языка, и он не увеличится, вы можете использовать его.
Второй - хорошо, но его трудно понять и поддерживать. И производительность хуже первой.
Последний хорош в масштабируемости, но плох в производительности. Таблица T_TRANSLATION_ENTRY будет становиться все больше и больше, это ужасно, когда вы хотите получить список сущностей из некоторых таблиц.
источник
В этом документе описываются возможные решения, а также преимущества и недостатки каждого метода. Я предпочитаю «локализацию строк», потому что вам не нужно изменять схему БД при добавлении нового языка.
источник