Trong các bài trước, chúng ta đã xây dựng project Bookstore, tạo các category Lịch sử và Triết học, đồng thời tìm hiểu cách sử dụng Content Delivery API để lấy dữ liệu từ Umbraco 13.
Trong bài Day 5, chúng ta sẽ mở rộng project bằng một chức năng thực tế hơn: xây dựng khu vực Blog có hỗ trợ tiếng Việt và tiếng Anh.
Thay vì chỉ quản lý sách, website sẽ có thêm các bài viết lịch sử. Ví dụ, chúng ta sẽ tạo một bài viết về Lý Tự Nghiệp (chữ Hán: 李嗣業), một danh tướng thời Đường, với nội dung tiếng Việt là ngôn ngữ chính và tiếng Anh là ngôn ngữ thứ hai.
Qua bài thực hành này, chúng ta sẽ tìm hiểu cách thiết kế Document Type, xây dựng Content Tree, cấu hình Language Variants, nhập nội dung theo từng ngôn ngữ và kiểm tra kết quả qua Content Delivery API.
Mục tiêu sau bài học:
- Hiểu cơ chế đa ngôn ngữ có sẵn trong Umbraco 13.
- Biết cách tạo Document Type cho Blog, Blog Category và Blog Post.
- Phân biệt dữ liệu dùng chung với dữ liệu thay đổi theo ngôn ngữ.
- Tạo một bài viết có phiên bản tiếng Việt và tiếng Anh.
- Gọi Content Delivery API để kiểm tra nội dung theo culture.
1. Language Variants trong Umbraco là gì?
Khi xây dựng website đa ngôn ngữ, chúng ta cần quyết định cách lưu trữ nội dung. Một cách đơn giản là tạo hai bài viết riêng biệt, một bài tiếng Việt và một bài tiếng Anh. Tuy nhiên, cách này có thể khiến việc quản lý trở nên phức tạp khi số lượng bài viết tăng lên.
Umbraco cung cấp tính năng Language Variants, cho phép một content item có nhiều phiên bản ngôn ngữ. Mỗi phiên bản có thể có tên, nội dung và trạng thái xuất bản riêng theo culture.
Ví dụ, bài viết Lý Tự Nghiệp có thể được quản lý như sau:
- Phiên bản tiếng Việt: Lý Tự Nghiệp (李嗣業) – Danh tướng nhà Đường.
- Phiên bản tiếng Anh: Li Siye (李嗣業) – A Tang Dynasty General.
Hai phiên bản này thuộc cùng một content item trong Umbraco, không phải hai bài viết độc lập.
Để thực hiện được điều đó, chúng ta cần cấu hình ngôn ngữ, bật variation trên Document Type và cho phép variation trên từng property cần dịch.
Tài liệu chính thức: Umbraco 13 – Language Variants.
2. Thiết kế Content Tree cho Blog
Trong project Bookstore, chúng ta đã có Bookstore Home cùng hai category sách Lịch sử và Triết học. Bây giờ, chúng ta sẽ bổ sung một nhánh Blog riêng để quản lý các bài viết.
Cấu trúc Content Tree đề xuất:
Bookstore Home
├── Lịch sử (Category sách)
├── Triết học (Category sách)
└── Blog
├── Lịch sử (Blog Category)
│ └── Lý Tự Nghiệp (Blog Post)
└── Triết học (Blog Category)
Ở đây có hai node mang tên Lịch sử nhưng chúng phục vụ hai mục đích khác nhau:
- Lịch sử – Category sách: phân loại các đầu sách lịch sử trong cửa hàng.
- Lịch sử – Blog Category: phân loại các bài viết lịch sử trên blog.
Việc tách biệt này giúp chúng ta tránh trộn lẫn dữ liệu sản phẩm sách với dữ liệu bài viết. Trong một dự án thực tế, hai loại dữ liệu có thể có thuộc tính, quy trình biên tập và cách hiển thị hoàn toàn khác nhau.
3. Thiết kế Document Type
Chúng ta sẽ tạo ba Document Type mới:
blog: trang gốc của khu vực Blog.blogCategory: chuyên mục Blog.blogPost: bài viết Blog.
Để tạo Document Type, vào phần Settings → Document Types trong Umbraco Backoffice. Tên các mục giao diện có thể khác đôi chút tùy phiên bản hoặc cấu hình.
3.1. Document Type Blog
Tạo Document Type với tên Blog và alias blog.
Đây là node gốc của khu vực Blog. Nó có thể chứa phần giới thiệu, tiêu đề trang và thông tin dùng chung cho trang Blog.
| Property alias | Data Type gợi ý | Mục đích |
|---|---|---|
pageTitle |
Textstring | Tiêu đề trang Blog |
introText |
Textarea | Phần giới thiệu Blog |
featuredTitle |
Textstring | Tiêu đề khu vực bài viết nổi bật |
Các property này là đề xuất cho bài thực hành, không phải những property Umbraco tự tạo. Bạn cần thêm chúng vào Document Type của mình.
3.2. Document Type Blog Category
Tạo Document Type có tên Blog Category và alias blogCategory.
Document Type này đại diện cho một chuyên mục như Lịch sử hoặc Triết học trong khu vực Blog.
| Property alias | Data Type gợi ý | Mục đích |
|---|---|---|
categoryName |
Textstring | Tên chuyên mục |
description |
Textarea | Mô tả chuyên mục |
thumbnail |
Media Picker | Ảnh đại diện chuyên mục |
Trong bài này, chúng ta sẽ sử dụng tên node làm tên hiển thị chính của chuyên mục. Property categoryName có thể được dùng khi cần một tiêu đề hiển thị riêng.
3.3. Document Type Blog Post
Tạo Document Type có tên Blog Post và alias blogPost.
Đây là Document Type quan trọng nhất trong bài vì nó lưu nội dung bài viết về Lý Tự Nghiệp và sẽ có nhiều phiên bản ngôn ngữ.
| Property alias | Data Type gợi ý | Cho phép variation? |
|---|---|---|
summary |
Textarea | Có |
chineseName |
Textstring | Không bắt buộc |
featuredImage |
Media Picker | Không bắt buộc |
bodyText |
Rich Text Editor | Có |
author |
Textstring | Tùy yêu cầu |
publishDate |
Date Picker | Thường không cần |
Tiêu đề bài viết được quản lý bằng tên node trong Content Tree. Khi tạo phiên bản tiếng Anh, chúng ta có thể đặt tên node tiếng Anh tương ứng.
Property chineseName chứa tên chữ Hán 李嗣業. Vì đây là thông tin định danh không thay đổi theo ngôn ngữ, chúng ta có thể giữ property này ở dạng invariant, tức dùng chung cho các phiên bản.
Tương tự, ảnh đại diện, tác giả và ngày xuất bản cũng có thể là dữ liệu dùng chung nếu yêu cầu nghiệp vụ không đòi hỏi bản dịch riêng.
Ngược lại, tiêu đề, tóm tắt và nội dung bài viết thường cần thay đổi theo ngôn ngữ, do đó nên cho phép variation.
4. Cấu hình quan hệ cha – con
Sau khi tạo ba Document Type, chúng ta cần xác định loại node nào được phép nằm bên dưới loại node nào.
| Document Type cha | Document Type con |
|---|---|
bookstoreHome |
blog |
blog |
blogCategory |
blogCategory |
blogCategory, blogPost |
Trong phần cấu hình cấu trúc của Document Type, hãy thiết lập các loại node con được phép tương ứng.
Cho phép blogCategory chứa một blogCategory khác giúp chúng ta có thể mở rộng chuyên mục theo nhiều cấp nếu cần. Nếu website chỉ cần một cấp chuyên mục, bạn có thể bỏ lựa chọn này.
Ví dụ sau khi hoàn thành:
Bookstore Home
└── Blog
└── Lịch sử
└── Lý Tự Nghiệp (李嗣業)
Lưu ý: nếu bạn sử dụng Document Type có sẵn cho Bookstore Home, hãy giữ nguyên các cấu hình cũ và chỉ bổ sung khả năng chứa Blog. Không cần tạo lại node gốc hoặc xóa các category sách đã có.
5. Cấu hình Ngôn ngữ trong Umbraco (Languages, Mandatory & Fallback)
Mặc định sau khi cài đặt, Umbraco chọn English (United States) làm ngôn ngữ gốc (Default language).
Đối với dự án Bookstore, chúng ta sẽ cấu hình Vietnamese (vi-VN) làm ngôn ngữ chính và giữ English (en-US) làm ngôn ngữ thứ hai.
5.1. Thêm tiếng Việt và chuyển đổi Default Language
Thực hiện các bước sau trong Umbraco Backoffice:
- Vào Settings → Languages.
- Nhấn nút Add language.
- Tại ô Language, chọn
Vietnamese (Vietnam)(ISO code:vi-VN). - Bật toggle Default language để đặt tiếng Việt làm ngôn ngữ mặc định của toàn bộ website.
- Nhấn Save để hoàn tất.
Lưu ý: Hệ thống sẽ hiển thị cảnh báo "Switching default language may result in default content missing" — đây là cảnh báo bình thường khi chuyển đổi ngôn ngữ mặc định trên project mới.
5.2. Hiểu rõ các tùy chọn cấu hình Ngôn ngữ
Trong màn hình tạo/chỉnh sửa Ngôn ngữ, Umbraco cung cấp 3 thiết lập quan trọng:
| Thuộc tính | Ý nghĩa kỹ thuật | Áp dụng cho dự án |
|---|---|---|
| Default language | Ngôn ngữ chính/mặc định của toàn bộ hệ thống. Mỗi website Umbraco chỉ có duy nhất 1 Default Language. | Đặt là vi-VN. |
| Mandatory language | Ngôn ngữ bắt buộc phải nhập dữ liệu. Nếu chưa điền và Publish ngôn ngữ này, Node sẽ bị chặn không cho xuất bản. | Nên bật cho vi-VN để đảm bảo mọi bài viết đều phải có bản tiếng Việt trước. |
| Fall back language | Ngôn ngữ dự phòng. Nếu bài viết chưa có bản dịch ở ngôn ngữ hiện tại, API sẽ tự lấy dữ liệu từ ngôn ngữ fallback thay vì trả về rỗng. | Cấu hình cho en-US fallback về vi-VN (nếu chưa dịch sang tiếng Anh thì hiển thị tạm tiếng Việt). |
6. Bật Language Variants cho Document Type
Thêm ngôn ngữ vào website chưa đủ để cho phép nội dung thay đổi theo culture. Chúng ta cần bật variation trên những Document Type cần hỗ trợ đa ngôn ngữ.
6.1. Bật variation cho Blog Post
- Vào Settings → Document Types.
- Mở Document Type
Blog Post. - Tìm phần Permissions hoặc cấu hình tương ứng với variation.
- Bật Allow vary by culture.
- Lưu Document Type.
Đây là bước cho phép content item thuộc Document Type Blog Post có các phiên bản theo ngôn ngữ.
6.2. Bật variation cho từng property
Tiếp theo, mở phần thiết kế property của Document Type và bật Allow vary by culture cho các trường cần dịch.
Trong ví dụ này, chúng ta sẽ bật variation cho:
summarybodyText
Nếu muốn quản lý tiêu đề bằng một property riêng thay vì dùng tên node, bạn cũng cần bật variation cho property tiêu đề đó.
Đối với chineseName, author và publishDate, chúng ta có thể giữ nguyên dữ liệu dùng chung giữa các ngôn ngữ nếu nghiệp vụ cho phép.
Điều quan trọng là phải phân biệt hai cấp cấu hình:
- Document Type variation: cho phép content item có nhiều phiên bản ngôn ngữ.
- Property variation: cho phép giá trị của một trường thay đổi theo ngôn ngữ.
Nếu Document Type đã bật variation nhưng property chưa bật, trường đó có thể vẫn dùng chung giá trị giữa các ngôn ngữ.
7. Tạo bài viết Lý Tự Nghiệp
Quay lại Content, tạo các node theo cấu trúc đã thiết kế:
- Tạo node Blog bên dưới Bookstore Home.
- Tạo Blog Category có tên Lịch sử bên dưới Blog.
- Tạo Blog Post có tên Lý Tự Nghiệp (李嗣業) bên dưới chuyên mục Lịch sử.
Tiếp theo, nhập dữ liệu cho bài viết bằng tiếng Việt.
7.1. Phiên bản tiếng Việt
Ví dụ nội dung ban đầu:
Node name:
Lý Tự Nghiệp (李嗣業)
chineseName:
李嗣業
summary:
Lý Tự Nghiệp là một danh tướng thời Đường,
được biết đến qua các chiến dịch quân sự
trong thế kỷ VIII.
bodyText:
Lý Tự Nghiệp (李嗣業) là một võ tướng
phục vụ triều Đường. Ông nổi tiếng với
khả năng chỉ huy và chiến đấu, đặc biệt
trong bối cảnh các chiến dịch quân sự
thời Đường Huyền Tông và Đường Túc Tông.
Đây là nội dung giới thiệu ngắn dùng cho bài thực hành, không phải một bài nghiên cứu lịch sử đầy đủ. Khi phát triển nội dung thật, bạn nên kiểm tra riêng các mốc thời gian, chiến dịch và chức vụ của nhân vật.
Nhập nội dung vào các property tương ứng rồi lưu và publish phiên bản tiếng Việt.
7.2. Tạo phiên bản tiếng Anh
Sau khi cấu hình variation, mở node Lý Tự Nghiệp và tìm lựa chọn ngôn ngữ trong giao diện chỉnh sửa.
Chọn English (United States). Tùy giao diện, bạn có thể mở phiên bản tiếng Anh hoặc sử dụng chế độ Split View để chỉnh sửa hai ngôn ngữ song song.
Nhập nội dung tiếng Anh:
Node name:
Li Siye (李嗣業)
summary:
Li Siye was a Tang dynasty general
known for his military leadership
during the eighth century.
bodyText:
Li Siye (李嗣業) was a military
commander who served the Tang dynasty.
He became known for his battlefield
skills and leadership during the
military campaigns of the eighth century.
Những giá trị này chỉ nhằm minh họa cách nhập bản dịch. Bạn có thể hiệu chỉnh cách phiên âm tên nhân vật và nội dung tiếng Anh khi hoàn thiện bài viết.
Hãy lưu và publish phiên bản tiếng Anh. Khi publish, kiểm tra ngôn ngữ được chọn trong hộp thoại xác nhận để bảo đảm bạn đã xuất bản đúng culture.
Điểm cần nhớ: việc nhập nội dung tiếng Anh không đồng nghĩa với việc phiên bản tiếng Anh đã được publish. Trạng thái xuất bản của từng culture cần được kiểm tra riêng.
8. Kiểm tra dữ liệu qua Content Delivery API
Đến đây, chúng ta đã có một content item với hai phiên bản ngôn ngữ. Bước tiếp theo là xác minh dữ liệu từ góc nhìn của ứng dụng tiêu thụ API.
Content Delivery API của Umbraco 13 hỗ trợ header Accept-Language để yêu cầu nội dung theo culture khi truy vấn một item bằng GUID.
Endpoint lấy một content item:
GET /umbraco/delivery/api/v2/content/item/{id}
Trong đó, {id} là GUID thật của bài viết Lý Tự Nghiệp.
8.1. Gọi API lấy nội dung tiếng Việt
Sử dụng URL và GUID thực tế của project. Ví dụ với curl:
curl.exe -k `
-H "Accept-Language: vi-VN" `
"https://localhost:44366/umbraco/delivery/api/v2/content/item/YOUR_POST_GUID"
Thay port và YOUR_POST_GUID bằng giá trị thực tế. Tùy cấu hình chứng chỉ local, bạn có thể cần hoặc không cần tùy chọn -k. Chỉ dùng tùy chọn này trong môi trường phát triển đáng tin cậy.
Nếu request thành công và phiên bản tiếng Việt đã publish, hãy kiểm tra các trường name, properties.summary, properties.bodyText và cultures trong JSON response.
8.2. Gọi API lấy nội dung tiếng Anh
Tiếp theo, thay header bằng en-US:
curl.exe -k `
-H "Accept-Language: en-US" `
"https://localhost:44366/umbraco/delivery/api/v2/content/item/YOUR_POST_GUID"
So sánh hai response. Nếu cấu hình variation và publish đúng, các giá trị đã được cấu hình theo ngôn ngữ có thể khác nhau giữa hai request.
Ví dụ, trường tên node có thể được trả về theo phiên bản ngôn ngữ đang được yêu cầu, còn property chineseName có thể giữ nguyên nếu đó là property invariant.
JSON response thực tế phụ thuộc vào cấu hình Document Type, property editor, trạng thái publish và culture được yêu cầu. Vì vậy, hãy sử dụng kết quả từ project của mình để xác minh thay vì mặc định response sẽ giống hoàn toàn ví dụ trong bài.
8.3. Nếu API vẫn trả về nội dung mặc định
Nếu cả hai request đều trả về tiếng Việt, hãy kiểm tra lần lượt:
- English đã được thêm trong Settings → Languages chưa?
- Document Type Blog Post đã bật Allow vary by culture chưa?
- Property cần dịch đã bật variation chưa?
- Phiên bản tiếng Anh đã có dữ liệu và được publish chưa?
- Header
Accept-Languagecó đúng với culture đã cấu hình không? - Request có đang truy vấn đúng GUID của bài viết không?
Nếu sử dụng API theo đường dẫn thay vì GUID, culture còn có thể được xác định thông qua đường dẫn và cấu hình Culture and Hostnames. Trong bài thực hành này, chúng ta ưu tiên truy vấn theo GUID để kiểm tra vai trò của header Accept-Language.
Tài liệu chính thức: Umbraco 13 – Content Delivery API.
9. Những khái niệm cần phân biệt
Khi xây dựng website đa ngôn ngữ, có ba khái niệm dễ bị nhầm lẫn.
| Khái niệm | Ý nghĩa | Ví dụ |
|---|---|---|
| Language | Ngôn ngữ được cấu hình trong Umbraco | Vietnamese, English (US) |
| Culture | Mã ngôn ngữ và vùng được sử dụng để xác định phiên bản nội dung | vi-VN, en-US |
| Language Variant | Phiên bản nội dung theo một culture | Bài Lý Tự Nghiệp bằng tiếng Việt hoặc tiếng Anh |
Ngoài ra, cần phân biệt ngôn ngữ của nội dung với ngôn ngữ giao diện Backoffice. Người quản trị có thể sử dụng giao diện Backoffice bằng một ngôn ngữ trong khi quản lý nội dung website bằng nhiều ngôn ngữ khác nhau.
10. Bài tập thực hành
Để củng cố kiến thức, hãy thực hiện các bài tập sau trên project Bookstore.
- Tạo Document Type Blog, Blog Category và Blog Post.
- Cấu hình quan hệ cha – con để tạo được Content Tree theo mẫu.
- Thiết lập tiếng Việt là ngôn ngữ mặc định và thêm tiếng Anh.
- Bật variation cho Document Type Blog Post.
- Bật variation cho các property tiêu đề, tóm tắt và nội dung cần dịch.
- Tạo bài viết Lý Tự Nghiệp với nội dung tiếng Việt.
- Tạo phiên bản tiếng Anh của cùng bài viết và publish.
- Gọi Content Delivery API hai lần với hai giá trị
Accept-Languagekhác nhau. - So sánh JSON response và ghi lại các trường thay đổi, các trường dùng chung.
Bạn có thể ghi kết quả theo bảng sau:
| Trường | Tiếng Việt | Tiếng Anh |
|---|---|---|
name |
Ghi giá trị thực tế | Ghi giá trị thực tế |
summary |
Ghi giá trị thực tế | Ghi giá trị thực tế |
bodyText |
Ghi giá trị thực tế | Ghi giá trị thực tế |
chineseName |
Ghi giá trị thực tế | Ghi giá trị thực tế |
cultures |
Ghi response thực tế | Ghi response thực tế |
11. Tổng kết Day 5
Qua bài thực hành này, chúng ta đã mở rộng project Bookstore bằng một khu vực Blog và tìm hiểu cách quản lý nội dung đa ngôn ngữ trong Umbraco 13.
Các kiến thức quan trọng cần ghi nhớ:
- Language Variants cho phép một content item có nhiều phiên bản ngôn ngữ.
- Document Type cần cho phép variation để nội dung có thể thay đổi theo culture.
- Mỗi property cần được cấu hình phù hợp: thay đổi theo ngôn ngữ hoặc dùng chung giữa các ngôn ngữ.
- Nội dung của từng phiên bản cần được nhập và publish theo đúng culture.
- Content Delivery API hỗ trợ header
Accept-Languagekhi truy vấn nội dung theo GUID. - Không nên mặc định mọi property đều được dịch hoặc mọi phiên bản đều đã publish.
Điểm quan trọng nhất là hiểu rằng đa ngôn ngữ không nhất thiết đồng nghĩa với việc tạo nhiều content item riêng biệt. Với Language Variants, chúng ta có thể quản lý các phiên bản ngôn ngữ của cùng một nội dung ngay trong Umbraco.
12. Bài tiếp theo: Day 6 – Thiết kế dữ liệu đa ngôn ngữ cho ứng dụng
Trong Day 5, chúng ta đã sử dụng cơ chế đa ngôn ngữ có sẵn của Umbraco để quản lý bài viết CMS.
Tuy nhiên, không phải mọi dữ liệu của website đều là nội dung biên tập. Những dữ liệu nghiệp vụ như sản phẩm, thuộc tính sản phẩm, cấu hình hoặc danh mục do ứng dụng quản lý có thể cần một thiết kế đa ngôn ngữ riêng.
Trong Day 6, chúng ta sẽ tìm hiểu cách thiết kế dữ liệu đa ngôn ngữ cho ứng dụng, phân biệt dữ liệu dùng chung với dữ liệu phụ thuộc ngôn ngữ và xác định khi nào nên sử dụng Language Variants của CMS, khi nào nên thiết kế bảng translation riêng trong database.

Nhận xét