Lưu dữ liệu đa ngôn ngữ trong Database

trong danh mục PHP, Web Programming

multilanguage-banner
Chắc hẳn trong quá trình triển khai các ứng dụng web cho doanh nghiệp, sẽ không ít lần bạn đối mặt với vấn đề đa ngôn ngữ. Đa ngôn ngữ có 2 dạng chính là đa ngôn ngữ giao diện (Template) và đa ngôn ngữ dữ liệu. Vấn đề đa ngôn ngữ giao diện thì không khó triển khai và dễ bắt gặp trong nhiều ứng dụng. Trong phạm vi bài viết này, mình sẽ giới thiệu tới các bạn 2 cách phổ biến để triển khai đa ngôn ngữ cho dữ liệu lưu trữ.

Vấn đề lưu trữ dữ liệu đa ngôn ngữ đối với các bạn chưa quen thì thường để triển khai 1 chức năng nào đó, ví dụ như sản phẩm, mỗi ngôn ngữ sẽ cần 1 cột trong database để lưu. Nếu như có M dữ liệu cần lưu theo ngôn ngữ trong 1 table và có N ngôn ngữ cần triển khai thì bạn cần ít nhất MxN cột trong table đó để lưu dữ liệu. Điều này sẽ tốt với ứng dụng 1 hoặc 2 ngôn ngữ và số lượng ngôn ngữ là thông dụng và hầu như không thay đổi như là VN và EN. Tuy nhiên triển khai như vậy sẽ không tốt về mặt thiết kế, khả năng mở rộng không có và viết truy vấn sẽ hơi phức tạp một tí vì phải select tương ứng cột mà mình muốn (sẽ phải thao tác trên tên cột). Dưới đây là hình minh họa table lưu thông tin sản phẩm ở 4 ngôn ngữ theo cách này.
multilanguage-database-table1

Ngoài giải pháp này, còn có 1 giải pháp khá phổ biến và được sử dụng rộng rãi đó là sử dụng mô hình table kết hợp để kết nối ngôn ngữ và dữ liệu cần lưu. Mô hình này phức tạp hơn một xíu, tuy nhiên nó giúp cho ứng dụng đa ngôn ngữ được mở rộng tối đa, có bao nhiêu ngôn ngữ thì tùy và nếu có thêm hay bớt 1 ngôn ngữ thì không phải thay đổi table nào cả nên hoàn toàn là thao tác trên dữ liệu. Dưới đây là hình minh họa table lưu thông tin như trên nhưng sử dụng mô hình kết hợp table.
multilanguage-database-table2

Đối với mô hình table kết hợp, để lấy được dữ liệu để hiển thị cho 1 ngôn ngữ thì chỉ cần INNER JOIN thêm 2 table là table kết hợp và table language để lấy ra đúng record.
VD:
SELECT *
FROM product p
INNER JOIN product_language pl ON p.p_id = pl.p_id
INNER JOIN language l ON l.l_id = pl.l_id AND l.l_code = "vn"

Hãy thử triển khai theo cách này xem, bạn sẽ thấy việc thêm bớt ngôn ngữ trong phần dữ liệu trong ứng dụng của mình sẽ nhẹ nhàng hơn và code mang tính portability khá cao và bất chấp ngôn ngữ và số lượng ngôn ngữ. Chúc vui.

33 bình luận

  1. Quang Huỳnh says:

    Viết khá nhẹ nhàng và có nội dung. Đã viết đến đây thì viết luôn phương án lập trình trong admin khi mún add item có multilanguage đi 🙂
    P/S: Cái web này phối màu ko chuẩn nè: ko thấy cursor khi compose cái comment này ở đâu hết =))

    • viet_it_pro says:

      @Quang Huỳnh,
      Bài bình luận thật là sôi nối.nhưng mình thấy cách 3 là cách login nhất,minh đã làm nhiều dự án theo cách này.Nhưng hơi mệt ở chỗ là update 2 bàng,nếu dùng cách này thì ko khách hàng nào phàn nàn về tính login.
      nhược điếm cùa cách 2 là web chậm,vi kết bàng nhiều.
      demo:http://www.cdpd.edu.vn/
      ai muốn share cơ sơ dư liệu mẫu pm:
      quocviet_maiyeuem2008@yahoo.com

      • viet_it_pro says:

        @viet_it_pro,

        cau truy van mau:
        //$query = “SELECT cs.id, csd.name, csd.seo_name FROM category_sub_desc csd ”
        // . “\n INNER JOIN category_sub cs ON csd.category_sub_id = cs.id ”
        // . “\n WHERE cs.published = 1 AND csd.language_id =%s AND cs.category_id=%s”
        // . “\n ORDER BY cs.ordering”
        //

      • viet says:

        @viet_it_pro,

        bạn nào cần source đa ngôn ngữ viết bàng c#
        -da ngon ngu noi dung,da ngon ngu giao dien,admin co du chuc nang them,cap nhat,active ,khoa lai,chon het ,bỏ chọn,xóa,xóa het,viet mo hinh 3 lớp,ADO,dong goi dll.
        có thể liên lạc với mình qua yahoo.
        Du lieu SQL theo mo hình 3 bang nhu tren..
        (code xịn nên không share free..hi)

      • viet says:

        @viet,
        nhap vao 2 la co chuyen ngon ngu.
        quocviet_maiyeuem2008@yahoo.com

  2. dongmt says:

    Bác Admin này có vẻ trùm lập trình PHP quá, hôm nào mang khăn gói tới mong bác chỉ vài chiêu.
    Em làm chủ yếu bên qindows form + qls server thui.

  3. Duy Tuyên says:

    Cách 1 nếu chỉ có 2 ngôn ngữ thì ok, nhiều hơn 2 thì mệt mỏi vì nhiều column quá!

    Cách 2 đúng chuẩn CSDL nhưng phát sinh nhiều table và khi truy vấn dữ liệu cũng dài dòng hơn. Khi thêm và sửa phải update 2 table.

    Cách 3:

    Trong mỗi table, đưa thêm cột lang. Ngôn ngữ lưu vào 1 array và để ở file config (để sau này có thêm ngôn ngữ cũng dễ dàng).

    ‘vn’ => ‘Tiếng Việt’,
    ‘en’ => ‘Tiếng Anh’

    Khi select thì chỉ việc thêm: where lang = ‘mã ngôn ngữ’. Khi thêm với sửa thì chỉ update 1 table!

    Mình đã từng xài cả 3 cách và hiện nay thì trung thành với cách 3!

    • admin says:

      Cảm ơn bạn đã chia sẽ. Cái cách 3 hình như có chỗ mình chưa hiểu.

      Theo mình nghĩ cách 3 của bạn có phải vậy không nhé:

      NEWS TABLE
      ID Title Contents Lang Datecreated

      Tiếng Việt:
      1 Cocacola Không có gì VN 1/1/2010

      Tiếng Anh:
      1 Coke Nothing EN 1/1/2010

      Vậy là duplicate data à? VD insert 2 ngôn ngữ, tức là sẽ có 2 row có 1 số trường y chang nhu ID, date create…mà ID trùng thì làm sao tạo được Primary key?

      Nếu mình hiểu sai thì đính chính giùm nhé :D. Thanks

  4. Duy Tuyên says:

    Làm sao ID có thể trùng nhau được khi cột đó ta cho nó là auto number tự tăng?

    • admin says:

      Hi, vậy hóa ra không phải là 1 tin 2 ngôn ngữ mà chính là 2 tin :D, vì 2 ID khác nhau chính là 2 tin rồi, như vậy các thao tác Edit, delete thì sao đây ta ??? Theo mình hiểu thì ứng dụng này không phải là phiên bản 1 tin có 2 ngôn ngữ mà là 2 phiên bản khác nhau cho 2 website khác nhau, một bên hiển thị toàn tin tiếng Anh, một bên hiển thị toàn tin Tiếng Việt. Vì ID của 2 ngôn ngữ khác nhau nè. Mình hiểu vậy đúng không ta???

      • Duy Tuyên says:

        Hi, Tuấn nói chính xác rồi đó! Tuy nhiên, vấn đề cũng không đến nỗi quá nan giải, nếu thêm một trường để xác định quan hệ giữa các mẫu tin đa ngôn ngữ này là có thể giải quyết được. Lựa chọn phương thức nào là tùy thuộc vào yêu cầu của website, những website dạng tin tức thì hiếm 1 record ngôn ngữ này phải tương ứng với 1 record ngôn ngữ kia, các trang về sản phẩm cũng thế, có thể ở phần tiếng Việt họ rao các sp cho người Việt còn tiếng Anh họ rao sản phẩm cho tiếng Anh.

        Hi vọng qua các phần tranh luận, bạn đọc sẽ tìm cho mình giải pháp tốt nhất ứng với 1 ứng dụng và 1 yêu cầu cụ thể!

  5. Duy Tuyên says:

    Cách 4: dùng 1 table gánh toàn bộ nội dung đa ngôn ngữ, nhược điểm thì mọi người tự tìm nhé!

    Table này giả sử tên là contents, gồm các cột:

    ID record_id field lang content

    Table news:

    ID — created
    1 — 2010-03-16

    Lúc này table contents sẽ lưu trữ:

    ID record_id field lang contents
    1 — 1 — desc — vn — Mo ta TV
    2 — 1 — content — vn — Noi dung TV
    3 — 1 — desc — en — Mo ta TA
    4 — 1 — content — en — Noi dung TA

    Cũng giống nhu cách 2, khi select, insert, update, delete đều phải chú ý tới dữ liệu 2 table!

    • admin says:

      Hi hi, vậy cách 3 sẽ có ứng dụng riêng của nó chứ không phải dùng để chuyển 1 trang nội dung sang đa ngôn ngữ. Cách 3 tốt nhất là để làm 1 trang có dữ liệu có nhiều ngôn ngữ nhưng các dữ liệu của các ngôn ngữ không cần mối liên quan với nhau (quá trình Update/Delete).
      Dù sao cũng cảm ơn Tuyên đã chia sẽ.

      • BiBi says:

        Mình thấy thêm một cột liên hệ nữa là được mà.

        Làm trên 1 table mình thấy tiện nhất đấy, vì MySQL đã khuyên rằng khi truy vấn, cách tốt nhất nên truy vấn càng ít table càng tốt.

        Theo mình dùng cách 3: và thêm 1 field là mutli vào table chẳng hạn, field multi này sẽ được gán bằng với id của record tiếng việt, khi đã có record tiếng việt, người ta vào trang admin click thêm dữ liệu cho ngôn ngữ tiếng anh, khi click thì ta đã xác định được multi là bao nhiêu rồi đúng không. Vậy thì khi người dùng view dữ liệu tiếng việt lên, click vào nút “xem định dạng En”, thì ta sẽ có conditions = “lang=’en’ and multi=”. Vậy là select chỉ duy nhất 1 bảng và giải quyết được tất cả vấn đề.
        Ý tưởng nhất thời của mình là vậy, mình cũng chỉ mới làm site duy nhất Tiếng Việt hà =.=”, đừng mắng mình nếu sai nhé.

        P/s: admin nên thêm chức năng send mail nếu có ai rả lời bài viết. thanks.

      • admin says:

        Cảm ơn chia sẽ của bạn, thật sự thì vấn đề multilanguage data thì tùy website àh. Mình thì làm cho nhiều khách hàng, người thì kiểu language này, người thì kiểu khác, mình cũng không muốn change table nên mình mới chơi kiểu table quan hệ, còn nếu hệ thống đơn giản hay cổ điển thì có thể chơi nhiều col cũng hay.
        Còn về việc notify mail thì mình không triển khai bởi mình sợ phải spam hộp mail của các bạn lắm. Nhưng sẽ xem xét trong thời gian sắp tới. Cảm ơn bạn nhé. Rất vui được làm quen.

  6. Truong Vi Bao says:

    Cảm ơn các bạn nhiều, 02 bạn đều pro ,súc tích lắm. nhưng nói thiệt mình thích cách mì ăn liền của bạn Tuyên hơn. đơn giản gọn lẹ, 1 câu tiếng anh, 1 câu tiếng việt hehe. chỉ để nói lời cảm ơn người đưa ra ý tưởng cho mình chứ không có ý phê phán gì đâu nhé. thanks all!

  7. TMQuang says:

    4 cách trên, cách nào cũng có ưu nhược điểm hết nhưng nói chung tùy theo nhu cầu website như thế nào mà triễn khai thôi phải không các bác. 🙂
    Cách làm của mình hiện nay đó là làm theo ý tưởng của bạn Tuấn.
    Mình đang mấp mé ý đồ làm 1 website đa ngôn ngữ với sự kết hợp của DBMS thông thường và XML.
    Ý tưởng là thế này, trong bảng products chúng ta có vài fields cần phải đa ngôn ngữ (title, description…) thì tại các fields này chúng ta không đơn thuần insert data theo cách truyền thống mà insert kiểu XML vào
    Sau đó khi Select ra, chúng ta mất thêm 1 ít thời gian để parse cái XML đó theo cái language hiện hành.
    Cách này đánh giá sơ bộ:
    Ưu:
    1)Giảm dung lượng DB (do còn dùng 1 bảng và cũng không nhân bảng các giá trị trùng lắp)
    2) Truy xuất nhanh (do chỉ dùng 1 select đơn thuần – không cần JOIN với ai hết. Ở đây nói nhanh là thao tác DB Access nha)
    3) Cấu trúc DB không thay đổi, do vẫn lưu dồn vào các field trước
    Khuyết:
    1)Xử lý bên server nặng hơn, do có thêm thao tác parse XML lấy data phù hợp (không khéo sẽ chậm hơn so với cách truyền thống :D)
    2)Có bao nhiêu ngôn ngữ, quăng hết vào các fields dưới dạng XML nên khi lấy dữ liệu lên … thật kinh hoàng, nặng lắm chứ chẳng chơi.
    3)Trong trường hợp lúc ban đầu có 2 ngôn ngữ và đã có khoảng 1000 records bây giờ yêu cầu nâng cấp lên > 2 ngôn ngữ => điều gì xảy ra chắc mọi người hiểu 😀
    Chút đóng góp xin được thọ giáo thêm

    • admin says:

      Không biết bác làm như vậy có nghĩ tới việc bảo mật data chưa. Vì như bác biết là XML người ta chỉ cần biết link là lấy data của bác rồi. Nếu bác muốn security chỗ này thì chỉ có 2 giải pháp:
      1. Bác giấu nhẹm cái link xml, không publish ra bên ngoài, đây là kiểu “Security through obscurity”, kiểu này tiềm ẩn nguy cơ.
      2.Bác dùng cơ chế authen gì đó trên cái xml file, như vậy tự nhiên lại thêm 1 layer để security cái xml, vừa authen cái db connect, vừa authen cái xml –> thấy quá ư là phung phí ^^.

      Chưa kể hệ thống Hybrid giữa DB và XML thế này tội nghiệp cho server quá.
      Vài ý kiến của em.

  8. TMQuang says:

    hiểu lầm rồi Tuấn ơi. Không có lưu file XML riêng, mà chỉ lưu string XML vào DB thôi. Chứ không phải lưu thêm cái file đâu mà sợ người ta cướp của 😀
    tức trong field title chẳng hạn thay vì lưu “sản phẩm a” thì giờ lưu như sau:

    vậy đó

  9. TMQuang says:

    bị chặn XSS rồi
    câu trên mình viết XML nên bị chặn. Tuấn xem câu sau nhá:
    (title vn=”sản phẩm a” en=”product a”)
    ————————–
    Thay dấu ( bằng dấu nhỏ hơn
    Thay dấu ) bằng dấu lớn hơn

  10. Minh tuấn says:

    Các bạn nói tùm lum cách , nói chung cách nào cũng có ưu và nhược điểm của nó.Trong trường hợp cách 2, theo đánh giá của riêng mình,mình cảm thấy cách đó hay nhất.Nhưng cho mình hỏi 1 câu nhé.Hiện tại các bạn đang bàn luận về các bản đơn thôi.Mình giả dụ, cái bảng News có sẽ nối thêm với bảng CategoriesNews nửa thì sau, khi đó sẽ thao tác đa ngôn ngữ như thế nào vậy ta, xin các pro góp ý dùm nhé.

    • admin says:

      hi, bài viết đề cập tới triển khai đa ngôn ngữ cho việc lưu trữ 1 chức năng chứ không đề cập tới bảng đơn hay gì khác. Nếu bạn muốn lưu đa ngôn ngữ thì có thể coi bài viết ở trên. Còn join thì không có phải vấn đề bàn luận ở đây. Tùy theo cách phân tích DB thì bạn sẽ có cách join để ra đúng và đủ dữ liệu với ngôn ngữ tương ứng thôi. Chúc vui!

  11. Chin Lee says:

    Chào Admin!

    Cách thiết kế db cho website đa ngôn ngữ của anh chia sẻ khá là hay, giờ em mới biết, nhưng em đang thắc mắc trong cách 2 của anh: Khi mình add 1 product mới vào DB, làm sao mình có thể add 1 lúc vào 2 table product và product_language được ạ? Hay còn cách khác add tốt hơn, mong anh chỉ giáo 🙂

    • admin says:

      Hihi, theo bạn muốn mở 1 cái máy thì làm gì? nhấn nút power đúng ko, vậy bây giờ muốn mở 2 cái máy thì làm thế nào? Nếu trả lời được câu hỏi đó thì sẽ trả lời được thắc mắc của bạn ở trên đó. Rất vui được làm quen!

  12. vnxitrum says:

    Cách cách của các bạn đưa ra cũng khá hay, mỗi cách đều có ưu va nhược điểm, nhân đây mình muốn hỏi cách 4 của bạn Duy Tuyên khi thêm một tin, ta thêm tiếng việt roi đến tiếng anh…. thi ok, nhưng nếu khi thêm tiếng việt xong, ta ko thêm các ngôn ngữ khác cho tin này mà ta lại tao mới một tin mới khác rổi quay lại thêm ngôn ngữ tiếng anh cho tin trước đó thì sao nhỉ??????

  13. ertshinguyrn says:

    trong mssql , co 1 loai id la uniqueidentifier loai nay cho ta truc tiep sinh ra id va insert vao database bang cach
    Guid.NewGuid() . luu vao 1 string, dua vao sring nay ta muon insert tat ca cac ban nao foreign toi’ bang chinh deu co the ok .. trong mysql toi ko bit,
    con 1 cach nua la neu dung identity(1,1) trong mssql hay la auto crement gi trong mysql ay thi lam sao lay duoc id vua insert ( tuyệt đối ko được lấy theo thời gian insert sớm nhất ) là bạn có thể giải quyết cách 2 rồi .
    em nói có gì ko phải thì mấy chú bỏ qua ạ

  14. tnvyen says:

    Mình thấy Joomla có chức năng đa ngôn ngữ, tuy nhiên nếu một hoặc hai ngôn ngữ thì được, chứ nếu site có nhiều ngôn ngữ, mà mỗi ngôn ngữ có rất nhiều record thì load quá nặng.
    Hơn nữa, mỗi lần người dùng chỉ dùng một ngôn ngữ, trong khi đó SQL phải truy vấn toàn bộ record của tất cả các ngôn ngữ để đưa ra kết quả của 1 ngôn ngữ thì thật lãng phí (thấy rõ trong trường hợp dữ liệu đồ sộ và nhiều ngôn ngữ).
    Minh có ý tưởng là mỗi ngôn ngữ có một CSDL riêng, nhưng dùng chung một mã nguồn (Vì nếu dùng nhiều mà nguồn thì việc bảo trì trở nên vất vả). Với người dùng, họ sẽ chọn ngôn ngữ hiện thị trước khi vào site. Tức là, index.php sẽ cho người dùng chọn ngôn ngữ trước, sau đó mới load CSDL tùy thuộc vào ngôn ngữ được chọn.
    Điều quan trọng là mình chưa biết làm cách nào! Trên đây chỉ mới là ý tưởng!
    Xin các Pro cho ý kiến!

  15. tnvyen says:

    @tuzebra,
    Site này vừa khác mà lại vừa giống.
    Nó khác cơ sở dữ liệu, nhưng cùng chung mã nguồn. Theo mình nhớ không lầm thì microsoft cũng tổ chức kiểu này. Vì không thể tập trung mấy chục thứ tiếng vào cùng một CSDL được (vì sẽ rất nặng và việc truy vấn hơi lãng phí).
    Việc sử dụng chung mã nguồn nhằm mục đích dễ bảo trì. Nhờ sử dụng mã nguồn tập trung nên khi muốn chỉnh sửa code, chỉ cần sửa 1 cái thôi.
    Mình đang thử ở Joomla, và có vẻ chạy được tuy có vài lỗi ở giao diện, còn plugin, component và module thì không có vấn đề gì.

  16. Trần Thanh Huy says:

    Cảm ơn các bạn đã chia sẽ về vấn đề đa ngôn ngữ.Mình thì thấy nên sử dụng cả 2 cách là cách 1 và cách 4 cho trang web.Theo mình có 2 dạng dữ liệu thường thấy là dữ liệu kiểu “item” và dữ liệu kiển “category”, “item” thì các cách trên đều ok còn với “category” khi mà có “id” và “parent_id” còn có “weight” để sắp xếp khi mà xuất menu đa cấp nữa thì như thế nào.
    Trong việc thêm xóa sửa thay đổi cha, con, thây đổi thứ tự thì mình thấy cách 1 là ok nhất để làm cho “category”
    Còn cách 4 áp dụng cho “item” là ok vì có thể tùy chỉnh riêng cho từng ngôn ngữ,
    nên mình sẽ là như vầy
    có 3 bảng
    1:category gồm các cột id,vi_title,vi_alias,en_title,en_alias,parent_id,weight
    2:vi_item gồm các cột id,cat_id,title. . .
    3:en_item: gồm các cột id,cat_id,title. . .
    . Vì category thường chỉ có vi_title,vi_alias,en_title,en_alias. nên cách không nhiêu cột như item
    thêm 1 chức năng cho phép đồng bộ hóa giữa các dòng của lang_item nữa là ok.
    ví dụ: bảng vi_item có các dòng có id là 1,3,5
    bảng en_item có các dòng có id là 1,2,4
    thì các dòng “Chỉ tồn tại ở 1 bản” là id 3,5,2,4 sẽ được đồng bộ qua lại còn dòng “Đã tồn tại cả ở cả 2 bảng” 1 thì giữ nguyên
    Ý kiến riêng của mình ^ ^

  17. Quốc Vĩnh says:

    Chào admin

    – Các cách multil language các bạn đưa ra đều rất hay, mình đang thắc mắc trong 4 cách trên nếu số records tăng lên 10.000 thì làm DB vẫn chạy nhanh như thường…

    Xin cảm ơn

Gởi bình luận