Как вы интернационализируете текст в базе данных? - PullRequest
7 голосов
/ 24 ноября 2008

Наличие полностью интернационализированной заявки является необходимостью, если вы хотите продавать по всему миру. В Java мы используем пакеты ресурсов, и это решает проблемы со статическим кодом.

Но что вы делаете с текстом, который хранится в базе данных? Начиная со статических определений, для изменяемых пользователем объектов, заканчивая введенными пользователем данными.

Предполагая, что у вас есть база данных, используемая пользователями с разными локалями - как вы справляетесь с этим? Как далеко вы интернационализируете? Где вы проводите черту? Какой обходной путь может помешать пользователям получать текст на языке, который они не понимают?

Ответы [ 7 ]

6 голосов
/ 24 ноября 2008

Не сохранять сгенерированный системой текст в базе данных. Вместо этого сохраните код (например, номер сообщения), а затем интернационализируйте его на уровне графического интерфейса. Убедитесь, что единственным текстом, который выходит непосредственно из базы данных, является текст, который пользователь вводит в себя. Убедитесь, что ваша база данных настроена на прием текста в Unicode.

2 голосов
/ 24 ноября 2008

Во-первых, будьте очень внимательны к ограничениям. Для контента, созданного пользователями, вы ищете перевод сообщества (ошибочный), машинный перевод (ненадежный) или платные человеческие переводчики (дорого!), Если вы хотите локализовать материал, который ваши пользователи вводят в ваше приложение. Возможно, вы захотите попросить своих пользователей предоставить две версии - одну для вашей культуры по умолчанию (на английском?) И одну для их локализованной культуры, чтобы вы могли предоставить запасной перевод для других пользователей?

Во-вторых, будьте готовы к очень длительным миграциям базы данных ... если у вас есть четыре столбца текста в электронной таблице Excel, вам вдруг приходится вставлять каждое значение в вашу систему перевода, извлекать локализованный идентификатор и затем сохраните , что в таблице, которую вы фактически импортируете - и SELECT * даст вам только идентификаторы фраз, которые вам нужно преобразовать обратно в строки, локализуя их в ваших таблицах перевода.

Тем не менее, вы можете локализовать множество справочных таблиц, раскрывающихся списков и т. Д., Которые управляются базой данных в типичном проекте. В других комментариях уже упоминалось о хранении значений StringId в базе данных, которые ссылаются на файлы внешних ресурсов или электронные таблицы, но если вы заинтересованы в том, чтобы хранить ВСЕ локализованный текст в базе данных вместе с самими данными, то этот подход может оказаться полезным.

Мы использовали таблицу под названием «Фраза», которая содержит идентификатор и содержимое по умолчанию (на английском языке) для каждого фрагмента текста в вашем приложении.

Ваши другие таблицы в итоге выглядят так:

CREATE TABLE ProductType (
    Id int primary key,
    NamePhraseId int, -- link to the Phrase containing the name of this product type.
    DescriptionPhraseId int
)

Создайте вторую таблицу Культура, которая содержит конкретные и нейтральные культуры, которые вы поддерживаете. Для получения бонусных баллов реализуйте эту таблицу в виде дерева со ссылками (каждая запись культуры содержит обнуляемую ссылку ParentCultureCode), чтобы вы могли откатиться от определенных культур ("fr-CA" для канадского французского) к нейтральным культурам ("fr" если региональная локализация не существует), к вашей инвариантной / стандартной культуре (обычно 'en', потому что на ней так много говорят)

Ваши фактические переводы находятся в таблице LocalizedPhrase, которая выглядит следующим образом:

CREATE TABLE LocalizedPhrase (
  PhraseId int primary key,
  CultureCode varchar(8) primary key,
  Content nvarchar(255) -- the actual localized content
)

Вы можете расширить эту модель, если хотите указать локализацию для мужчин и женщин:

CREATE TABLE GenderedLocalizedPhrase (
  PhraseId  int primary key,
  CultureCode varchar(8) primary key,
  GenderCode char(1) primary key, -- 'm', 'f' or '?' - links to Gender table
  Content nvarchar(255)
)

Вы захотите кэшировать весь этот табличный граф в памяти и соответствующим образом изменить свои стратегии запросов / объединений - одним из подходов является кэширование локализаций внутри классов Phrase и переопределение метода ToString () объекта Phrase для проверки текущей культуры потоков. Если вы попытаетесь сделать это в своих запросах, вы понесете существенные потери производительности, и каждый запрос будет выглядеть так:

-- assume @MyCulture contains the culture code ('ca-FR') that we are looking for:
SELECT 
    Product.Id, 
    Product.Name, 

    COALESCE(ProductStatusLocalizedPhrase.Content, ProductStatusPhrase.Content) as ProductStatus, 
    COALESCE(ProductTypeLocalizedPhrase.Content, ProductTypePhrase.Content) as ProductType, 
  FROM Product

    INNER JOIN ProductStatus ON Product.StatusId = ProductStatus.Id
    INNER JOIN Phrase as ProductStatusPhrase ON ProductStatus.NamePhraseId = Phrase.Id
    LEFT JOIN LocalizedPhrase as ProductStatusLocalizedPhrase 
      ON ProductStatus.NamePhraseId = ProductStatusLocalizedPhrase.Id and CultureCode = @MyCulture

    INNER JOIN ProductType ON Product.TypeId = ProductType.Id
    INNER JOIN Phrase as ProductTypePhrase ON ProductType.NamePhraseId = Phrase.Id
    LEFT JOIN LocalizedPhrase as ProductTypeLocalizedPhrase 
      ON ProductType.NamePhraseId = ProductTypeLocalizedPhrase.Id and CultureCode = @MyCulture
