Я все время сталкиваюсь с требованием i18n, где мои данные (а не мой пользовательский интерфейс) должны быть интернационализированы.
public class FooEntity
{
public long Id { get; set; }
public string Code { get; set; } // Some values might not need i18n
public string Name { get; set } // but e.g. this needs internationalized
public string Description { get; set; } // and this too
}
Каковы некоторые подходы, которые я мог бы использовать?
Некоторые вещи, которые я пробовал: -
1) Сохраните ключ ресурса в db
public class FooEntity
{
...
public string NameKey { get; set; }
public string DescriptionKey { get; set; }
}
- Плюсы: нет необходимости в сложных запросах для получения переведенной сущности.
System.Globalization
обрабатывает резервные копии для вас. - Минусы. Переводы не могут легко управляться пользователем-администратором (необходимо развернуть файлы ресурсов при изменении моего
Foo
).
2) Используйте тип сущности LocalizableString
public class FooEntity
{
...
public int NameId { get; set; }
public virtual LocalizableString Name { get; set; }
public int NameId { get; set; }
public virtual LocalizableString Description { get; set; }
}
public class LocalizableString
{
public int Id { get; set; }
public ICollection<LocalizedString> LocalizedStrings { get; set; }
}
public class LocalizedString
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual LocalizableString Parent { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Value { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string CultureCode { get; set; }
}
- Плюсы: все локализованные строки в одной таблице. Проверка может выполняться в строке.
- Минусы: запросы ужасны. Обязательно. Включите таблицу LocalizedStrings раз для каждой локализуемой строки в родительском объекте. Резервы сложны и связаны с широким объединением. Не удалось найти способ избежать N + 1 при получении, например. данные для таблицы.
3) Используйте родительский объект со всеми свойствами инварианта и дочерними объектами, содержащими все локализованные свойства
public class FooEntity
{
...
public ICollection<FooTranslation> Translations { get; set; }
}
public class FooTranslation
{
public long Id { get; set; }
public int ParentId { get; set; }
public virtual FooEntity Parent { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set }
public string Description { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string CultureCode { get; set; }
}
- Плюсы: не так сложно (но все же слишком сложно!), чтобы получить полный перевод объекта в память.
- Минусы: удвоить количество объектов. Невозможно обрабатывать частичные переводы объекта - особенно в том случае, если, скажем, имя исходит от
es
, но описание происходит отes-AR
.
У меня есть три требования к решению
-
Пользователи могут редактировать сущности, языки и переводы во время выполнения
-
Пользователи могут предоставлять частичные переводы с отсутствующими строками, исходящими из резервной копии в соответствии с System.Globalization
-
Объекты могут быть внесены в память без использования в. N + 1 выпусков