我正在开发一个多语言软件。尽的应用程序代码,本地化是不是一个问题。我们可以使用特定语言的资源和各种工具的工作以及与他们。

但是,什么是最好的办法在界定一个多语言数据库架构?让我们说我们有很多的表(100个或更多的),并且每个表可以有多个列,可以进行本地化的(大多数的限列应本地化).例如,一个表可能保持产品的信息:

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

我能想到的三种办法,以支持多种语言的文本的名称和说明列:

  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)
    )
    

有利弊每个解决方案,我想知道什么是您的经验与这些方法,你有什么建议你会怎么去设计一个多语言数据库架构。

有帮助吗?

解决方案

什么你认为有关具有相关的转换表,对每一翻译的表?

CREATE TABLE T_PRODUCT(pr_id int、价格数(18,2))

CREATE TABLE T_PRODUCT_tr(pr_id INT FK、语言代码varchar pr_name文本,pr_descr文本)

这样,如果你有多个翻译柱,它将只需要一个单一的加入得到它+因为你不是autogenerating一translationid它可能更易于进口物品在一起,与他们相关的翻译。

负面是,如果你有一个复杂的语言后退机构可能需要执行,对每一翻译表-如果你依靠在一些存储程序做到这一点。如果你这样做,从程序,这将可能不是一个问题。

让我知道你在想什么-我也要做一个决定在这个对于我们下一步应用程序。迄今为止我们使用你的3种类型。

其他提示

这是一个有趣的问题,因此,让我们necromance.

让我们开始该问题的方法1:
问题:你将拯救的速度。
在SQL(除PostGreSQL与hstore),可以不通过一个参数的语言,并且说:

SELECT ['DESCRIPTION_' + @in_language]  FROM T_Products

所以你必须要做到这一点:

SELECT 
    Product_UID 
    ,
    CASE @in_language 
        WHEN 'DE' THEN DESCRIPTION_DE 
        WHEN 'SP' THEN DESCRIPTION_SP 
        ELSE DESCRIPTION_EN 
    END AS Text 
FROM T_Products 

这意味着你必须要改变所有的查询,如果添加了新的语言。这自然导致采用"动态SQL",所以你不必改变所有的查询。

这通常会导致这样的事情(而这不能被用于意见或表值功能的办法,这真的是一个问题,如果实际上,你需要过滤器的报告的日期)

CREATE PROCEDURE [dbo].[sp_RPT_DATA_BadExample]
     @in_mandant varchar(3) 
    ,@in_language varchar(2) 
    ,@in_building varchar(36) 
    ,@in_wing varchar(36) 
    ,@in_reportingdate varchar(50) 
AS
BEGIN
    DECLARE @sql varchar(MAX), @reportingdate datetime

    -- Abrunden des Eingabedatums auf 00:00:00 Uhr
    SET @reportingdate = CONVERT( datetime, @in_reportingdate) 
    SET @reportingdate = CAST(FLOOR(CAST(@reportingdate AS float)) AS datetime)
    SET @in_reportingdate = CONVERT(varchar(50), @reportingdate) 

    SET NOCOUNT ON;


    SET @sql='SELECT 
         Building_Nr AS RPT_Building_Number 
        ,Building_Name AS RPT_Building_Name 
        ,FloorType_Lang_' + @in_language + ' AS RPT_FloorType 
        ,Wing_No AS RPT_Wing_Number 
        ,Wing_Name AS RPT_Wing_Name 
        ,Room_No AS RPT_Room_Number 
        ,Room_Name AS RPT_Room_Name 
    FROM V_Whatever 
    WHERE SO_MDT_ID = ''' + @in_mandant + ''' 

    AND 
    ( 
        ''' + @in_reportingdate + ''' BETWEEN CAST(FLOOR(CAST(Room_DateFrom AS float)) AS datetime) AND Room_DateTo 
        OR Room_DateFrom IS NULL 
        OR Room_DateTo IS NULL 
    ) 
    '

    IF @in_building    <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Building_UID  = ''' + @in_building + ''') '
    IF @in_wing    <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Wing_UID  = ''' + @in_wing + ''') '

    EXECUTE (@sql) 

END


GO

这个问题是
a)日期格式是非常特定语言,所以您得到的一个问题有,如果你不输入in ISO format(平均花园各种程序通常不这样做,并在情况的报告的用户一定如地狱不会为你做的,即使明确指示这样做)。

