Hướng dẫn thực tế về cách viết các biểu thức CEL trong CMS.
Hướng dẫn thực tế về cách viết các biểu thức CEL trong CMS.
CEL (Common Expression Language) là một ngôn ngữ lập trình nhẹ được tích hợp trong CMS của chúng tôi. Ngôn ngữ này cho phép bạn viết các biểu thức động có thể lấy dữ liệu từ tài liệu, đọc tham số URL và tính toán giá trị ngay khi chạy.
Đây là những gì xảy ra khi một tập lệnh CEL chạy:
Tập lệnh của bạn Bộ máy Kết quả
| | |
v v v
documents.get("article", "intro") --> Lấy từ cơ sở dữ liệu --> { headline: "Welcome", body: "..." }
.headline --> Trích xuất trường --> "Welcome"
Hãy coi CEL như một ngôn ngữ truy vấn chỉ đọc. Nó không thể sửa đổi bất kỳ thứ gì trong cơ sở dữ liệu - nó chỉ đọc dữ liệu và trả về một kết quả đã tính toán. Điều này giúp CEL an toàn khi sử dụng ở bất kỳ đâu trong CMS.
Mỗi biểu thức CEL đều có quyền truy cập vào ba thành phần:
| Đối tượng | Là gì | Ví dụ |
|---|---|---|
documents | Lấy bất kỳ tài liệu nào từ CMS | documents.get("country", "us") |
meta | Thông tin về yêu cầu hiện tại (locale, tham số URL) | meta.locale, meta.params.slug |
schema | Định nghĩa các trường của tài liệu hiện tại | schema.fields |
docKhi viết biểu thức CEL trong trình chỉnh sửa tài liệu, bạn có thể truy cập các giá trị trường của tài liệu hiện tại bằng đối tượng doc. Điều này cho phép tạo các trường được tính toán và tham chiếu giữa các trường.
// Truy cập trường giá của tài liệu hiện tại
doc.price
// Tính tổng từ các trường của tài liệu hiện tại
doc.price * doc.quantity
// Điều kiện dựa trên trạng thái của tài liệu hiện tại
doc.status == "published" ? doc.title : "Draft: " + doc.title
Đối tượng doc chứa tất cả giá trị trường của tài liệu đang được chỉnh sửa. Đối tượng này hữu ích cho:
doc.price * doc.quantity)Tính năng mạnh mẽ nhất của CEL là lấy tài liệu từ bất kỳ đâu trong CMS.
Cú pháp: documents.get(schemaName, identifier)
Giả sử bạn có một tài liệu article được lưu với mã định danh "welcome-post":
// Được lưu trong CMS dưới dạng: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Để lấy toàn bộ tài liệu:
documents.get("article", "welcome-post")
Trả về:
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Để chỉ lấy tiêu đề:
documents.get("article", "welcome-post").headline
Trả về: "Welcome to Our Platform"
Để lấy tác giả:
documents.get("article", "welcome-post").author
Trả về: "Sarah Chen"
Khi trang của bạn có các route động (như /articles/[slug]), bạn có thể dùng meta.params để lấy tham số URL và lấy đúng tài liệu.
Nếu người dùng truy cập /articles/welcome-post:
documents.get("article", meta.params.slug).headline
Trả về: "Welcome to Our Platform"
Đây là cách xây dựng các trang động - cùng một tập lệnh CEL hoạt động cho mọi bài viết, chỉ cần sử dụng slug có trong URL.
Cú pháp: documents.find(schemaName) hoặc documents.find(schemaName, filter)
// Lấy tất cả quốc gia
documents.find("country")
Trả về:
[
{ "code": "us", "name": "United States", "flag": "US" },
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
{ "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Lấy quốc gia với bộ lọc
documents.find("country", { "where": { "code": "us" } })
Trả về:
[
{ "code": "us", "name": "United States", "flag": "US" }
]
CEL hỗ trợ lấy nội dung tài liệu đã dịch theo hai cách: dịch tự động dựa trên locale và tra cứu bản dịch rõ ràng.
Khi meta.locale được thiết lập (ví dụ từ tham số route hoặc tùy chọn của người dùng), documents.get() sẽ tự động hợp nhất nội dung đã dịch:
// Nếu meta.locale là "fr", trả về bản dịch tiếng Pháp được hợp nhất với tài liệu gốc
documents.get("greeting", "welcome").headline
Cách hoạt động:
meta.locale không phải là "en" hoặc "en-US", tìm bản dịch trong bảng translations{ ...baseContent, ...translatedContent }Điều này có nghĩa là các trường đã dịch sẽ ghi đè lên trường gốc, còn các trường chưa dịch sẽ dùng lại tài liệu gốc.
Trong những trường hợp cần lấy một bản dịch cụ thể bất kể locale hiện tại:
Cú pháp: documents.translated(schemaName, identifier, locale)
// Luôn lấy bản dịch tiếng Tây Ban Nha
documents.translated("greeting", "welcome", "es").headline
// Lấy bản dịch dựa trên tham số URL
documents.translated("product", meta.params.id, meta.params.lang).description
// So sánh các bản dịch
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
Các tài liệu lời chào cùng bản dịch:
// Tài liệu gốc: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }
// Bản dịch (ngôn ngữ: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }
// Bản dịch (ngôn ngữ: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }
Các tập lệnh CEL:
// Với meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Trả về: "Bienvenue"
// Bản dịch tiếng Tây Ban Nha rõ ràng
documents.translated("greeting", "welcome", "es").headline
// Trả về: "Bienvenido"
// Mẫu dự phòng khi thiếu bản dịch
documents.translated("greeting", "welcome", meta.params.lang) != null
? documents.translated("greeting", "welcome", meta.params.lang).headline
: documents.get("greeting", "welcome").headline
Bạn có một hero-block cần hiển thị tiêu đề lấy từ tài liệu article.
Tài liệu bài viết của bạn (mã định danh: "homepage-hero"):
{
"headline": "Build Faster, Ship Smarter",
"subheadline": "The modern CMS for developers"
}
Tập lệnh CEL trong trường tiêu đề của khối hero:
documents.get("article", "homepage-hero").headline
Kết quả: Hero hiển thị "Build Faster, Ship Smarter"
Bạn đang xây dựng trang tại /countries/[code] và muốn hiển thị tên quốc gia đầy đủ.
Các tài liệu quốc gia của bạn:
// country / us
{ "code": "us", "name": "United States", "flag": "US", "languages": ["en", "es"] }
// country / sa
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA", "languages": ["ar", "en"] }
Tập lệnh CEL:
documents.get("country", meta.params.code).name
Khi người dùng truy cập /countries/us:
meta.params.code = "us""United States"Khi người dùng truy cập /countries/sa:
meta.params.code = "sa""Saudi Arabia"Hiển thị các tiêu đề khác nhau dựa trên locale của người dùng.
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
Nếu locale là "ar-SA": Trả về "Welcome, everyone"
Nếu locale là giá trị khác: Trả về "Welcome"
Tài liệu article có trường countryCode và bạn muốn lấy tên quốc gia đầy đủ.
Tài liệu bài viết:
{ "headline": "News from the US", "countryCode": "us" }
Tập lệnh CEL:
documents.get("country", documents.get("article", "us-news").countryCode).name
Điều gì xảy ra:
documents.get("article", "us-news") trả về { "headline": "News from the US", "countryCode": "us" }.countryCode trích xuất "us"documents.get("country", "us") trả về { "code": "us", "name": "United States", ... }.name trích xuất "United States"Kết quả: "United States"
Nếu tài liệu có thể không tồn tại, bạn có thể cung cấp giá trị dự phòng:
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
Hoặc kiểm tra xem một trường cụ thể có tồn tại không:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Unknown Author"
Bài viết của bạn có các thẻ và bạn muốn kiểm tra xem một thẻ cụ thể có tồn tại không:
"featured" in documents.get("article", "welcome-post").tags
Trả về: true nếu bài viết có thẻ "featured"
Lấy thẻ đầu tiên:
documents.get("article", "welcome-post").tags[0]
Trả về: "announcement" (thẻ đầu tiên)
Đếm số thẻ:
size(documents.get("article", "welcome-post").tags)
Trả về: 2 (số lượng thẻ)
Route tham số là chìa khóa để xây dựng các trang động, bản địa hóa. Khi định nghĩa mẫu route như /{lang}/landingPage, CMS sẽ trích xuất các tham số từ URL và cung cấp chúng qua meta.params.
Định nghĩa mẫu route:
Route sử dụng cú pháp :paramName hoặc {paramName} để định nghĩa các phân đoạn động:
| Mẫu | URL ví dụ | Tham số được trích xuất |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
Liên kết tham số: Mỗi tham số route có thể được liên kết với một schema tài liệu để xác thực:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
Liên kết này cho CMS biết:
lang từ URLlanguage (tìm tài liệu có content.code khớp)Cấu hình route:
/{lang}/landingPage/{lang}/landingPage{ "lang": "language" }Các tài liệu lời chào:
// greeting / ko
{ "code": "ko", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/ko/get-started" }
// greeting / en
{ "code": "en", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/en/get-started" }
// greeting / ja
{ "code": "ja", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Start", "ctaUrl": "/ja/get-started" }
Tập lệnh CEL để lấy nội dung bản địa hóa:
documents.get("greeting", meta.params.lang).headline
Cách phân giải:
| URL | meta.params.lang | Kết quả |
|---|---|---|
/ko/landingPage | "ko" | "Welcome" |
/en/landingPage | "en" | "Welcome" |
/ja/landingPage | "ja" | "Welcome" |
Đối với các route như /{country}/{lang}/products:
Cấu hình route:
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
Các tập lệnh CEL:
// Lấy tên quốc gia
documents.get("country", meta.params.country).name
// Lấy danh sách sản phẩm dựa trên quốc gia
documents.find("product", { "where": { "country": meta.params.country } })
// Kết hợp: Hiển thị lời chào theo quốc gia bằng ngôn ngữ của người dùng
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name
Chuỗi xác thực:
CMS xác thực các tham số theo thứ bậc. Đối với route /{country}/{lang}:
country với schema countrylang với schema languagelang nằm trong mảng country.languages[] (xác thực phân cấp)meta.segments cung cấp đường dẫn URL thô dưới dạng một mảng, hữu ích khi cần truy cập theo vị trí mà không có tham số được đặt tên.
Cách hoạt động:
| Đường dẫn URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| Trường hợp sử dụng | Cách tiếp cận tốt nhất |
|---|---|
| Tham số được đặt tên từ mẫu route | meta.params.lang |
| Truy cập theo vị trí | meta.segments[0] |
| Lấy độ sâu đường dẫn | size(meta.segments) |
| Kiểm tra đường dẫn có chứa một phân đoạn | "admin" in meta.segments |
// Lấy phân đoạn đầu tiên (thường là mã ngôn ngữ)
meta.segments[0]
// Kiểm tra độ sâu đường dẫn
size(meta.segments) > 2 ? "deep" : "shallow"
// Kiểm tra xem có đang ở khu vực quản trị không
"admin" in meta.segments ? "admin mode" : "public mode"
// Dự phòng: Dùng phân đoạn nếu tham số chưa được liên kết
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
Đối tượng meta chứa toàn bộ ngữ cảnh của yêu cầu hiện tại:
| Thuộc tính | Kiểu | Mô tả | |
|---|---|---|---|
meta.locale | string | Mã locale hiện tại (ví dụ: "en-US", "ko-KR", "ar-SA") | |
meta.params | Record<string, string> | Các tham số route được trích xuất từ mẫu URL | |
meta.segments | string[] | Đường dẫn URL được tách thành các phân đoạn | |
meta.docId | `string \ | null` | UUID của tài liệu hiện tại (null đối với tài liệu mới) |
meta.title | string | Tiêu đề tài liệu hiện tại |
Mã locale tuân theo định dạng BCP 47 (ngôn ngữ-khu vực):
// Kiểm tra locale cho các ngôn ngữ RTL
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// Chỉ lấy phần ngôn ngữ
meta.locale.split("-")[0] // Không được hỗ trợ - hãy dùng meta.params.lang
Các tham số route luôn là chuỗi. CMS sẽ xác thực chúng với các schema đã liên kết trước khi đánh giá:
// Truy cập tham số được đặt tên
meta.params.lang // "ko"
meta.params.country // "us"
meta.params.slug // "welcome-post"
// Kiểm tra tham số có tồn tại không
has(meta.params.category) // true/false
// Dùng trong truy vấn tài liệu
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
Các phân đoạn URL thô dưới dạng mảng:
// Truy cập theo chỉ mục (bắt đầu từ 0)
meta.segments[0] // Phân đoạn đầu tiên
meta.segments[1] // Phân đoạn thứ hai
// Kiểm tra độ dài
size(meta.segments) // Số lượng phân đoạn
// Kiểm tra thành phần
"products" in meta.segments // Đường dẫn có chứa "products" không?
UUID của tài liệu hiện tại, hữu ích cho các tập lệnh tự tham chiếu:
// Chỉ khả dụng khi chỉnh sửa tài liệu hiện có
meta.docId != null ? "editing" : "creating new"
// Dùng trong logic điều kiện
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
Tiêu đề của tài liệu hiện tại:
// Dùng để hiển thị
"Editing: " + meta.title
// Điều kiện dựa trên tiêu đề
meta.title.contains("Draft") ? "work in progress" : "published"
Để có cú pháp rõ ràng hơn khi schema đã biết nhưng mã định danh là động:
// Cách truyền thống
documents.get("airports", meta.params.code).name
// Dùng ref() - tách schema khỏi mã định danh động
documents.ref("airports").get(meta.params.code).name
Cả hai cách là tương đương, nhưng ref() làm phần động trở nên rõ ràng hơn.
documents.get("schema", "identifier") // Lấy một tài liệu
documents.get("schema", "id").fieldName // Lấy một trường cụ thể
documents.find("schema") // Lấy tất cả tài liệu
documents.find("schema", { "where": {...}}) // Truy vấn có bộ lọc
documents.ref("schema").get(identifier) // Tra cứu theo chuỗi
documents.translated("schema", "id", "fr") // Lấy với locale rõ ràng
meta.locale // "en-US", "ar-SA", v.v.
meta.params.xyz // Tham số URL có tên "xyz"
meta.segments // Đường dẫn URL dưới dạng mảng: ["articles", "intro"]
meta.segments[0] // Phân đoạn đường dẫn đầu tiên
meta.docId // ID tài liệu hiện tại (hoặc null)
meta.title // Tiêu đề tài liệu hiện tại
doc.fieldName // Giá trị trường của tài liệu hiện tại (trong ngữ cảnh trình chỉnh sửa)
// So sánh
== != < <= > >=
// Logic
&& || !
// Toán tử ba ngôi (nếu-thì-không thì)
condition ? valueIfTrue : valueIfFalse
// Kiểm tra thành phần
"value" in listOrMap
size(list) // Đếm số phần tử
size(string) // Độ dài chuỗi
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // Kiểm tra thuộc tính có tồn tại không
hasProperty(obj, "key") // Kiểm tra đối tượng có khóa không (cú pháp thay thế)
Nếu xảy ra sự cố, bạn sẽ thấy một trong các lỗi sau:
| Lỗi | Ý nghĩa |
|---|---|
SYNTAX_ERROR | Lỗi chính tả trong tập lệnh (thiếu dấu ngoặc kép, toán tử không hợp lệ) |
TYPE_ERROR | Bạn đang kết hợp các kiểu dữ liệu không tương thích |
RUNTIME_ERROR | Tập lệnh đã chạy nhưng gặp sự cố (biến không xác định) |
FETCH_LIMIT_EXCEEDED | Bạn đang lấy quá nhiều tài liệu (tối đa 50) |
TIMEOUT | Tập lệnh chạy quá lâu (tối đa 5 giây) |
AST_DEPTH_EXCEEDED | Biểu thức lồng quá sâu (độ sâu tối đa: 50) |
SCRIPT_TOO_LONG | Tập lệnh vượt quá giới hạn 5000 ký tự |
Bộ máy CEL được thiết kế để có thể mở rộng. Các khả năng dự kiến trong tương lai bao gồm:
// Tương lai: Gọi các dịch vụ bên ngoài qua MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// Tương lai: Tạo nội dung bằng AI
ai.summarize(documents.get("article", meta.params.id).body, 100)
ai.translate(meta.params.text, meta.params.targetLang)
ai.classify(meta.params.input, ["positive", "negative", "neutral"])
Các khả năng này sẽ được bổ sung thông qua hệ thống hàm đã đăng ký, đồng thời duy trì khả năng tương thích ngược với các tập lệnh hiện có.
documents. hoặc meta. và trình chỉnh sửa sẽ hiển thị các tùy chọn có sẵndocuments.get("schema", "id"), sau đó thêm .fieldName!= null ? ... : ...documents.get() hoặc documents.find() đều được tính vào giới hạn 50 lần lấyhas(meta.params.category) trước khi truy cậpPhần này trình bày các mẫu nâng cao để liên kết các tài liệu với nhau và xây dựng cấu trúc nội dung quan hệ.
Dạng đơn giản nhất: một tài liệu tham chiếu đến tài liệu khác bằng mã định danh.
// Bài viết lưu ID tác giả, lấy tên tác giả
documents.get("author", documents.get("article", "intro").authorId).name
Để có cú pháp rõ ràng hơn khi mã định danh là động:
// Cách truyền thống
documents.get("country", documents.get("airport", meta.params.code).countryCode).name
// Dùng ref() - rõ ràng hơn khi schema đã biết nhưng mã định danh là động
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name
Xây dựng các quan hệ sâu bằng cách nối nhiều lần tra cứu:
// Sân bay → Quốc gia → Khu vực → Châu lục
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
Kết hợp tham chiếu tài liệu với bản dịch:
// Lấy tên quốc gia đã bản địa hóa cho một sân bay
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
Tài liệu lưu ID tham chiếu đến một tài liệu khác.
// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// Phân giải tên tác giả
documents.get("author", documents.get("article", meta.params.slug).authorId).name
// Phân giải danh mục với giá trị dự phòng
documents.get("article", meta.params.slug).categoryId != null
? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
: "Uncategorized"
Các tài liệu tham chiếu lẫn nhau bằng mã ngữ nghĩa thay vì UUID.
// airport / JFK
{ "code": "JFK", "name": "John F. Kennedy International", "countryCode": "us" }
// country / us
{ "code": "us", "name": "United States", "currencyCode": "usd" }
// currency / usd
{ "code": "usd", "symbol": "$", "name": "US Dollar" }
// Chuỗi Sân bay → Quốc gia → Tiền tệ
documents.get("currency",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).currencyCode
).symbol
// Với JFK: Trả về "$"
Dùng doc cho các trường được tính toán tham chiếu đến tài liệu khác dựa trên giá trị của tài liệu hiện tại.
// Trong tài liệu sản phẩm, lấy thông tin chi tiết danh mục liên quan
documents.get("category", doc.categoryId).description
// Chi phí vận chuyển được tính dựa trên quốc gia xuất xứ của sản phẩm
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight
Khi các tài liệu tham chiếu lẫn nhau, hãy cẩn thận với giới hạn lấy dữ liệu.
// Lấy tác giả của bài viết, sau đó lấy các bài viết khác của tác giả (hãy chú ý số lần lấy!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })
Khi một trường có thể tham chiếu đến các schema khác nhau:
// content-block / hero-1
{ "type": "hero", "sourceType": "article", "sourceId": "welcome-post" }
// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Tra cứu schema động dựa trên sourceType
documents.get("content-block", "hero-1").sourceType == "article"
? documents.get("article", documents.get("content-block", "hero-1").sourceId).headline
: documents.get("product", documents.get("content-block", "hero-1").sourceId).name
Mọi lời gọi documents.get(), documents.find() và documents.ref().get() đều được theo dõi để vô hiệu hóa bộ nhớ đệm. Khi một tài liệu được tham chiếu thay đổi, CMS biết những biểu thức CEL nào cần được đánh giá lại.
Các phụ thuộc được theo dõi gồm:
schema:identifier - Phụ thuộc vào một tài liệu cụ thểschema:identifier - Giống get, thông qua cú pháp chuỗischema:* - Phụ thuộc cấp schema (bất kỳ tài liệu nào trong schema)// Không tốt: Lấy cùng một tài liệu hai lần
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio
// Tốt hơn: Dùng điều kiện để kiểm tra một lần
documents.get("article", "intro").authorId != null
? documents.get("author", documents.get("article", "intro").authorId).name
: "Unknown Author"
Phần hướng dẫn này tạo một trang đích đa ngôn ngữ có thể truy cập tại /{lang}/landingPage.
Trong trang quản trị CMS, tạo schema tùy chỉnh có tên greeting:
{
"name": "greeting",
"fields": [
{ "name": "code", "type": "string", "required": true },
{ "name": "headline", "type": "string", "required": true },
{ "name": "subheadline", "type": "string" },
{ "name": "ctaText", "type": "string" },
{ "name": "ctaUrl", "type": "string" }
]
}
Tạo tài liệu cho từng ngôn ngữ:
Tài liệu: greeting/ko
{
"code": "ko",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Get Started",
"ctaUrl": "/ko/get-started"
}
Tài liệu: greeting/en
{
"code": "en",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Get Started",
"ctaUrl": "/en/get-started"
}
Tài liệu: greeting/ja
{
"code": "ja",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Start",
"ctaUrl": "/ja/get-started"
}
Tạo một trang với cấu hình sau:
/{lang}/landingPagelang → thành phần language {
"lang": "language"
}
Thêm một khối hero vào route với các tập lệnh CEL sau cho từng trường:
Trường Headline:
documents.get("greeting", meta.params.lang).headline
Trường Subheadline:
documents.get("greeting", meta.params.lang).subheadline
Trường CTA Text:
documents.get("greeting", meta.params.lang).ctaText
Trường CTA URL:
documents.get("greeting", meta.params.lang).ctaUrl
Thêm một route bắt tất cả. ParametricRoutePage phân giải trang, trích xuất meta.params từ URL, đánh giá các liên kết CEL ở phía máy chủ và hiển thị từng khối thông qua registry của bạn — bạn không cần tự xây dựng ngữ cảnh meta hoặc gọi client cấp thấp.
// app/[...slug]/page.tsx
import ParametricRoutePage from 'cms-renderer/lib/renderer';
import { registry } from '@/lib/registry';
import { cmsConfig } from '@/lib/cms-config';
export const dynamic = 'force-static';
interface PageProps {
params: Promise<{ slug: string[] }>;
}
export default async function Page({ params }: PageProps) {
const { slug } = await params;
return (
<ParametricRoutePage
registry={registry}
apiKey={cmsConfig.apiKey}
websiteId={cmsConfig.websiteId}
cmsUrl={cmsConfig.cmsUrl}
params={Promise.resolve({ slug })}
/>
);
}
Truy cập các URL sau để xem nội dung bản địa hóa:
| URL | Tiêu đề dự kiến |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
Khi người dùng truy cập /ko/landingPage:
/{lang}/landingPagemeta.params.lang = "ko"languagedocuments.get("greeting", meta.params.lang) được phân giải thành nội dung tiếng Hàninterface CelMeta {
/** Mã locale hiện tại (ví dụ: 'en-US') */
locale: string;
/** Các tham số route được trích xuất từ URL */
params: Record<string, string>;
/** Các phân đoạn đường dẫn URL */
segments: string[];
/** ID tài liệu hiện tại (nếu đang chỉnh sửa tài liệu hiện có) */
docId: string | null;
/** Tiêu đề tài liệu hiện tại */
title: string;
}
Hàm extractParams xử lý các đường dẫn URL:
Mẫu: /{country}/{lang}/products
Đường dẫn: /us/en/products
Thuật toán:
1. Chuẩn hóa cả hai (xóa dấu gạch chéo ở cuối)
2. Tách thành các phân đoạn: ["us", "en", "products"] và ["{country}", "{lang}", "products"]
3. So khớp số lượng phân đoạn (phải bằng nhau)
4. Với mỗi cặp phân đoạn:
- Nếu mẫu bắt đầu bằng : hoặc {}, trích xuất thành tham số
- Nếu không, phải khớp chính xác
5. Trả về: { country: "us", lang: "en" }
// Liên kết đơn giản (dùng trường "code" để tra cứu)
{ "lang": "language" }
// Liên kết chi tiết (trường slug tùy chỉnh)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
Khi lấy dữ liệu qua documents.get(schema, identifier):
idcontent.codecontent.slugtitleĐiều này cho phép tham chiếu tài liệu linh hoạt bằng bất kỳ mã định danh duy nhất nào.