多语言数据库的模式

时间:2008-11-25 09:13:26

标签: database-design localization multilingual

我正在开发一种多语言软件。就应用程序代码而言,可本地化不是问题。我们可以使用特定于语言的资源,并拥有适合他们的各种工具。

但是,定义多语言数据库模式的最佳方法是什么?假设我们有很多表(100或更多),每个表可以有多个可以本地化的列(大多数nvarchar列应该是可本地化的)。例如,其中一个表可能包含产品信息:

CREATE TABLE T_PRODUCT (
  NAME        NVARCHAR(50),
  DESCRIPTION NTEXT,
  PRICE       NUMBER(18, 2)
)

我可以考虑在NAME和DESCRIPTION列中支持多语言文本的三种方法:

  1. 每种语言的单独列

    当我们向系统添加新语言时,我们必须创建其他列来存储翻译后的文本,如下所示:

    CREATE TABLE T_PRODUCT (
      NAME_EN        NVARCHAR(50),
      NAME_DE        NVARCHAR(50),
      NAME_SP        NVARCHAR(50),
      DESCRIPTION_EN NTEXT,
      DESCRIPTION_DE NTEXT,
      DESCRIPTION_SP NTEXT,
      PRICE          NUMBER(18,2)
    )
    
  2. 包含每种语言列的翻译表

    不存储翻译文本,而是仅存储翻译表的外键。翻译表包含每种语言的列。

    CREATE TABLE T_PRODUCT (
      NAME_FK        int,
      DESCRIPTION_FK int,
      PRICE          NUMBER(18, 2)
    )
    
    CREATE TABLE T_TRANSLATION (
      TRANSLATION_ID,
      TEXT_EN NTEXT,
      TEXT_DE NTEXT,
      TEXT_SP NTEXT
    )
    
  3. 包含每种语言行的翻译表

    不存储翻译文本,而是仅存储翻译表的外键。翻译表仅包含一个键,而单独的表包含每种语言翻译的行。

    CREATE TABLE T_PRODUCT (
      NAME_FK        int,
      DESCRIPTION_FK int,
      PRICE          NUMBER(18, 2)
    )
    
    CREATE TABLE T_TRANSLATION (
      TRANSLATION_ID
    )
    
    CREATE TABLE T_TRANSLATION_ENTRY (
      TRANSLATION_FK,
      LANGUAGE_FK,
      TRANSLATED_TEXT NTEXT
    )
    
    CREATE TABLE T_TRANSLATION_LANGUAGE (
      LANGUAGE_ID,
      LANGUAGE_CODE CHAR(2)
    )
    
  4. 每个解决方案都有优缺点,我想知道您对这些方法的体验,您的建议是什么以及如何设计多语言数据库模式。

11 个答案:

答案 0 :(得分:108)

您如何为每个可翻译表格提供相关的翻译表?

  

创建表T_PRODUCT(pr_id int,PRICE NUMBER(18,2))

     

CREATE TABLE T_PRODUCT_tr(pr_id INT FK,languagecode varchar,pr_name text,pr_descr text)

这样,如果你有多个可翻译列,它只需要一个连接来获取它+因为你没有自动生成翻译,所以可能更容易将项目与相关翻译一起导入。

这方面的负面影响是,如果您有一个复杂的语言回退机制,您可能需要为每个转换表实现这一点 - 如果您依赖某些存储过程来执行此操作。如果你从应用程序那样做,这可能不是问题。

让我知道你的想法 - 我也将为我们的下一个申请做出决定。 到目前为止,我们已经使用了你的第三种类型。

答案 1 :(得分:47)

第三种选择是最好的,原因如下:

  • 不需要更改新语言的数据库架构(从而限制代码更改)
  • 对于未实现的语言或特定项目的翻译,不需要大量空间
  • 提供最大的灵活性
  • 您最终不会使用稀疏表格
  • 您不必担心空键并检查您是否显示现有翻译而不是某些空条目。
  • 如果您更改或扩展数据库以包含其他可翻译的项目/事物/等,您可以使用相同的表格和系统 - 这与其他数据非常不相关。

- 亚当

答案 2 :(得分:9)

看看这个例子:

PRODUCTS (
    id   
    price
    created_at
)

LANGUAGES (
    id   
    title
)

