我知道一个问题已经得到广泛讨论,但是我认为仍有一个方面需要澄清。
我正在创建一个具有多语言数据库的Web应用程序,我已经找到了一些很好的实践文章(例如 this),
因此,我决定使用一个包含项目ID的主表,以及一个包含每个项目的翻译的表,例如
Content ContentTranslation
要么
Category CategoryTranslation
等等。
现在我在做什么?我只是从数据库中获取所有翻译的项目,然后遍历每个翻译,以根据当前用户的本地来查找正确的翻译,如果找到正确的本地,则将其设置到页面的翻译的主对象中进行渲染,否则我只会得到标记为“默认”的翻译。
但是,在有大量对象和转换的情况下,服务器响应时间可能会增加,即使用户可能没有注意到,我也不希望这样。
因此,此用例是否也有良好的做法?例如,一些特定的查询说“选择语言环境为“ it”的翻译,但如果找不到,只需获取设置了“默认”标志的查询?
现在,对于该技术,我正在将Spring MVC与Hibernate和JPA结合使用(通过JPARepository)。
我的对象都扩展了我通过这种方式创建的基本Translatable类
@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”属性,在我的页面中,我接受了该内容,并且用户获得了所需的正确语言。
我希望我很清楚并且不会凌乱,但是如果您需要更多信息,我很乐意告诉您更多信息。
预先注意一些事项:
在我的应用程序中,根据使用情况,我使用两种不同的方法来加载多语言数据:
在用户输入数据或编辑现有数据(例如带有翻译的产品)的情况下,我使用的是与您在问题中上面显示的方法相同的方法,例如:
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映射器加载带有所有翻译的产品实例。然后,我遍历翻译并选择所需的翻译。
在这种情况下(主要是前端代码),我通常只向用户显示信息(最好以用户的语言显示),因此我使用了另一种方法:
首先,我使用的是不同的数据模型,该模型不支持/不知道多重翻译的概念。相反,它只是当前用户使用“最佳”语言表示的产品:
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,我将使用以下查询:
@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
如果存在该语言的翻译版本,则会以请求的语言返回产品的标题和说明。如果不存在翻译,则将返回默认语言的标题和描述。