1 голос
/ 22 февраля 2014

Допустим, у вас есть таблица:

create table countries (
  country_id int primary key,
  short_name text not null unique,
  official_name text not null unique,
  iso_code char(2) not null unique
);

insert into countries values (12, 'Algeria', 'The People''s Democratic Republic of Algeria' 'DZ');

Затем вы создаете таблицу перевода:

create table countries_t (
  country_id int not null references countries(country_id),
  short_name text not null,
  official_name text not null,
  locale varchar(5) not null,

  primary key (country_id, locale)
);

insert into countries_t values
(12, 'Algérie', 'la République algérienne démocratique et populaire', 'fr'),
(12, 'Algerien', 'Demokratische Volksrepublik Algerien', 'de-DE');

Создание представления для возврата данных на основе пользовательской - определенной переменной сеанса. Ниже приведено описание, специфичное для PostgreSQL, но ваша база данных может поддерживать пользовательские переменные сеанса, иначе используйте временную таблицу:

create view countries_i18n as 
  select 
    a.country_id,
    coalesce(c.short_name, b.short_name, a.short_name) as short_name, --default to countries.name if translation not found
    coalesce(c.official_name, b.official_name, a.official_name) as official_name
    countries.iso_code
  from countries a
  left join countries_t b on b.id = a.id and b.locale = current_setting('my_custom_guc.locale')
  left join countries_t c on c.id = a.id and c.locale = left(current_setting('my_custom_guc.locale'), 2); --fall-back to 2-letter locale

Запросить таблицу на немецком языке, как говорят в Германии:

SET my_custom_guc.language_code = 'de-DE';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algerien    Demokratische ...

Запрос к таблице на канадском французском (возвращается к общему французскому):

SET my_custom_guc.locale= 'fr-CA';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algérie     la République ...

Запрос к таблице на испанском языке (перевода нет, возвращается английский):

SET my_custom_guc.language_code = 'es';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algeria    The People's D ...
1 голос
/ 02 июня 2009

Мы изменяем большую часть текста в нашей базе данных на «ключ: текст по умолчанию», затем ищем «ключ» в наших файлах перевода. Это относится ко всему тексту, который клиент не изменяет в базе данных (например, что называется «кредит-нота»). Когда клиент изменяет текст, он может просто удалить ключ, чтобы он всегда получал там значение.

В нашей системе есть несколько таблиц, которые содержат данные конфигурации, которые требуют вышеуказанного, таблицы, которые просто содержат текст, введенный клиентами, не являются проблемой, если каждому клиенту нужен только один язык.

1 голос
/ 24 ноября 2008

Какой обходной путь может удержать пользователей от получать текст на языке, который они не понимаешь?

Это будет проблемой только для введенных пользователем данных. Поэтому, если вы хотите, чтобы другие пользователи не видели контент в языке, который они могут не понимать, сохраните код локали вместе с контентом и отображайте этот контент только для тех, у кого выбран язык, выбранный той же локалью / пользователем.

С другой стороны, пользователи могут знать несколько языков, поэтому я бы не стал ограничивать их в просмотре контента, я просто добавил бы сообщение типа «Этот контент недоступен на выбранном вами языке, ...», а затем отобразил бы содержание в доступном языке. Таким образом вы увеличиваете вероятность того, что пользователь получит контент, который он может понять.

0 голосов
/ 24 ноября 2008

Мы используем XML-файл для нашей системы. Файл содержит ключевые ассоциации с определенной частью наших модулей. Таким образом, мы можем быстро сделать XPath для получения информации. У нас есть 1 файл для каждого языка (на данный момент мы поддерживаем 2 языка, но добавить язык очень просто, просто скопируйте и вставьте файл). Это решение не идеально, но имеет ряд преимуществ:

  1. Нет в базе данных.
  2. Может быть отредактировано кем-то, кто не связан с программированием.
  3. Простота реализации в нескольких представлениях (у нас есть WinForm и WebForm).
0 голосов
/ 24 ноября 2008

Статические данные - это самое простое, что я создал бы таблицу перевода, поэтому представьте себе таблицу UserStatus, в которой есть StatusId, TranslationToken, а затем в TranslationTable есть токен, язык и текст.

Или же вы можете просто вернуть токен для обработки приложением ваших файлов ресурсов.

Что касается пользовательских вводимых данных, это намного сложнее. Вы должны принять как минимум символы Юникода, но тогда возникает вопрос Сортировка и сравнение. Сортировка самая большая. Многое из того, что вы можете сделать, зависит от вашего приложения. Таким образом, если ваша база данных должна поддерживать только один язык в любой момент (представьте, что ваше приложение было распространено среди ваших клиентов), то сопоставление является спорным вопросом, поскольку вы можете установить его во время установки.

Однако, если вам нужно поддерживать несколько языков в одной базе данных, вам нужно будет правильно обрабатывать параметры сортировки. Единственный способ, которым мы нашли способ изменить параметры сортировки на лету, - это задать их в наших запросах, и для этого потребовалось создать динамический sql. В этом примере вы храните русский, английский и польский языки в одном поле в одной таблице.

Мы никогда не исследовали ничего, кроме латинского и кириллического сопоставления, но я думаю, азиатские языки будут работать одинаково.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...