b) 最显着的, 您 松散任何种类的语法检查.如果 <insert name of your "favourite" person here> 改变模式,因为突然的要求改变机翼和新的表格创建的老人离开,但参考场更名,你不得到任何类型的警告。报告甚至可以 当你碰它没有选择翼参数 (==>guid。空)。但突然,当一个实际的用户实际上选择一个翼==> 繁荣. 这种方法完全breakes任何种类的检验。


方法2:
一言以蔽之:"大"的概念(报警-讽刺),让我们结合起来的缺点的方法3(缓慢的速度的时候,许多条目)与相当可怕的缺点的方法1。
只有利用这一方法是你把所有的翻译中有一个表,因此使维护简单。但是,同样的事情可以实现的方法1和一个动态SQL储存过程,并(有可能是临时的)表格含有翻译和名称的目标表(而是相当简单的假设你命你的所有文本的领域相同)。


方法3:
一个表所有翻译:缺点:你有储存n外键的产品表n场你想要翻译。因此,你必须做n加入为n领域。当翻译表是全球性的,它具有许多条目,并加入成为缓慢。还有,你总是要加入T_TRANSLATION表n次n领域。这是一个非常开销。现在,你是做什么的时候你必须适应自定义的翻译每客户?你就必须增加另一个2倍n加入到一个额外的表格。如果你要加入,说10表,与2x2xn=4n额外的加入,多么乱七八糟的!此外,这种设计使得能够使用相同的翻译有2表。如果我改变的项目名称在一个表,我真的想要改变的一个条目中另一个表,以及每一个时间?

再加上你可以不删除,并重新插入的表格了,因为现在有外键产品表中(s)...你当然可以省略设定的驻,然后 <insert name of your "favourite" person here> 可以删除的表格,并重新插入所有项目与 newid() [或通过指定id在插入的,但有 身入关],并将(将)导致数据的垃圾(null参考的例外)真的很快。


方法4(未列出):存储所有语言中的一种XML领域的数据库。e.克

-- CREATE TABLE MyTable(myfilename nvarchar(100) NULL, filemeta xml NULL )


;WITH CTE AS 
(
      -- INSERT INTO MyTable(myfilename, filemeta) 
      SELECT 
             'test.mp3' AS myfilename 
            --,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body>Hello</body>', 2) 
            --,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body><de>Hello</de></body>', 2) 
            ,CONVERT(XML
            , N'<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<lang>
      <de>Deutsch</de>
      <fr>Français</fr>
      <it>Ital&amp;iano</it>
      <en>English</en>
</lang>
            ' 
            , 2 
            ) AS filemeta 
) 

SELECT 
       myfilename
      ,filemeta
      --,filemeta.value('body', 'nvarchar') 
      --, filemeta.value('.', 'nvarchar(MAX)') 

      ,filemeta.value('(/lang//de/node())[1]', 'nvarchar(MAX)') AS DE
      ,filemeta.value('(/lang//fr/node())[1]', 'nvarchar(MAX)') AS FR
      ,filemeta.value('(/lang//it/node())[1]', 'nvarchar(MAX)') AS IT
      ,filemeta.value('(/lang//en/node())[1]', 'nvarchar(MAX)') AS EN
FROM CTE 

然后你可以得到的价值XPath查询在SQL,在那里你可以把串的变量

filemeta.value('(/lang//' + @in_language + '/node())[1]', 'nvarchar(MAX)') AS bla

你可以更新的数值是这样的:

UPDATE YOUR_TABLE
SET YOUR_XML_FIELD_NAME.modify('replace value of (/lang/de/text())[1] with "&quot;I am a ''value &quot;"')
WHERE id = 1 

在这里你可以替换 /lang/de/...'.../' + @in_language + '/...'

有点像PostGre hstore,除了由于开销的分析XML(而不是读一项从联阵在PG hstore)它变得过于缓慢加上xml编码使得这太痛苦是有用的。


5的方法(作为建议通过SunWuKung,一个你应该选择):一个翻译表的每个"产品"表。这意味着一个行为每种语言,和几个"文本"领域,因此它仅需要一个(左)加入上N领域。然后你可以很容易地添加一个默认场中的"产品"表,你可以容易地删除,并重新插入的转换表,并且你可以创建一个二表定的翻译(在需求),也可以删除,并重新插入),和你还有所有的外键。

让我们做一个例子来看看这个工作:

第一,创建表:

CREATE TABLE dbo.T_Languages
(
     Lang_ID int NOT NULL
    ,Lang_NativeName national character varying(200) NULL
    ,Lang_EnglishName national character varying(200) NULL
    ,Lang_ISO_TwoLetterName character varying(10) NULL
    ,CONSTRAINT PK_T_Languages PRIMARY KEY ( Lang_ID )
);

GO




