多语言数据库,具有默认回退功能
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多语言数据库,具有默认回退功能相关的知识,希望对你有一定的参考价值。
我有一个问题,我知道,已被广泛讨论过,但在我看来,还有一个方面需要澄清。
我正在创建一个带有多语言数据库的Web应用程序,我已经找到了一些好的练习文章(例如this),这里有像this这样的堆栈溢出答案。
所以我决定使用一个带有我的项目ID的主表和另一个带有每个项目翻译的表格,比方说,例如
Content
ContentTranslation
要么
Category
CategoryTranslation
等等。
现在我在做什么?我只是从数据库中获取所有翻译的项目,然后我迭代每一个以根据当前用户的本地查找正确的翻译,如果我找到正确的本地我设置到主要对象,该页面的翻译渲染,否则我只是得到标记为“默认”的翻译。
但是,对于大量对象和翻译,服务器响应时间可能会增长,即使用户可能没有注意到,我也不希望这样。
那么,这个用例有没有好的做法呢?例如,一些特定的查询说“选择带语言环境的翻译”,但是如果你没有找到它只是设置了一个“默认”标志?
现在,我正在使用Spring MVC和Hibernate以及JPA(通过JPARepository)。
我的对象都扩展了我用这种方式创建的基本可翻译类
@MappedSuperclass
public abstract class Translatable<T extends Translation> extends BaseDTO {
private static final long serialVersionUID = 562001309781752460L;
private String title;
@OneToMany(fetch=FetchType.EAGER, orphanRemoval=true, cascade=CascadeType.ALL)
private Set<T> translations = new HashSet<T>();
@Transient private T currentLocale;
public void addLocale(T translation, boolean edit) {
if (!edit)
getTranslations().add(translation);
}
public void remLocale(String locale) {
T tr = null;
for (T candidate: getTranslations()) {
if (candidate.getLocale().equals(locale))
tr = candidate;
}
getTranslations().remove(tr);
}
public T getLocaleFromString(String locale) {
if (locale == null)
return null;
for (T trans: translations) {
if (trans.getLocale().equals(locale))
return trans;
}
return null;
}
public T getDefaultLocale() {
for (T tr: translations) {
if (tr.isDefaultLocale())
return tr;
}
return null;
}
public Set<T> getTranslations() {
return translations;
}
public void setTranslations(Set<T> translations) {
this.translations = translations;
}
public T getCurrentLocale() {
return currentLocale;
}
public void setCurrentLocale(T currentLocale) {
this.currentLocale = currentLocale;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
因此,在我的控制器中,我迭代翻译,找到具有正确语言环境的语言并填充“currentLocale”属性,在我的页面中我只是采用它并且用户获得正确的语言。
我希望我一直很清楚,而不是凌乱,但如果你需要更多的信息,我会很高兴告诉你更多。
一些注意事项:
- 我的答案更多的是my answer to this question的补充,你添加了一个评论然后导致了这个问题
- 在我的回答中,我正在使用C#和MS SQL Server(我将省略任何OR映射特定代码)
在我的应用程序中,我使用两种不同的方法来加载多语言数据,具体取决于用例:
行政/ CRUD
在用户输入数据或编辑现有数据(例如带有翻译的产品)的情况下,我使用与您在上述问题中所示相同的方法,例如:
public class Product
{
public int ID {get; set;}
public string SKU {get; set;}
public IList<ProductTranslation> Translations {get; set;}
}
public class ProductTranslation
{
public string Language {get; set;}
public bool IsDefaultLanguage {get; set;}
public string Title {get; set;}
public string Description {get; set;}
}
即我将让OR-mapper加载产品实例并附上所有翻译。然后我遍历翻译并挑选所需的翻译。
前端/只读
在这种情况下,主要是前端代码,我通常只向用户显示信息(最好是用户的语言),我使用的是另一种方法:
首先,我使用的是不同的数据模型,它不支持/知道多个翻译的概念。相反,它只是当前用户使用“最佳”语言表示的产品:
public class Product
{
public int ID {get; set;}
public string SKU {get; set;}
// language-specific properties
public string Title {get; set;}
public string Description {get; set;}
}
要加载此数据,我使用不同的查询(或存储过程)。例如。要使用@Id
语言加载ID为@Language
的产品,我将使用以下查询:
SELECT
p.ID,
p.SKU,
-- get title, description from the requested translation,
-- or fall back to the default if not found:
ISNULL(tr.Title, def.Title) Title,
ISNULL(tr.Description, def.Description) Description
FROM Products p
-- join requested translation, if available:
LEFT OUTER JOIN ProductTranslations tr
ON p.ID = tr.ProductId AND tr.Language = @Language
-- join default language of the product:
LEFT OUTER JOIN ProductTranslations def
ON p.ID = def.ProductId AND def.IsDefaultLanguage = 1
WHERE p.ID = @Id
如果存在该语言的翻译,则以所请求的语言返回产品的标题和描述。如果不存在翻译,则将返回默认语言的标题和说明。
对所有表的所有可翻译字段使用公共共享表
在上述方法中,转换表是父表的扩展。因此,ProductTranslation具有Product的所有可翻译字段。这是一个简洁快捷的方法,也很好。
但是有一个缺点(不确定它是否可以称为劣势)。如果有更多表需要可翻译字段,则需要许多新表。根据我的经验,我们采取了不同的方法。我们创建了一个用于翻译的通用表和一个链接表,用于将翻译链接到父表的可翻译字段。
所以我将使用前面的Product示例,它有两个字段标题和描述,可以翻译我们的方法。还要考虑具有字段名称和描述的另一个表ProductCategory,它们也需要翻译。
Product
(
ID: Integer
SKU: String
titleID: Integer // ID of LocalizableText record corresponding title
descriptionID: Integer // ID of LocalizableText record corresponding description
)
ProductCategory
(
ID: Integer
nameID: Integer // ID of LocalizableText record corresponding name
descriptionID: Integer // ID of LocalizableText record corresponding description
)
LocalizableText // This is nothing but a link table
{
ID: Integer
}
Translations //This is where all translations are stored.
{
ID: Integer
localizableTextID: Integer
language: String
text: String
}
要加载此数据,我正在使用不同的查询(修改上述内容)。例如。要使用@Language语言加载ID为@Id的产品,我将使用以下查询
SELECT
p.ID,
p.SKU,
-- get title, description from the requested translation,
-- or fall back to the default if not found:
Title.text Title,
description.text Description
FROM Products p
-- join requested translation for title, if available:
LEFT OUTER JOIN Translations title
ON p.titleID = title.localizableTextID
AND title.Language = @Language
-- join requested translation for description, if available:
LEFT OUTER JOIN Translations description
ON p.descriptionID = description.localizableTextID
AND description.Language = @Language
WHERE p.ID = @Id
此查询基于Product的各个字段没有默认转换的假设
类似的查询可用于从ProductCategory中获取记录
以上是关于多语言数据库,具有默认回退功能的主要内容,如果未能解决你的问题,请参考以下文章