TRANSLATIONS (
    id           (// id of translation, UNIQUE)
    language_id  (// id of desired language)
    table_name   (// any table, in this case PRODUCTS)
    item_id      (// id of item in PRODUCTS)
    field_name   (// fields to be translated)
    translation  (// translation text goes here)
)

我认为没有必要解释,结构描述了自己。

答案 3 :(得分:8)

我通常会采用这种方法(不是实际的sql),这与你的最后一个选项相对应。

table Product
productid INT PK, price DECIMAL, translationid INT FK

table Translation
translationid INT PK

table TranslationItem
translationitemid INT PK, translationid INT FK, text VARCHAR, languagecode CHAR(2)

view ProductView
select * from Product
inner join Translation
inner join TranslationItem
where languagecode='en'

因为在一个地方拥有所有可翻译的文本使维护变得更加容易。有时翻译会外包给翻译机构,这样您就可以向他们发送一个大的导出文件,并轻松地将其导回。

答案 4 :(得分:3)

在了解技术细节和解决方案之前,您应该停一分钟并询问有关要求的几个问题。答案可能对技术解决方案产生巨大影响。这类问题的例子如下:
   - 是否会一直使用所有语言?
   - 谁和什么时候填写不同语言版本的列?    - 当用户需要某种语言的文本且系统中没有文本时会发生什么?    - 只有本地文本或其他项目(例如PRICE可以存储在$和€中,因为它们可能不同)

答案 5 :(得分:3)

我正在寻找本地化的一些技巧并找到了这个主题。 我想知道为什么使用它:

CREATE TABLE T_TRANSLATION (
   TRANSLATION_ID
)

所以你得到像user39603建议的东西:

table Product
productid INT PK, price DECIMAL, translationid INT FK

table Translation
translationid INT PK

table TranslationItem
translationitemid INT PK, translationid INT FK, text VARCHAR, languagecode CHAR(2)

view ProductView
select * from Product
inner join Translation
inner join TranslationItem
where languagecode='en'

你能不能把表格翻译出去,这样你就明白了:

    table Product
    productid INT PK, price DECIMAL

    table ProductItem
    productitemid INT PK, productid INT FK, text VARCHAR, languagecode CHAR(2)

    view ProductView
    select * from Product
    inner join ProductItem
    where languagecode='en'

答案 6 :(得分:1)

我同意随机发生器。我不明白为什么你需要一个表“翻译”。

我认为,这已经足够了:

TA_product: ProductID, ProductPrice
TA_Language: LanguageID, Language
TA_Productname: ProductnameID, ProductID, LanguageID, ProductName

答案 7 :(得分:1)

以下方法是否可行?假设您有超过1列需要翻译的表。因此,对于产品,您可以同时拥有产品名称和产品名称需要翻译的产品说明。你能做到以下几点:

CREATE TABLE translation_entry (
      translation_id        int,
      language_id           int,
      table_name            nvarchar(200),
      table_column_name     nvarchar(200),
      table_row_id          bigint,
      translated_text       ntext
    )

    CREATE TABLE translation_language (
      id int,
      language_code CHAR(2)
    )   

答案 8 :(得分:1)

document描述了可能的解决方案以及每种方法的优缺点。我更喜欢“行本地化”,因为添加新语言时不必修改数据库架构。

答案 9 :(得分:0)

“哪一个最好”是基于项目情况。第一个很容易选择和维护,性能也是最好的,因为它不需要在select实体时连接表。如果您确认您的项目仅支持2或3种语言,并且不会增加,则可以使用它。

第二个是好的,但很难理解和维护。而且表现比第一次差。

最后一个擅长可扩展性但性能不佳。 T_TRANSLATION_ENTRY表会变得越来越大,当你想要从某些表中检索实体列表时,这很糟糕。

答案 10 :(得分:0)

您需要记住,创建多语言数据库时,您要从产品表中切断诸如名称或描述之类的字段,并将其移至翻译后的资源中。

翻译后的资源可能是另一个表,例如在我的示例中,该表旨在与SQL视图一起使用,以简化查询并简化基础应用程序的开发

multilanguage database

我分开了LabelTranslations,因为这是一个包含网页上字段标签的全局翻译的表。您可以根据需要调用它,它们是无状态的,并且不依赖于特定的产品或类别。

ProductTranslations的CategoryTranslations是有状态的,这意味着“名称”的描述符将是实际的产品名称。

使用物化视图来获得比简单SQL视图更好的性能(需要付出存储空间的代价,并且需要在底层应用程序开发中进行更多工作来刷新它们),或者如果需要的话,可以使用更笨重的SQL视图。

要在 Postgres 中创建类别的物化视图:

CREATE MATERIALIZED VIEW VCategories AS (
    SELECT cat.id, lng.iso_639_1_code, ct.descriptor, ct.value
    FROM Categories cat
    JOIN CategoryTranslations ct ON ct.category_id = cat.id
    JOIN Languages lng ON lng.id = ct.language_id
);

查询ID为120的类别的每个翻译

SELECT * FROM VCategories WHERE id = 120 AND iso_639_1_code = 'en'

使用该应用程序的代码时,我觉得很方便,您可以编写一个非常简单的代码来查询翻译和搜索记录