多語言系統的數據庫設計


之前做的項目涉及到中國大陸和紐倫新港的用戶使用,也就需要做成一個多語言的系統,現在總結下其中一些經驗和思考。

首先我們需要確認我們要做的系統,多語言到底是要做多少種語言,以后會不會要求增加更多的語言。比如我們做一個給中國大陸和紐倫新港使用的系統,可以確定的語言就是簡體中文、繁體中文和英語,而且可以確定以后也不會增加語言。確定以后是否需要增加語言這一點很重要,決定了我們在數據庫設計時,是否需要考慮多語上的擴展性。

先說下在數據庫設計時,可以有以下方案實現多語:

一、為每個多語字段建立對應語言的字段列。

比如我們有一個客戶表,記錄了客戶Id、客戶名稱、客戶地址、客戶電話等,其中客戶名稱和客戶地址是多語的,而且需要支持簡體中文、繁體中文和英語,於是我們可以將客戶表設計如下:

create table Client
(
    ClientId int primary key,
    NameChs nvarchar(50),
    NameCht nvarchar(50),
    NameEng varchar(200),
    AddressChs nvarchar(50),
    AddressCht nvarchar(50),
    AddressEng varchar(200),
    TelephoneNumber varchar(50)
)

這樣做的優點是容易理解,容易查詢,一個客戶實例對應的就是數據庫中的一條數據,與普通的非多語數據庫無異,而且由於沒有形成新的表,所以也不需要額外的Join,所以查詢效率很高:

insert into Client values(1,'工商銀行','工商銀行','ICBC','中國北京','中國北京','China,Beijing','13811255555');

select *
from Client c
where c.ClientId=1

二、建立統一的翻譯表,在翻譯表中使用多列存儲多語言,然后在實體表中外鍵引用翻譯表。

create table Translation 
(
    TranslationId int primary key,
    TextChs nvarchar(200),
    TextCht nvarchar(200),
    TextEng varchar(200),
)

create table Client
(
    ClientId int primary key,
    NameTranId int references Translation(TranslationId),
    AddressTranId int references Translation(TranslationId),
    TelephoneNumber varchar(200)
)

這樣要查詢數據時,需要將Translation表JOIN2次,獲得對應的Name和Address的多語。

insert into Translation values(10,'工商銀行','工商銀行','ICBC');
insert into Translation values(20,'中國北京','中國北京','China,Beijing');
insert into Client values(1,10,20,'13811255555');

select c.ClientId,c.TelephoneNumber,
tn.TextChs as NameChs,tn.TextCht as NameCht,tn.TextEng as NameEng,
ta.TextChs as AddressChs,ta.TextCht as AddressCht,ta.TextEng as AddressEng
from Client c
inner join Translation tn
on c.NameTranId=tn.TranslationId
inner join Translation ta
on c.AddressTranId=ta.TranslationIdwhere 
where c.ClientId=1

以上介紹的方法都是將多語作為列輸出,也就是說有多少種語言就會有多少對於的列,不利於語言的增加下面再介紹將語言以數據行的形式保存的設計方法,這種方法可以在后期任意增加語言而不改動數據庫Schema.

三、將每個表中需要多語的字段獨立出來,形成一個對應的多語表。

多語表外鍵關聯原表,每個需要多語的字段在多語表中對應一列,多語表中增加“語言”字段。同樣以Client表為例,那么對應的表結構是:

create table Client
(
    ClientId int primary key,
    TelephoneNumber varchar(200)
)
create table Client_MultiLanguages
(
    CLId int primary key,
    ClientId int references Client(ClientId),
    Name nvarchar(200),
    Address nvarchar(200),
    Language char(3)
)

這樣的優點是便於擴展,在Schema中並沒有定義具體的語言,所以如果要增加語言的話,只需要在多語表中增加一行對應的數據即可。查詢也相對比較簡單,執行要將原表與對應的多語表JOIN,然后跟上具體的語言作為WHERE條件,即可完成對數據的查詢,比如要查詢Id為1的Client對象的英語實例:

insert into Client values(1,'13811255555');
insert into Client_MultiLanguages values(1,1,'工商銀行','中國北京','CHS');
insert into Client_MultiLanguages values(2,1,'工商銀行','中國北京','CHT');
insert into Client_MultiLanguages values(3,1,'ICBC','China,Beijing','ENG');

select c.*,cm.Name,cm.Address
from Client c inner join Client_MultiLanguages cm
on c.ClientId=cm.ClientId
where c.ClientId=1 and cm.Language='ENG'

 

四、建立統一翻譯表和對應的多語表,在每個多語列指向翻譯表。

create table Translation 
(
    TranslationId int primary key
)

create table Client
(
    ClientId int primary key,
    NameTranId int references Translation(TranslationId),
    AddressTranId int references Translation(TranslationId),
    TelephoneNumber varchar(200)
)


create table TranslationEntity 
(
    TranslationEntityId int primary key,
    TranslationId int references Translation(TranslationId),
    Language char(3),
    TranslatedText nvarchar(200)
)

如果要查詢Id為1的Client對應的英語實例,那么腳本為:

insert into Translation values(10);
insert into Translation values(20);
insert into Client values(1,10,20,'13811255555');
insert into TranslationEntity values(1,10,'CHS','工商銀行');
insert into TranslationEntity values(2,10,'CHT','工商銀行');
insert into TranslationEntity values(3,10,'ENG','ICBC');
insert into TranslationEntity values(4,20,'CHS','中國北京');
insert into TranslationEntity values(5,20,'CHT','中國北京');
insert into TranslationEntity values(6,20,'ENG','China,Beijing');


select c.ClientId,tne.TranslatedText as Name,tae.TranslatedText as Address,c.TelephoneNumber
from Client c
inner join TranslationEntity tne
on c.NameTranId=tne.TranslationId
inner join TranslationEntity tae
on c.AddressTranId=tae.TranslationId
where c.ClientId=1 and tne.Language='ENG' and tae.Language='ENG'

這個數據的插入和查詢也太復雜了。同時也可以注意到在查詢時根本沒有用到Translation表,其實這個表只是標識每個數據實例中的多語字段,可以直接使用數據庫的Sequence生成或者使用GUID,只要保證全局唯一即可。另外也可以注意到在查詢時JOIN了2次TranslationEntity 表,如果一個表的多語字段比較多,比如有10個字段有多語,那么查詢是就需要JOIN10次,這個效率會很低。另外還可以注意到,在WHERE條件中寫了2次Language='ENG',如果多個多語字段,那么就要寫多次。剛才這個查詢寫的不夠嚴謹,因為不能保證Name字段和Address字段必然就有英文值,如果沒有英文值會導致查詢結果為空,所以正確的寫法應該是:

select c.ClientId,tne.TranslatedText as Name,tae.TranslatedText as Address,c.TelephoneNumber
from Client c
left join TranslationEntity tne
on c.NameTranId=tne.TranslationId  and tne.Language='ENG'
left join TranslationEntity tae
on c.AddressTranId=tae.TranslationId and tae.Language='ENG'
where c.ClientId=1 

實際項目中,如果我們使用了NHibernate等ORMapping工具,那么多語字段就會映射成一個集合,所以對於某種語言的實例,那么需要執行N+1次SQL查詢,而不是JOIN查詢,N是該對象中多語的屬性個數.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM