多语言数据库,默认回退

我有一个问题,我知道已经被广泛讨论,但在我看来,还有一个方面还需要澄清。

我正在用多语言数据库创建一个Web应用程序,我已经找到了一些好的实践文章(比如这个),并在这里以堆栈溢出的方式回答这个问题。

所以我决定使用一个带有我的物品ID和另一个表格以及每个物品的翻译的主表格,比方说,比如说

Content
ContentTranslation

要么

Category
CategoryTranslation

等等。

现在我在做什么? 我只是从数据库中获取所有翻译的项目,然后遍历每个翻译以根据当前用户的本地查找正确的翻译,并且如果找到了正确的本地我设置为主页面的翻译页面渲染,否则我只是得到标记为“默认”的翻译。

尽管有大量的对象和翻译,但服务器响应时间可能会增长,即使用户可能没有注意到,我也不想这样做。

那么,这个用例还有什么好的做法吗? 例如,一些特定的查询说“选择带语言环境的翻译”it“,但如果你没有找到它只是得到一个与”默认“标志设置?

现在,我正在使用Hibernate和JPA的Spring MVC技术(通过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”属性的页面,在我的页面中,我只是按照目标获取正确的语言。

我希望我已经清楚,不要乱,但如果你需要更多的信息,我会很乐意告诉你更多。


一些笔记提前:

  • 我的回答更多的是我对这个问题的回答,在那里你添加了一个评论,然后导致这个问题
  • 在我的回答中,我使用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映射程序加载附加所有翻译的产品实例。 然后我遍历翻译并挑选需要的翻译。

    前端/只读

    在这种情况下,主要是前端代码,我通常只是向用户显示信息(最好用用户的语言),我使用的是不同的方法:

    首先,我使用不支持/知道多个翻译概念的不同数据模型。 相反,它只是当前用户对“最佳”语言的产品的表示:

    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;}
    }
    

    要加载这些数据,我使用了不同的查询(或存储过程)。 例如,要用语言@Language加载ID @Id的产品,我会使用以下查询:

    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具有产品的所有可翻译字段。 这是一个整洁而快速的方法,也是一个不错的方法。

    但有一个缺点(不确定是否可以称为劣势)。 如果更多表需要可翻译字段,则需要许多新表。 根据我的经验,我们采取了不同的方法。 我们创建了一个用于翻译的通用表格和一个将翻译链接到父表格的可翻译字段的链接表。

    因此,我将使用前面的产品示例,其中有两个字段的标题和说明可以翻译来解释我们的方法。 另请考虑另一个带有字段名称和说明的表格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
    

    此查询基于产品的各个字段没有默认翻译的假设

    类似的查询可以用来从ProductCategory获取记录

    链接地址: http://www.djcxy.com/p/37503.html

    上一篇: Multi language database, with default fallback

    下一篇: language data in JSON (MySQL)