دليل عملي لكتابة تعبيرات CEL في نظام إدارة المحتوى.
دليل عملي لكتابة تعبيرات CEL في نظام إدارة المحتوى.
CEL (لغة التعبيرات العامة) هي لغة برمجة نصية خفيفة مدمجة في نظام إدارة المحتوى لدينا. تتيح لك كتابة تعبيرات ديناميكية يمكنها جلب البيانات من المستندات، وقراءة معلمات عناوين URL، وحساب القيم أثناء التشغيل.
إليك ما يحدث عند تشغيل نص CEL:
نصك البرمجي المحرك النتيجة
| | |
v v v
documents.get("article", "intro") --> يجلب من قاعدة البيانات --> { headline: "Welcome", body: "..." }
.headline --> يستخرج الحقل --> "Welcome"
فكّر في CEL كلغة استعلام للقراءة فقط. لا يمكنها تعديل أي شيء في قاعدة البيانات، بل تقرأ البيانات فقط وتُرجع نتيجة محسوبة. وهذا يجعل استخدامها آمنًا في أي مكان داخل نظام إدارة المحتوى.
كل تعبير CEL يمكنه الوصول إلى ثلاثة أشياء:
| الكائن | ماهيته | مثال |
|---|---|---|
documents | جلب أي مستند من نظام إدارة المحتوى | documents.get("country", "us") |
meta | معلومات حول الطلب الحالي (الإعدادات المحلية ومعلمات URL) | meta.locale, meta.params.slug |
schema | تعريفات حقول المستند الحالي | schema.fields |
docعند كتابة تعبيرات CEL داخل محرر المستندات، يمكنك الوصول إلى قيم حقول المستند الحالي باستخدام الكائن doc. يتيح ذلك إنشاء الحقول المحسوبة والإشارة إلى الحقول الأخرى.
// الوصول إلى حقل السعر في المستند الحالي
doc.price
// حساب الإجمالي من حقول المستند الحالي
doc.price * doc.quantity
// شرط يعتمد على حالة المستند الحالي
doc.status == "published" ? doc.title : "Draft: " + doc.title
يحتوي الكائن doc على جميع قيم الحقول من المستند الذي يتم تحريره. وهذا مفيد من أجل:
doc.price * doc.quantity)أقوى ميزة في CEL هي جلب المستندات من أي مكان في نظام إدارة المحتوى.
الصيغة: documents.get(schemaName, identifier)
لنفترض أن لديك مستند article مخزنًا بالمعرّف "welcome-post":
// مخزن في نظام إدارة المحتوى باسم: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
لجلب المستند بالكامل:
documents.get("article", "welcome-post")
يُرجع:
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
لجلب العنوان فقط:
documents.get("article", "welcome-post").headline
يُرجع: "Welcome to Our Platform"
لجلب اسم المؤلف:
documents.get("article", "welcome-post").author
يُرجع: "Sarah Chen"
عندما تحتوي صفحتك على مسارات ديناميكية (مثل /articles/[slug])، يمكنك استخدام meta.params للحصول على معلمة URL وجلب المستند الصحيح.
إذا زار أحدهم /articles/welcome-post:
documents.get("article", meta.params.slug).headline
يُرجع: "Welcome to Our Platform"
هكذا تنشئ صفحات ديناميكية؛ إذ يعمل نص CEL نفسه مع أي مقالة، باستخدام قيمة slug الموجودة في عنوان URL.
الصيغة: documents.find(schemaName) أو documents.find(schemaName, filter)
// الحصول على جميع الدول
documents.find("country")
يُرجع:
[
{ "code": "us", "name": "United States", "flag": "US" },
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
{ "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// الحصول على الدول باستخدام عامل تصفية
documents.find("country", { "where": { "code": "us" } })
يُرجع:
[
{ "code": "us", "name": "United States", "flag": "US" }
]
تدعم CEL جلب محتوى المستندات المترجم بطريقتين: الترجمة التلقائية حسب الإعدادات المحلية، والبحث الصريح عن الترجمة.
عند تعيين meta.locale (مثلًا من معلمات المسار أو تفضيلات المستخدم)، تدمج documents.get() المحتوى المترجم تلقائيًا:
// إذا كانت قيمة meta.locale هي "fr"، يُرجع الترجمة الفرنسية مدمجة مع المستند الأساسي
documents.get("greeting", "welcome").headline
آلية العمل:
meta.locale هي "en" أو "en-US"، البحث عن الترجمة في جدول translations{ ...baseContent, ...translatedContent }هذا يعني أن الحقول المترجمة تتجاوز الحقول الأساسية، بينما تعود الحقول غير المترجمة إلى المستند الأساسي.
في الحالات التي تحتاج فيها إلى جلب ترجمة محددة بغض النظر عن الإعداد المحلي الحالي:
الصيغة: documents.translated(schemaName, identifier, locale)
// جلب الترجمة الإسبانية دائمًا
documents.translated("greeting", "welcome", "es").headline
// جلب الترجمة بناءً على معلمة URL
documents.translated("product", meta.params.id, meta.params.lang).description
// مقارنة الترجمات
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
مستندات التحية مع ترجماتها:
// المستند الأساسي: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }
// الترجمة (اللغة: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }
// الترجمة (اللغة: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }
نصوص CEL:
// مع meta.locale = "fr"
documents.get("greeting", "welcome").headline
// يُرجع: "Bienvenue"
// الترجمة الإسبانية الصريحة
documents.translated("greeting", "welcome", "es").headline
// يُرجع: "Bienvenido"
// نمط بديل للترجمات المفقودة
documents.translated("greeting", "welcome", meta.params.lang) != null
? documents.translated("greeting", "welcome", meta.params.lang).headline
: documents.get("greeting", "welcome").headline
لديك hero-block يجب أن يعرض عنوانًا مأخوذًا من مستند article.
مستند المقالة (المعرّف: "homepage-hero"):
{
"headline": "Build Faster, Ship Smarter",
"subheadline": "The modern CMS for developers"
}
نص CEL في حقل عنوان كتلة Hero:
documents.get("article", "homepage-hero").headline
النتيجة: تعرض كتلة Hero القيمة "Build Faster, Ship Smarter"
أنت تبني صفحة على /countries/[code] وتريد عرض اسم الدولة كاملًا.
مستندات الدول:
// country / us
{ "code": "us", "name": "United States", "flag": "US", "languages": ["en", "es"] }
// country / sa
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA", "languages": ["ar", "en"] }
نص CEL:
documents.get("country", meta.params.code).name
عند زيارة /countries/us:
meta.params.code = "us""United States"عند زيارة /countries/sa:
meta.params.code = "sa""Saudi Arabia"اعرض عناوين مختلفة بناءً على الإعداد المحلي للمستخدم.
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
إذا كان الإعداد المحلي "ar-SA": يُرجع "Welcome, everyone"
إذا كان الإعداد المحلي أي قيمة أخرى: يُرجع "Welcome"
تحتوي مقالتك على حقل countryCode، وتريد الحصول على اسم الدولة كاملًا.
مستند المقالة:
{ "headline": "News from the US", "countryCode": "us" }
نص CEL:
documents.get("country", documents.get("article", "us-news").countryCode).name
ما يحدث:
documents.get("article", "us-news") القيمة { "headline": "News from the US", "countryCode": "us" }.countryCode القيمة "us"documents.get("country", "us") القيمة { "code": "us", "name": "United States", ... }.name القيمة "United States"النتيجة: "United States"
إذا كان من المحتمل ألا يكون المستند موجودًا، يمكنك توفير قيمة بديلة:
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
أو تحقق من وجود حقل محدد:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Unknown Author"
تحتوي مقالتك على وسوم، وتريد التحقق من وجود وسم محدد:
"featured" in documents.get("article", "welcome-post").tags
يُرجع: true إذا كانت المقالة تحتوي على وسم "featured"
الحصول على الوسم الأول:
documents.get("article", "welcome-post").tags[0]
يُرجع: "announcement" (الوسم الأول)
حساب عدد الوسوم:
size(documents.get("article", "welcome-post").tags)
يُرجع: 2 (عدد الوسوم)
المسارات المعلمية هي المفتاح لبناء صفحات ديناميكية ومحلية. عند تعريف نمط مسار مثل /{lang}/landingPage، يستخرج نظام إدارة المحتوى المعلمات من عنوان URL ويتيحها عبر meta.params.
تعريف نمط المسار:
تستخدم المسارات الصيغة :paramName أو {paramName} لتعريف المقاطع الديناميكية:
| النمط | مثال على URL | المعلمات المستخرجة |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
ربط المعلمات: يمكن ربط كل معلمة مسار بمخطط مستند للتحقق من صحتها:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
يُخبر هذا الربط نظام إدارة المحتوى بما يلي:
lang من عنوان URLlanguage (البحث عن مستند يطابق فيه content.code القيمة)إعداد المسار:
/{lang}/landingPage/{lang}/landingPage{ "lang": "language" }مستندات التحية:
// 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" }
نص CEL لجلب المحتوى المحلي:
documents.get("greeting", meta.params.lang).headline
كيفية الحل:
| URL | meta.params.lang | النتيجة |
|---|---|---|
/ko/landingPage | "ko" | "Welcome" |
/en/landingPage | "en" | "Welcome" |
/ja/landingPage | "ja" | "Welcome" |
للمسارات مثل /{country}/{lang}/products:
إعداد المسار:
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
نصوص CEL:
// الحصول على اسم الدولة
documents.get("country", meta.params.country).name
// الحصول على قائمة المنتجات المحلية بناءً على الدولة
documents.find("product", { "where": { "country": meta.params.country } })
// مدمج: عرض تحية خاصة بالدولة بلغة المستخدم
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name
تسلسل التحقق:
يتحقق نظام إدارة المحتوى من المعلمات هرميًا. بالنسبة إلى مسارات /{country}/{lang}:
country مقابل مخطط countrylang مقابل مخطط languagelang موجودة في مصفوفة country.languages[] (تحقق هرمي)يوفر meta.segments مسار URL الخام على هيئة مصفوفة، وهو مفيد عندما تحتاج إلى الوصول الموضعي دون استخدام معلمات مسماة.
آلية العمل:
| مسار URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| حالة الاستخدام | الأسلوب الأفضل |
|---|---|
| المعلمات المسماة من نمط المسار | meta.params.lang |
| الوصول حسب الموضع | meta.segments[0] |
| الحصول على عمق المسار | size(meta.segments) |
| التحقق من احتواء المسار على مقطع | "admin" in meta.segments |
// الحصول على المقطع الأول (غالبًا رمز اللغة)
meta.segments[0]
// التحقق من عمق المسار
size(meta.segments) > 2 ? "deep" : "shallow"
// التحقق مما إذا كنا في قسم الإدارة
"admin" in meta.segments ? "admin mode" : "public mode"
// بديل: استخدام المقطع إذا لم تكن المعلمة مرتبطة
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
يحتوي الكائن meta على كل السياق المتعلق بالطلب الحالي:
| الخاصية | النوع | الوصف | |
|---|---|---|---|
meta.locale | string | رمز الإعداد المحلي الحالي (مثل "en-US" و"ko-KR" و"ar-SA") | |
meta.params | Record<string, string> | معلمات المسار المستخرجة من نمط URL | |
meta.segments | string[] | مسار URL مقسم إلى مقاطع | |
meta.docId | `string \ | null` | المعرّف الفريد العام للمستند الحالي (فارغ للمستندات الجديدة) |
meta.title | string | عنوان المستند الحالي |
يتبع رمز الإعداد المحلي صيغة BCP 47 (اللغة-المنطقة):
// التحقق من الإعداد المحلي للغات التي تُكتب من اليمين إلى اليسار
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// الحصول على جزء اللغة فقط
meta.locale.split("-")[0] // غير مدعوم - استخدم meta.params.lang بدلًا منه
تكون معلمات المسار دائمًا سلاسل نصية. يتحقق نظام إدارة المحتوى منها مقابل المخططات المرتبطة قبل التقييم:
// الوصول إلى معلمة مسماة
meta.params.lang // "ko"
meta.params.country // "us"
meta.params.slug // "welcome-post"
// التحقق من وجود المعلمة
has(meta.params.category) // true/false
// استخدامها في جلب مستند
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
مقاطع URL الخام على هيئة مصفوفة:
// الوصول حسب الفهرس (يبدأ من 0)
meta.segments[0] // المقطع الأول
meta.segments[1] // المقطع الثاني
// التحقق من الطول
size(meta.segments) // عدد المقاطع
// التحقق من العضوية
"products" in meta.segments // هل يحتوي المسار على "products"؟
المعرّف الفريد العام للمستند الحالي، وهو مفيد للنصوص التي تشير إلى نفسها:
// متاح فقط عند تحرير مستندات موجودة
meta.docId != null ? "editing" : "creating new"
// استخدامه في منطق شرطي
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
عنوان المستند الحالي:
// استخدامه للعرض
"Editing: " + meta.title
// شرط مبني على العنوان
meta.title.contains("Draft") ? "work in progress" : "published"
لصياغة أوضح عندما يكون المخطط معروفًا لكن المعرّف ديناميكيًا:
// الأسلوب التقليدي
documents.get("airports", meta.params.code).name
// استخدام ref() - فصل المخطط عن المعرّف الديناميكي
documents.ref("airports").get(meta.params.code).name
كلا الأسلوبين متكافئان، لكن ref() يجعل الجزء الديناميكي أكثر وضوحًا.
documents.get("schema", "identifier") // الحصول على مستند واحد
documents.get("schema", "id").fieldName // الحصول على حقل محدد
documents.find("schema") // الحصول على جميع المستندات
documents.find("schema", { "where": {...}}) // استعلام مع تصفية
documents.ref("schema").get(identifier) // بحث متسلسل
documents.translated("schema", "id", "fr") // الحصول على المستند بإعداد محلي محدد
meta.locale // "en-US" و"ar-SA" وغيرهما
meta.params.xyz // معلمة URL المسماة "xyz"
meta.segments // مسار URL كمصفوفة: ["articles", "intro"]
meta.segments[0] // أول مقطع في المسار
meta.docId // معرّف المستند الحالي (أو null)
meta.title // عنوان المستند الحالي
doc.fieldName // قيمة حقل المستند الحالي (في سياق المحرر)
// المقارنة
== != < <= > >=
// المنطق
&& || !
// الثلاثي (إذا-وإلا)
condition ? valueIfTrue : valueIfFalse
// العضوية
"value" in listOrMap
size(list) // عد العناصر
size(string) // طول السلسلة النصية
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // التحقق من وجود الخاصية
hasProperty(obj, "key") // التحقق من احتواء الكائن على المفتاح (صيغة بديلة)
إذا حدث خطأ، فسترى إحدى الرسائل التالية:
| الخطأ | معناه |
|---|---|
SYNTAX_ERROR | خطأ مطبعي في النص البرمجي (علامة اقتباس مفقودة أو عامل غير صحيح) |
TYPE_ERROR | خلط أنواع لا تعمل معًا |
RUNTIME_ERROR | تم تشغيل النص البرمجي لكنه واجه مشكلة (متغير غير معرّف) |
FETCH_LIMIT_EXCEEDED | جلب عدد كبير جدًا من المستندات (الحد الأقصى 50) |
TIMEOUT | استغرق النص البرمجي وقتًا طويلًا (5 ثوانٍ كحد أقصى) |
AST_DEPTH_EXCEEDED | التعبير متداخل بعمق كبير (الحد الأقصى للعمق: 50) |
SCRIPT_TOO_LONG | يتجاوز النص البرمجي حد 5000 حرف |
صُمم محرك CEL ليكون قابلًا للتوسعة. تشمل الإمكانات المستقبلية المخطط لها ما يلي:
// مستقبلًا: استدعاء خدمات خارجية عبر MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// مستقبلًا: إنشاء محتوى مدعوم بالذكاء الاصطناعي
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"])
ستُضاف هذه الإمكانات عبر نظام الدوال المسجلة، مع الحفاظ على التوافق العكسي مع النصوص البرمجية الحالية.
documents. أو meta. ليعرض المحرر الخيارات المتاحةdocuments.get("schema", "id")، ثم أضف .fieldName!= null ? ... : ...documents.get() أو documents.find() ضمن حد الجلب البالغ 50has(meta.params.category) قبل الوصول إليهايغطي هذا القسم الأنماط المتقدمة لربط المستندات معًا وبناء هياكل محتوى علائقية.
أبسط صورة: يشير مستند إلى مستند آخر باستخدام المعرّف.
// يخزن المقال معرّف المؤلف، ثم يجلب اسم المؤلف
documents.get("author", documents.get("article", "intro").authorId).name
لصياغة أوضح عندما يكون المعرّف ديناميكيًا:
// الأسلوب التقليدي
documents.get("country", documents.get("airport", meta.params.code).countryCode).name
// استخدام ref() - أوضح عندما يكون المخطط معروفًا لكن المعرّف ديناميكيًا
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name
أنشئ علاقات عميقة بربط عمليات بحث متعددة:
// المطار → الدولة → المنطقة → القارة
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
ادمج مراجع المستندات مع الترجمات:
// الحصول على اسم الدولة المحلي لمطار
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
يخزن المستند معرّفًا يشير إلى مستند آخر.
// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// حل اسم المؤلف
documents.get("author", documents.get("article", meta.params.slug).authorId).name
// حل الفئة مع قيمة بديلة
documents.get("article", meta.params.slug).categoryId != null
? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
: "Uncategorized"
تشير المستندات إلى بعضها باستخدام رموز دلالية بدلًا من المعرّفات الفريدة العامة.
// 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" }
// سلسلة المطار → الدولة → العملة
documents.get("currency",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).currencyCode
).symbol
// بالنسبة إلى JFK: تُرجع "$"
استخدم doc للحقول المحسوبة التي تشير إلى مستندات أخرى بناءً على قيم المستند الحالي.
// في مستند منتج، جلب تفاصيل الفئة المرتبطة
documents.get("category", doc.categoryId).description
// تكلفة الشحن المحسوبة بناءً على بلد منشأ المنتج
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight
عندما تشير المستندات إلى بعضها، انتبه إلى حدود الجلب.
// جلب مؤلف المقالة، ثم جلب مقالات المؤلف الأخرى (راقب عدد عمليات الجلب!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })
عندما يمكن لحقل أن يشير إلى مخططات مختلفة:
// content-block / hero-1
{ "type": "hero", "sourceType": "article", "sourceId": "welcome-post" }
// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// البحث في المخطط ديناميكيًا بناءً على 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
يُتتبع كل استدعاء لـ documents.get() وdocuments.find() وdocuments.ref().get() بغرض إبطال ذاكرة التخزين المؤقت. وعندما يتغير مستند مشار إليه، يعرف نظام إدارة المحتوى تعبيرات CEL التي تحتاج إلى إعادة التقييم.
تشمل التبعيات المتتبعة:
schema:identifier - تبعية لمستند محددschema:identifier - مثل get، عبر الصياغة المتسلسلةschema:* - تبعية على مستوى المخطط (أي مستند في المخطط)// سيئ: يجلب المستند نفسه مرتين
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio
// أفضل: استخدم شرطًا للتحقق مرة واحدة
documents.get("article", "intro").authorId != null
? documents.get("author", documents.get("article", "intro").authorId).name
: "Unknown Author"
تُنشئ هذه الجولة الإرشادية صفحة هبوط متعددة اللغات يمكن الوصول إليها عبر /{lang}/landingPage.
في لوحة إدارة نظام إدارة المحتوى، أنشئ مخططًا مخصصًا باسم 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" }
]
}
أنشئ مستندًا لكل لغة:
المستند: 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"
}
أنشئ صفحة بالإعدادات التالية:
/{lang}/landingPagelang بمكوّن language {
"lang": "language"
}
أضف كتلة Hero إلى المسار مع نصوص CEL التالية لكل حقل:
حقل العنوان:
documents.get("greeting", meta.params.lang).headline
حقل العنوان الفرعي:
documents.get("greeting", meta.params.lang).subheadline
حقل نص زر CTA:
documents.get("greeting", meta.params.lang).ctaText
حقل عنوان URL لزر CTA:
documents.get("greeting", meta.params.lang).ctaUrl
أضف مسارًا شاملًا. يحل ParametricRoutePage الصفحة، ويستخرج meta.params من عنوان URL، ويقيّم روابط CEL على جانب الخادم، ويعرض كل كتلة عبر السجل الخاص بك؛ ولا تحتاج إلى إنشاء سياق meta أو استدعاء العميل منخفض المستوى بنفسك.
// 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 })}
/>
);
}
زر عناوين URL التالية لرؤية المحتوى المحلي:
| URL | العنوان المتوقع |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
عندما يزور المستخدم /ko/landingPage:
/{lang}/landingPagemeta.params.lang = "ko"languagedocuments.get("greeting", meta.params.lang) إلى المحتوى الكوريinterface CelMeta {
/** رمز الإعداد المحلي الحالي (مثل 'en-US') */
locale: string;
/** معلمات المسار المستخرجة من URL */
params: Record<string, string>;
/** مقاطع مسار URL */
segments: string[];
/** معرّف المستند الحالي (إذا كان مستندًا موجودًا قيد التحرير) */
docId: string | null;
/** عنوان المستند الحالي */
title: string;
}
تعالج الدالة extractParams مسارات URL:
النمط: /{country}/{lang}/products
المسار: /us/en/products
الخوارزمية:
1. توحيد كليهما (إزالة الشرطات المائلة اللاحقة)
2. تقسيمهما إلى مقاطع: ["us", "en", "products"] و["{country}", "{lang}", "products"]
3. مطابقة عدد المقاطع (يجب أن يكون متساويًا)
4. لكل زوج من المقاطع:
- إذا بدأ النمط بـ : أو {}، استخرجه كمعلمة
- وإلا، يجب أن تتطابق القيمتان تمامًا
5. الإرجاع: { country: "us", lang: "en" }
// ربط بسيط (يستخدم حقل "code" للبحث)
{ "lang": "language" }
// ربط تفصيلي (حقل slug مخصص)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
عند الجلب عبر documents.get(schema, identifier):
idcontent.codecontent.slugtitleيتيح ذلك مراجع مرنة للمستندات باستخدام أي معرّف فريد.