Mặc dù một số hệ thống phần mềm được sử dụng bởi một số lượng hạn chế người dùng nói cùng một ngôn ngữ, nhưng hầu hết các tổ chức cần thống nhất và tập trung các ứng dụng của họ để những người nói các ngôn ngữ khác nhau trên toàn thế giới sử dụng. Cơ sở dữ liệu đa ngôn ngữ có thêm một mức độ khó khăn trong việc thiết kế và triển khai các mô hình dữ liệu. Trong bài viết này, chúng tôi đề xuất một số cách tiếp cận để đối phó với thách thức này.
Chúng ta cần lưu trữ thông tin gì bằng nhiều ngôn ngữ?
Nhìn bề ngoài, tất cả thông tin chuỗi có vẻ hợp lý để dịch sang nhiều ngôn ngữ. Tuy nhiên, trường hợp này thường không xảy ra. Thông tin liên quan đến khách hàng như CompanyName
hoặc Address
có thể được dịch, nhưng đó có thể không phải là một ý kiến hay.
Đưa một khách hàng doanh nghiệp ở Vương quốc Anh tên là “Xe tải ven sông” có văn phòng tại “123 Đường Upper Castle”. Bạn không muốn người dùng nói tiếng Tây Ban Nha in và gửi thư cho “Camiones Orilla” có địa chỉ tại “123 Calle Castillo Superior”. Royal Mail (dịch vụ bưu chính của Vương quốc Anh) sẽ không tìm thấy nó! Bạn có thể chỉ muốn dịch các cột chứa thông tin mô tả, không phải tên riêng.
Khi thiết kế một hệ thống xử lý bản dịch, không phải lúc nào bạn cũng biết trước chính xác cột nào có thể dịch được và cột nào không yêu cầu dịch. Lựa chọn một cách tiếp cận linh hoạt giúp tiết kiệm rất nhiều thời gian trong việc thiết kế và phát triển. Hãy xem bài viết “Cách thiết kế hệ thống sẵn sàng bản địa hóa” để xem một số ví dụ.
Chúng tôi đang xem xét những cách tiếp cận nào?
Trong bài viết này, chúng tôi mô tả ba cách tiếp cận để thiết kế cơ sở dữ liệu đa ngôn ngữ. Chúng tôi bắt đầu với phương án đơn giản nhất không linh hoạt, sau đó chuyển sang xem xét các phương án khác, giải thích ưu và nhược điểm của từng phương án.
Cả cú pháp và mô hình cơ sở dữ liệu (có sẵn trên trình tạo mô hình dữ liệu dựa trên web Vertabelo) được sử dụng trong bài viết này đều dành cho SQL Server. Tuy nhiên, chúng dễ dàng thích nghi với bất kỳ công cụ cơ sở dữ liệu nào.
Phương pháp 1:Tạo các cột bổ sung để giữ nội dung đã dịch
Đây là cách tiếp cận đơn giản nhất để thực hiện, mặc dù không phải là một cách rất linh hoạt. Nó bao gồm việc thêm một cột cho mỗi cột và ngôn ngữ chúng ta cần sử dụng trong hệ thống của mình, như được hiển thị trong sơ đồ Vertabelo sau:
Mặc dù đây có vẻ là một giải pháp rất đơn giản, nhưng nó có một số nhược điểm. Chúng tôi giải thích bên dưới.
Con:Độ phức tạp của mã
Cách tiếp cận này làm cho mã phức tạp hơn. Nó yêu cầu chúng tôi viết một truy vấn khác nhau cho từng ngôn ngữ hoặc sử dụng CASE
cấu trúc để truy xuất bản dịch cho ngôn ngữ thích hợp dựa trên cấu hình người dùng. Ví dụ:hãy xem đoạn mã sau:
SELECT ProductID, CASE @Language KHI 'ES' THEN ProductName_ES WHEN 'DE' THEN ProductName_DE WHEN 'FR' THEN ProductName_FR ELSE ProductName END AS ProductName, CASE @Language KHI 'ES' THEN ProductDescription_ES WHEN 'DE' THEN ProductDescription FR 'THEN ProductDescription_FR ELSE ProductDescription END AS ProductDescription, Price, Weight, ProductCategoryIDFROM ProductWHERE…
Lưu ý: Trong ví dụ, chúng tôi sử dụng biến @Language để giữ ngôn ngữ mà chúng tôi muốn sử dụng. Bạn có thể cân nhắc sử dụng SESSION_CONTEXT () (hoặc Ngữ cảnh ứng dụng trong Oracle) để đặt và đọc ngôn ngữ cho từng người dùng.
Con:Thiếu tính linh hoạt
Cách tiếp cận này thiếu tính linh hoạt. Nếu chúng ta cần triển khai một ngôn ngữ mới, chúng ta cần sửa đổi mô hình dữ liệu của mình bằng cách thêm một cột cho ngôn ngữ mới cho mỗi cột có thể dịch được trong hệ thống của chúng ta. Chúng tôi cũng cần tạo truy vấn ngôn ngữ mới cho mỗi bảng (hoặc chỉnh sửa bảng hiện có bằng cách thêm CASE WHEN
mới mệnh đề sử dụng ngôn ngữ mới cho mỗi cột có thể dịch).
Con:Những thách thức trong việc xử lý thông tin không xác định
Hãy tưởng tượng điều này:một người dùng thêm một sản phẩm nhưng không biết cách dịch nó và để trống các cột đã dịch. Người dùng nói các ngôn ngữ đó xem NULL
hoặc thông tin trống trong các cột có thể được yêu cầu.
Phương pháp 2:Cô lập các cột có thể dịch trong một bảng riêng biệt
Cách tiếp cận này nhóm các cột trong bảng thành các cột có thể dịch và không dịch được. Các cột không thể dịch vẫn ở trong bảng gốc. Ngược lại, những cái có thể dịch được nằm trong một bảng riêng biệt, với một khóa ngoại cho bảng gốc và một chỉ báo ngôn ngữ. Xem bên dưới:
Sơ đồ hiển thị các bảng gốc (không có dữ liệu có thể dịch được) bằng màu trắng. Các bảng chứa bản dịch có màu xanh lam nhạt và bảng chính chứa thông tin ngôn ngữ có màu vàng.
Điều này có lợi thế về tính linh hoạt rất lớn so với việc sử dụng nhiều cột như đã thảo luận trước đó. Phương pháp này không yêu cầu thay đổi mô hình dữ liệu khi cần một ngôn ngữ mới. Ngoài ra, cú pháp để truy vấn thông tin đơn giản hơn:
SELECT p.ProductID, pt.ProductName, pt.ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product pLEFT JOIN ProductTranslation pt ON pt.ProductID =p.ProductID AND pt.LanguageID =@LanguageWHERE…Tuy nhiên, vẫn có một số nhược điểm, như chúng tôi thảo luận bên dưới.
Con:Những thách thức khi các cột bổ sung cần được dịch
Nếu chúng ta cần chuyển đổi một cột không thể dịch thành một cột có thể dịch được (hoặc ngược lại), chúng ta cần sửa đổi mô hình dữ liệu của mình, di chuyển cột từ bảng này sang bảng khác. Điều này thường ngụ ý chi phí lớn hơn khi hệ thống được triển khai và sử dụng.
Con:Những thách thức trong việc xử lý thông tin không xác định
Giống như cách tiếp cận đầu tiên, cách tiếp cận này có những thách thức khi xử lý thông tin chưa biết. Một lần nữa, nếu người dùng thêm một sản phẩm nhưng không biết cách dịch sản phẩm đó và để trống các cột đã dịch, người dùng nói các ngôn ngữ đó sẽ thấy
NULL
hoặc thông tin trống trong các cột có thể được yêu cầu. Ngoài ra, truy vấn yêu cầuLEFT JOIN
trong trường hợp bản dịch cho ngôn ngữ của người dùng hiện tại chưa được tạo để dữ liệu không thể dịch vẫn được hiển thị.Phương pháp 3:Thêm hệ thống con dịch
Dịch có thể được coi là một tính năng hoàn toàn độc lập với mô hình dữ liệu yêu cầu dịch. Trong một hệ thống lý tưởng, chúng tôi có thể bật hoặc tắt tính năng dịch cho bất kỳ cột nào mà không yêu cầu sửa đổi mô hình dữ liệu. Thật không may, điều này nói dễ hơn làm.
Chúng tôi trình bày một phương pháp không ảnh hưởng đến mô hình dữ liệu hiện có và hoàn toàn linh hoạt. Mặc dù nó tăng thêm độ phức tạp tại thời điểm truy vấn dữ liệu, nhưng nó không yêu cầu bất kỳ thay đổi bổ sung nào đối với mô hình dữ liệu ngoại trừ một vài bảng. Đây có thể là một lựa chọn tuyệt vời nếu bạn cần thêm khả năng giữ bản dịch vào mô hình dữ liệu hiện có.
Hãy xem qua mô hình và xem nó hoạt động như thế nào:
Điều đầu tiên cần nhận thấy là mô hình dữ liệu ban đầu không có thay đổi nào cả. Ngoài ra, không có mối quan hệ trực tiếp giữa mô hình đó và hệ thống con dịch.
Hệ thống con dịch bao gồm một từ điển dữ liệu nhỏ với các bảng và cột yêu cầu dịch. Từ điển dữ liệu này có thể được sửa đổi chỉ bằng cách thêm / bớt hàng mà không làm thay đổi mô hình dữ liệu. Các bản dịch được lưu trữ trong một bảng riêng biệt, với mỗi giá trị được xác định bởi 3 cột sau:
-
ColumnID
:Xác định duy nhất cột (và bảng) mà chúng tôi đang dịch. -
KeyID
:Lưu trữ ID (khóa chính) của hàng cụ thể mà chúng tôi đang dịch. -
LanguageID
:Xác định ngôn ngữ của bản dịch.
Thiết kế này cho phép dữ liệu được nhập và lưu trữ trong các bảng gốc, chỉ thêm các bản dịch nếu và khi được yêu cầu. Thông tin đã dịch được sử dụng khi truy xuất dữ liệu, giữ cho dữ liệu gốc (bằng ngôn ngữ gốc) không bị ảnh hưởng.
Dữ liệu có thể được truy vấn bằng cú pháp phức tạp hơn các ví dụ trên. Nó yêu cầu một JOIN
bổ sung cho mỗi cột có thể dịch được như hình dưới đây:
SELECT p.ProductID, ISNULL (t1.TranslationValue, p.ProductName) AS ProductName, ISNULL (t2.TranslationValue, p.ProductDescription) AS ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Sản phẩm pLEFT THAM GIA Bản dịch t1 ON t1.ColumnID =<> AND t1.Key =p.ProductID VÀ t1.LanguageID =@LanguageLEFT THAM GIA Bản dịch t2 TRÊN t2.ColumnID =< > AND t2.Key =p.ProductID VÀ t2.LanguageID =@LanguageWHERE…;
Lưu ý: “<<ProductName_ColumnID>>
”Và“ <<ProductDescription_ColumnID>>
”Phải được thay thế bằng ID của các cột sẽ được dịch khi được lưu trữ trong ColumnInformation
bàn. Cân nhắc tạo chế độ xem bản dịch cho mỗi bảng yêu cầu bản dịch để che giấu sự phức tạp của các JOIN đối với người dùng cuối. Bạn thậm chí có thể tự động hóa bước này bằng một tập lệnh tạo từng chế độ xem. Tập lệnh này có thể truy vấn từ điển dữ liệu của cơ sở dữ liệu để chọn các bảng và cột và thêm logic dịch cho các cột tồn tại trong ColumnInformation
bảng.
Mẹo bổ sung số 1
Bạn cũng có thể đơn giản hóa cú pháp. Thay thế mỗi JOIN bằng một lệnh gọi đến một hàm xử lý (và ẩn) khía cạnh dịch, như được hiển thị bên dưới:
SELECT p.ProductID, ISNULL (fn_translate ('Product', 'ProductName', ProductID), p.ProductName) AS ProductName, ISNULL (fn_translate ('Product', 'ProductDescription', ProductID), p.ProductDescription) AS ProductName, p.Price, p.Weight, p.ProductCategoryIDFROM Product pWHERE…;
Hàm có thể đọc ngôn ngữ mong muốn từ ngữ cảnh hoặc bạn có thể thêm nó làm tham số bổ sung. Trong ví dụ này, hàm sử dụng tên bảng và cột được cung cấp dưới dạng tham số cộng với khóa hàng (cũng được cung cấp dưới dạng tham số) để tìm kiếm và trả về bản dịch mong muốn.
Việc gọi một hàm ngụ ý một tác động bổ sung đến hiệu suất do chuyển đổi ngữ cảnh giữa SQL và ngôn ngữ thủ tục. Tuy nhiên, nó có thể là một giải pháp đơn giản hơn cho cơ sở dữ liệu hoặc bảng mà lượng dữ liệu đang được dịch cho phép.
Cả hai ví dụ - ví dụ có JOIN và ví dụ có hàm - đều sử dụng hàm ISNULL () SQL Server. Vì vậy, khi bản dịch sang ngôn ngữ mong muốn không tồn tại, nó vẫn hiển thị giá trị gốc được lưu trữ trong cột ProductName và ProductDescription thay vì khoảng trống hoặc NULL.
Cân nhắc chung
Cách tiếp cận thứ ba thường là tốt nhất để triển khai các thiết kế lớn hơn. Nó cho phép sự linh hoạt cả khi thiết kế và khi hệ thống được sử dụng. Tuy nhiên, có những cân nhắc cụ thể có thể làm cho các phương pháp tiếp cận khác trở nên hữu ích. Bất kể lựa chọn của bạn là gì, hãy cân nhắc những điều sau để tiết kiệm thời gian cả khi thiết kế và phát triển / thực hiện.
Thêm một lớp trừu tượng
Như đã đề cập trước đó, hãy cân nhắc việc tạo các dạng xem quan tâm đến logic dịch, ví dụ:chọn một cột trong số nhiều cột dịch hoặc kết hợp với các hàng cụ thể. Điều này giữ cho các chi tiết triển khai cụ thể bị ẩn với các lập trình viên. Họ chỉ đơn giản là sử dụng các khung nhìn này thay vì phải tạo các câu SQL phức tạp mỗi khi họ cần truy cập vào một bảng có thông tin có thể dịch được.
Sử dụng ngữ cảnh để lọc dữ liệu
Như đã đề cập trong ví dụ đầu tiên, hãy xem xét sử dụng các hàm ngữ cảnh có sẵn trong hầu hết các công cụ cơ sở dữ liệu. Sử dụng chúng để lưu trữ thông tin ngôn ngữ của người dùng sau khi đăng nhập vào hệ thống, sau đó lọc kết quả tự động trong các chế độ xem có liên quan đến bản dịch.
Tự động hóa
Các hệ thống hiện đại có thể có hàng trăm, thậm chí hàng nghìn bảng. Dành thời gian để tự động tạo các chế độ xem bản dịch thay vì viết từng truy vấn một. Bạn có thể mất một khoảng thời gian để có được một tập lệnh hoạt động, nhưng sau đó bạn luôn có thể tạo các chế độ xem mới hoặc tạo lại các chế độ xem hiện có trong vòng chưa đầy một giây!