CREATE TABLE dbo.T_Products
(
     PROD_Id int NOT NULL
    ,PROD_InternalName national character varying(255) NULL
    ,CONSTRAINT PK_T_Products PRIMARY KEY ( PROD_Id )
); 

GO



CREATE TABLE dbo.T_Products_i18n
(
     PROD_i18n_PROD_Id int NOT NULL
    ,PROD_i18n_Lang_Id int NOT NULL
    ,PROD_i18n_Text national character varying(200) NULL
    ,CONSTRAINT PK_T_Products_i18n PRIMARY KEY (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id)
);

GO

-- ALTER TABLE dbo.T_Products_i18n  WITH NOCHECK ADD  CONSTRAINT FK_T_Products_i18n_T_Products FOREIGN KEY(PROD_i18n_PROD_Id)
ALTER TABLE dbo.T_Products_i18n  
    ADD CONSTRAINT FK_T_Products_i18n_T_Products 
    FOREIGN KEY(PROD_i18n_PROD_Id)
    REFERENCES dbo.T_Products (PROD_Id)
ON DELETE CASCADE 
GO

ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO

ALTER TABLE dbo.T_Products_i18n 
    ADD  CONSTRAINT FK_T_Products_i18n_T_Languages 
    FOREIGN KEY( PROD_i18n_Lang_Id )
    REFERENCES dbo.T_Languages( Lang_ID )
ON DELETE CASCADE 
GO

ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO



CREATE TABLE dbo.T_Products_i18n_Cust
(
     PROD_i18n_Cust_PROD_Id int NOT NULL
    ,PROD_i18n_Cust_Lang_Id int NOT NULL
    ,PROD_i18n_Cust_Text national character varying(200) NULL
    ,CONSTRAINT PK_T_Products_i18n_Cust PRIMARY KEY ( PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id )
);

GO

ALTER TABLE dbo.T_Products_i18n_Cust  
    ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Languages 
    FOREIGN KEY(PROD_i18n_Cust_Lang_Id)
    REFERENCES dbo.T_Languages (Lang_ID)

ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Languages

GO



ALTER TABLE dbo.T_Products_i18n_Cust  
    ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Products 
    FOREIGN KEY(PROD_i18n_Cust_PROD_Id)
REFERENCES dbo.T_Products (PROD_Id)
GO

ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Products
GO

然后填写数据

DELETE FROM T_Languages;
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (1, N'English', N'English', N'EN');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (2, N'Deutsch', N'German', N'DE');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (3, N'Français', N'French', N'FR');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (4, N'Italiano', N'Italian', N'IT');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (5, N'Russki', N'Russian', N'RU');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (6, N'Zhungwen', N'Chinese', N'ZH');

DELETE FROM T_Products;
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (1, N'Orange Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (2, N'Apple Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (3, N'Banana Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (4, N'Tomato Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (5, N'Generic Fruit Juice');

DELETE FROM T_Products_i18n;
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 1, N'Orange Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 2, N'Orangensaft');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 3, N'Jus d''Orange');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 4, N'Succo d''arancia');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 1, N'Apple Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 2, N'Apfelsaft');

DELETE FROM T_Products_i18n_Cust;
INSERT INTO T_Products_i18n_Cust (PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id, PROD_i18n_Cust_Text) VALUES (1, 2, N'Orangäsaft'); -- Swiss German, if you wonder

然后查询的数据:

DECLARE @__in_lang_id int
SET @__in_lang_id = (
    SELECT Lang_ID
    FROM T_Languages
    WHERE Lang_ISO_TwoLetterName = 'DE'
)

SELECT 
     PROD_Id 
    ,PROD_InternalName -- Default Fallback field (internal name/one language only setup), just in ResultSet for demo-purposes
    ,PROD_i18n_Text  -- Translation text, just in ResultSet for demo-purposes
    ,PROD_i18n_Cust_Text  -- Custom Translations (e.g. per customer) Just in ResultSet for demo-purposes
    ,COALESCE(PROD_i18n_Cust_Text, PROD_i18n_Text, PROD_InternalName) AS DisplayText -- What we actually want to show 
FROM T_Products 

LEFT JOIN T_Products_i18n 
    ON PROD_i18n_PROD_Id = T_Products.PROD_Id 
    AND PROD_i18n_Lang_Id = @__in_lang_id 

LEFT JOIN T_Products_i18n_Cust 
    ON PROD_i18n_Cust_PROD_Id = T_Products.PROD_Id
    AND PROD_i18n_Cust_Lang_Id = @__in_lang_id

如果你懒,然后你还可以使用的ISO-TwoLetterName('DE','EN',etc.) 作为主键的语言表,然后你不必查询语言身份证。但是如果你这样做的话,你也许想要使用 IETF-语言标记 相反,这是好的,因为你得到的de-CH和德-德,这真的是不同ortography明智的(双s而不是β处),虽然它同基地语言。这只是作为一个小小的细节,可以对你很重要,特别是考虑到en-US和en-GB/en-CA/en-AU或fr-FR/fr-CA有类似的问题。
引用:我们不需要它,我们只做我们的软件为英文。
回答:是的-但哪一个??

无论如何,如果使用整数ID,你是灵活的,可以改变你的方法在以后的任何时间。
你应该使用这整数,因为没有更讨厌的、破坏性和麻烦比一个拙劣的Db设计。

也参看 RFC5646, ISO639-2,

而且,如果你还说"我们" 让我们应用程序"只 一个 文化"(如en-我们通常的),因此,我不需要额外的整数,这将是一个很好的时间和地点来说 IANA语言标记, ,不是吗?
因为他们是这样的:

de-DE-1901
de-DE-1996

de-CH-1901
de-CH-1996

(有一个正字法改革在1996年...) 试图找到一词在字典如果它是错误拼写;这变得非常重要的应用程序中处理法律和公共服务的门户网站。
更重要的是,在有些地区正在改变从西里尔字母的拉丁字母,这可能只是更多的麻烦比肤浅的骚扰的一些模糊的拼写法改革,这就是为什么这可能是一个重要的考虑,也取决于哪个国家。一种方式或其他,这是最好有整数在那里,只是在情况下...

编辑:
并通过加入 ON DELETE CASCADE

REFERENCES dbo.T_Products( PROD_Id )

你可以简单地说: DELETE FROM T_Products, 和没有得到任何外国的关键的违反。

作为对排序规则,我会做这样的:

A)拥有自己的DAL
B)保存所需的排序规则的名字的语言表

