代码之家  ›  专栏  ›  技术社区  ›  Wahid Bitar

如何支持数据库模式中的多语言方法?

  •  9
  • Wahid Bitar  · 技术社区  · 16 年前

    我希望我的数据库能够支持其表中所有文本值的多语言。

    那么,最好的方法是什么呢?.

    Eddi1::

    例如。

    我有一张“人”桌:

    ID int
    FirstName  nvarchar(20)
    LastName   nvarchar(20)
    Notes      nvarchar(max)
    BirthDate  date
    ...........
    

    所以,如果我希望我的程序支持新语言“让我们说法语”。

    我应该在每次添加新语言时添加新列吗? 所以我的“个人”桌会像这样

    ID int
    FirstName_en  nvarchar(20)
    FirstName_fr  nvarchar(20)
    LastName_en   nvarchar(20)
    LastName_fr   nvarchar(20)
    Notes_en      nvarchar(max)
    Notes_fr      nvarchar(max)
    BirthDate     date
    ...........
    

    或者我应该添加两个新表,一个用于语言,另一个用于“个人语言”值?

    所以这看起来像: “ 语言文字 “表:

    ID           int
    Lang-symbol  nvarchar(4)
    

    “ 人 表:

    ID         int
    BirthDate  Date
    

    最后“ 个人翻译 表:

    LangID        int
    PersonID      int
    Translation   nvarchar(max)
    

    或者有更好的东西?是吗?

    .

    2 回复  |  直到 14 年前
        1
  •  9
  •   Roland Bouman    16 年前

    我必须在问卷数据库中处理这个问题。多个问题需要翻译成多种语言(英语、日语、中文)。

    我们首先确定了将在问题表上打印出来的所有文本列。对于所有这些,我们需要能够存储翻译。对于每个具有需要翻译的文本列的表,我们随后创建了一个翻译表,其中有一个外键指向原始表的主键,一个外键指向我们的语言表,然后为每个需要翻译的文本字段创建一个Unicode列。在这些文本列中,我们将存储所需的每种语言的翻译。

    所以一个典型的查询应该是:

    select     p.id
    ,          pt.product_name
    ,          pt.product_description
    from       product                  p
    inner join product_translations pt
    on         p.id = pt.product_id
    and        'fr' = pt.language_code
    

    所以,总是只有一个额外的连接(对于每个表)来获得翻译。

    我要指出的是,我们只需要处理有限数量的表,所以维护一些额外的翻译表并不是一个大问题。

    我们确实考虑过为新语言添加列,但出于多种原因决定不添加。首先,尚不知道要支持的语言数量,但可能是大量的(10种、20种或更多)。结合大多数表至少有3个不同的人类可读列这一事实,我们必须添加许多文本列,这将导致行非常宽。所以我们决定不这样做。

    我们考虑的另一种方法是制作一个大的“标签”表,其中有列:

    (表名 ,表的ID_ 栏名 语言文字 ,翻译文本)

    有效地拥有一个表来存储数据库中任何地方的所有翻译。我们也反对这样做,因为这会使编写查询变得复杂(因为每个“普通”列都会在转换表中产生一行,这将有效地将已经很大的转换表多次连接到普通表(每个已转换列一次)。对于示例表,您将得到如下查询:

    select     product.id 
    ,          product_name.translated_text product_name
    ,          product_description.translated_text product_description
    from       product p
    inner join translations product_name
    on         p.id = product_name.id
    and        'product'      = product_name.table_name
    and        'product_name' = product_name.column_name
    and        'fr'           = product_name.language
    inner join translations product_description
    on         p.id = product_name.id
    and        'product'      = product_description.table_name
    and        'product_description' = product_description.column_name
    and        'fr'           = product_description.language
    

    如您所见,本质上类似于实体属性值设计,这使得查询变得很麻烦。

    最后一种方法的另一个问题是,如果不可能对翻译文本强制执行约束(在我们的例子中,主要是单一性约束),则会很困难。有了一个独立的翻译表,您可以轻松而清晰地解决这些问题。

        2
  •  2
  •   Sicco    14 年前

    我刚刚实现了一些非常有效的功能。

    像其他许多人一样,我没有一个干净的开始,所有的桌子都准备好存储英文文本。

    为了实现多语言数据库,我不太乐意将表或列的数量加倍。

    我决定使用XML将所有翻译存储到一个字段中:

    <text>
      <translation lang="EN-GB">good day</translation>
      <translation lang="FR-FR">bonjour</translation>
    </text>
    

    例如,我从一张只有英语的桌子开始:

    ProductId INT
    ProductName varchar(200)
    ProductDescription varchar(1000)
    

    然后我创建了多语言字段:

    ProductId INT
    ProductNameTranslation xml
    ProductDescriptionTranslation xml
    

    如果希望能够获取原始字段的只读值,只需添加持久化的计算列:

    ProductId INT
    ProductNameTranslation xml
    ProductDescriptionTranslation xml
    ProductNameDefaultLang = dbo.GetDefaultLanguage(ProductNameTranslation)
    ProductDescriptionDefaultLang = dbo.GetDefaultLanguage(ProductDescriptionTranslation)
    

    在业务/数据层中,XML字段转换为字典类型的业务类,键是语言枚举:

    ProductName {
      get {return this.ProductNameTranslation[selectedLanguage];}
      set {...}
    }
    

    这种方法允许我避免完全重新划分数据库。它还意味着与数据库的交互更少,保存一行而不是一个主行,以及3个翻译行。

    它还避免了必须处理主行没有翻译行的情况。

    应该注意,XML有一个模式和XML索引来提高性能。

    这种方法在您希望逐步进行语言转换(即一次一个字段)时非常有用。