你可能希望把排序规则在他们自己的表格,例如:

SELECT * FROM sys.fn_helpcollations() 
WHERE description LIKE '%insensitive%'
AND name LIKE '%german%' 

C)安排序规则的姓名可以在你的身份验证。用户。语言信息

D)编写SQL这样的:

SELECT 
    COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName 
FROM T_Groups 

ORDER BY GroupName COLLATE {#COLLATION}

E)然后,你可以做到这一点在达尔:

cmd.CommandText = cmd.CommandText.Replace("{#COLLATION}", auth.user.language.collation)

然后给你这个完美的组成SQL Query

SELECT 
    COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName 
FROM T_Groups 

ORDER BY GroupName COLLATE German_PhoneBook_CI_AI

第三个选择是最好的几个原因:

  • 不需要改变数据库架构,为新的语言(并因此限制的代码变化)
  • 不需要很多的空间用于未得到执行的语言或翻译的特定项目
  • 提供最大的灵活性
  • 你不会结束与疏表
  • 你不必担心空键和检查,你是显示一个现有的翻译,而不是一些空入境。
  • 如果你改变或扩大数据库以包含其他翻译项目/东西/等你可以使用相同的表格和系统-这是非常脱离的其余部分数据。

-亚当

看看这个例子:

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)
)

我认为没有必要解释、结构阐述了本身。

我通常会再为这个方法(不实际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'

因为所有的翻译文本在一个地方使得维护,因此容易得多。有时翻译是外包翻译机构,这样你可以送他们只是一个大出口文件,以及进口回它仅仅是作为容易。

之前要的技术细节和解决方案,则应停止一分钟和问几个问题有关的要求。答案可以产生巨大影响的技术解决方案。这种实例的问题将是:
-将所有语言被使用所有的时间?
-谁以及何时将填补所列的不同语言版本?
-发生什么事情时用户将需要一定的语言的文本并没有在系统?
-只有文字都是局部的或还有其他的项目(例如价格可以储存的美元和欧元,因为他们可能是不同的)

我在找一些提示,对于本地化和发现这个问题。我想知道为什么这被用于:

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'

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

我认为,这是不够的:

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

将以下做法是可行的?说你已经表,其中超过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)
    )   

"哪个是最好的"是根据该项目的情况。第一个是容易的选择和保持,并且还表现是最好的,因为它不需要参加表当选的实体。如果你确认你的poject仅仅支持2或3种语言,它将不会增加,可以使用它。

第二个是好的,但是很难理解和维护。而且性能比第一个。

最后一个是好的伸缩性,但糟糕的表现。该T_TRANSLATION_ENTRY表将成为更大的和更大,这是可怕的,当你想要检索名单的实体从一些表格。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top