Un ghid practic pentru scrierea expresiilor CEL în CMS.
Un ghid practic pentru scrierea expresiilor CEL în CMS.
CEL (Common Expression Language) este un limbaj de scripting ușor integrat în CMS-ul nostru. Îți permite să scrii expresii dinamice care pot prelua date din documente, citi parametri de URL și calcula valori din mers.
Iată ce se întâmplă când rulează un script CEL:
Scriptul tău Motorul Rezultat
| | |
v v v
documents.get("article", "intro") --> Preia din baza de date --> { headline: "Bun venit", body: "..." }
.headline --> Extrage câmpul --> "Bun venit"
Consideră CEL ca pe un limbaj de interogare numai pentru citire. Nu poate modifica nimic în baza de date - doar citește datele și returnează un rezultat calculat. Acest lucru îl face sigur de folosit oriunde în CMS.
Fiecare expresie CEL are acces la trei lucruri:
| Obiect | Ce reprezintă | Exemplu |
|---|---|---|
documents | Preia orice document din CMS | documents.get("country", "us") |
meta | Informații despre cererea curentă (localizare, parametri URL) | meta.locale, meta.params.slug |
schema | Definițiile câmpurilor documentului curent | schema.fields |
docCând scrii expresii CEL în editorul de documente, poți accesa valorile câmpurilor documentului curent folosind obiectul doc. Acest lucru permite câmpuri calculate și referințe între câmpuri.
// Accesează câmpul de preț al documentului curent
doc.price
// Calculează totalul din câmpurile documentului curent
doc.price * doc.quantity
// Condiție bazată pe starea documentului curent
doc.status == "published" ? doc.title : "Ciornă: " + doc.title
Obiectul doc conține toate valorile câmpurilor din documentul editat. Acest lucru este util pentru:
doc.price * doc.quantity)Caracteristica cea mai puternică a CEL este preluarea documentelor din orice loc din CMS.
Sintaxă: documents.get(schemaName, identifier)
Să spunem că ai un document article stocat cu identificatorul "welcome-post":
// Stocat în CMS ca: article / welcome-post
{
"headline": "Bine ai venit pe platforma noastră",
"author": "Sarah Chen",
"body": "Suntem încântați să anunțăm...",
"tags": ["announcement", "news"]
}
Pentru a prelua întregul document:
documents.get("article", "welcome-post")
Returnează:
{
"headline": "Bine ai venit pe platforma noastră",
"author": "Sarah Chen",
"body": "Suntem încântați să anunțăm...",
"tags": ["announcement", "news"]
}
Pentru a prelua doar titlul:
documents.get("article", "welcome-post").headline
Returnează: "Bine ai venit pe platforma noastră"
Pentru a prelua autorul:
documents.get("article", "welcome-post").author
Returnează: "Sarah Chen"
Când pagina ta are rute dinamice (cum ar fi /articles/[slug]), poți folosi meta.params pentru a obține parametrul din URL și a prelua documentul potrivit.
Dacă cineva vizitează /articles/welcome-post:
documents.get("article", meta.params.slug).headline
Returnează: "Bine ai venit pe platforma noastră"
Așa construiești pagini dinamice - același script CEL funcționează pentru orice articol, folosind slug-ul din URL.
Sintaxă: documents.find(schemaName) sau documents.find(schemaName, filter)
// Obține toate țările
documents.find("country")
Returnează:
[
{ "code": "us", "name": "Statele Unite", "flag": "US" },
{ "code": "sa", "name": "Arabia Saudită", "flag": "SA" },
{ "code": "gb", "name": "Regatul Unit", "flag": "GB" }
]
// Obține țările cu un filtru
documents.find("country", { "where": { "code": "us" } })
Returnează:
[
{ "code": "us", "name": "Statele Unite", "flag": "US" }
]
CEL acceptă preluarea conținutului tradus al documentelor în două moduri: traducere automată bazată pe localizare și căutare explicită de traduceri.
Când meta.locale este setat (de ex., din parametrii de rută sau preferințele utilizatorului), documents.get() îmbină automat conținutul tradus:
// Dacă meta.locale este "fr", returnează traducerea în franceză îmbinată cu documentul de bază
documents.get("greeting", "welcome").headline
Cum funcționează:
meta.locale nu este "en" sau "en-US", caută traducerea în tabela translations{ ...baseContent, ...translatedContent }Asta înseamnă că câmpurile traduse suprascriu câmpurile de bază, în timp ce câmpurile netraduse revin la documentul de bază.
Pentru cazurile în care trebuie să preiei o traducere specifică, indiferent de localizarea curentă:
Sintaxă: documents.translated(schemaName, identifier, locale)
// Preia întotdeauna traducerea în spaniolă
documents.translated("greeting", "welcome", "es").headline
// Preia traducerea pe baza parametrului din URL
documents.translated("product", meta.params.id, meta.params.lang).description
// Compară traduceri
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
Documentele tale de întâmpinare cu traduceri:
// Document de bază: greeting / welcome
{ "headline": "Bun venit", "subheadline": "Bun venit pe platforma noastră" }
// Traducere (limba: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }
// Traducere (limba: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }
Scripturi CEL:
// Cu meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Returnează: "Bienvenue"
// Traducere explicită în spaniolă
documents.translated("greeting", "welcome", "es").headline
// Returnează: "Bienvenido"
// Model de rezervă pentru traduceri lipsă
documents.translated("greeting", "welcome", meta.params.lang) != null
? documents.translated("greeting", "welcome", meta.params.lang).headline
: documents.get("greeting", "welcome").headline
Ai un hero-block care ar trebui să afișeze un titlu preluat dintr-un document article.
Documentul tău article (identificator: "homepage-hero"):
{
"headline": "Construiește mai rapid, lansează mai inteligent",
"subheadline": "CMS-ul modern pentru dezvoltatori"
}
Script CEL în câmpul de titlu al blocului hero:
documents.get("article", "homepage-hero").headline
Rezultat: blocul hero afișează "Construiește mai rapid, lansează mai inteligent"
Construiești o pagină la /countries/[code] și vrei să afișezi numele complet al țării.
Documentele tale country:
// country / us
{ "code": "us", "name": "Statele Unite", "flag": "US", "languages": ["en", "es"] }
// country / sa
{ "code": "sa", "name": "Arabia Saudită", "flag": "SA", "languages": ["ar", "en"] }
Script CEL:
documents.get("country", meta.params.code).name
Când cineva vizitează /countries/us:
meta.params.code = "us""Statele Unite"Când cineva vizitează /countries/sa:
meta.params.code = "sa""Arabia Saudită"Afișează titluri diferite în funcție de localizarea utilizatorului.
meta.locale == "ar-SA" ? "Bun venit, tuturor" : "Bun venit"
Dacă localizarea este "ar-SA": returnează "Bun venit, tuturor"
Dacă localizarea este orice altceva: returnează "Bun venit"
Documentul tău article are un câmp countryCode, iar tu vrei să obții numele complet al țării.
Document article:
{ "headline": "Știri din SUA", "countryCode": "us" }
Script CEL:
documents.get("country", documents.get("article", "us-news").countryCode).name
Ce se întâmplă:
documents.get("article", "us-news") returnează { "headline": "Știri din SUA", "countryCode": "us" }.countryCode extrage "us"documents.get("country", "us") returnează { "code": "us", "name": "Statele Unite", ... }.name extrage "Statele Unite"Rezultat: "Statele Unite"
Dacă un document ar putea să nu existe, poți oferi o valoare de rezervă:
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Articolul nu a fost găsit"
Sau verifică dacă un anumit câmp există:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Autor necunoscut"
Articolul tău are etichete și vrei să verifici dacă există o etichetă specifică:
"featured" in documents.get("article", "welcome-post").tags
Returnează: true dacă articolul are eticheta "featured"
Obține prima etichetă:
documents.get("article", "welcome-post").tags[0]
Returnează: "announcement" (prima etichetă)
Numără etichetele:
size(documents.get("article", "welcome-post").tags)
Returnează: 2 (numărul de etichete)
Rutele parametrice sunt cheia pentru construirea paginilor dinamice și localizate. Când definești un model de rută precum /{lang}/landingPage, CMS extrage parametrii din URL și îi pune la dispoziție via meta.params.
Definirea modelului de rută:
Rutele folosesc sintaxa :paramName sau {paramName} pentru a defini segmente dinamice:
| Model | URL exemplu | Parametri extrași |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
Asocieri de parametri: Fiecare parametru de rută poate fi asociat cu o schemă de document pentru validare:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
Această asociere îi spune CMS-ului:
lang din URLlanguage (caută un document unde content.code se potrivește)Configurația rutei:
/{lang}/landingPage/{lang}/landingPage{ "lang": "language" }Documentele tale greeting:
// greeting / ko
{ "code": "ko", "headline": "Bun venit", "subheadline": "Bun venit pe platforma noastră", "ctaText": "Începe", "ctaUrl": "/ko/get-started" }
// greeting / en
{ "code": "en", "headline": "Bun venit", "subheadline": "Bun venit pe platforma noastră", "ctaText": "Începe", "ctaUrl": "/en/get-started" }
// greeting / ja
{ "code": "ja", "headline": "Bun venit", "subheadline": "Bun venit pe platforma noastră", "ctaText": "Start", "ctaUrl": "/ja/get-started" }
Script CEL pentru a prelua conținutul localizat:
documents.get("greeting", meta.params.lang).headline
Cum se rezolvă:
| URL | meta.params.lang | Rezultat |
|---|---|---|
/ko/landingPage | "ko" | "Bun venit" |
/en/landingPage | "en" | "Bun venit" |
/ja/landingPage | "ja" | "Bun venit" |
Pentru rute precum /{country}/{lang}/products:
Configurația rutei:
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
Scripturi CEL:
// Obține numele țării
documents.get("country", meta.params.country).name
// Obține lista de produse localizată în funcție de țară
documents.find("product", { "where": { "country": meta.params.country } })
// Combinat: arată salutul specific țării în limba utilizatorului
documents.get("greeting", meta.params.lang).headline + " din " + documents.get("country", meta.params.country).name
Cascadă de validare:
CMS validează parametrii ierarhic. Pentru rutele /{country}/{lang}:
country față de schema countrylang față de schema languagelang se află în array-ul country.languages[] (validare ierarhică)meta.segments oferă calea URL brută ca un array, util atunci când ai nevoie de acces pozițional fără parametri numiți.
Cum funcționează:
| Cale URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| Caz de utilizare | Abordare recomandată |
|---|---|
| Parametri numiți din modelul rutei | meta.params.lang |
| Acces bazat pe poziție | meta.segments[0] |
| Obținerea adâncimii căii | size(meta.segments) |
| Verificarea dacă calea conține un segment | "admin" in meta.segments |
// Obține primul segment (adesea codul limbii)
meta.segments[0]
// Verifică adâncimea căii
size(meta.segments) > 2 ? "adâncă" : "superficială"
// Verifică dacă suntem în secțiunea de administrare
"admin" in meta.segments ? "mod admin" : "mod public"
// Rezervă: folosește segmentul dacă parametrul nu este asociat
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
Obiectul meta conține tot contextul despre cererea curentă:
| Proprietate | Tip | Descriere | |
|---|---|---|---|
meta.locale | string | Codul localizării curente (de ex., "en-US", "ko-KR", "ar-SA") | |
meta.params | Record<string, string> | Parametrii de rută extrași din modelul URL | |
meta.segments | string[] | Calea URL împărțită în segmente | |
meta.docId | `string \ | null` | UUID-ul documentului curent (null pentru documente noi) |
meta.title | string | Titlul documentului curent |
Codul localizării urmează formatul BCP 47 (limbă-regiune):
// Verifică localizarea pentru limbile RTL
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// Obține doar partea de limbă
meta.locale.split("-")[0] // Nu este acceptat - folosește meta.params.lang în schimb
Parametrii rutei sunt întotdeauna stringuri. CMS îi validează față de schemele asociate înainte de evaluare:
// Accesează un parametru numit
meta.params.lang // "ko"
meta.params.country // "us"
meta.params.slug // "welcome-post"
// Verifică dacă parametrul există
has(meta.params.category) // true/false
// Folosește-l pentru a prelua un document
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
Segmente brute ale URL-ului, ca array:
// Acces pe index (0-based)
meta.segments[0] // Primul segment
meta.segments[1] // Al doilea segment
// Verifică lungimea
size(meta.segments) // Numărul de segmente
// Verifică apartenența
"products" in meta.segments // Calea include "products"?
UUID-ul documentului curent, util pentru scripturi auto-referențiale:
// Disponibil doar când editezi documente existente
meta.docId != null ? "editare" : "creare nouă"
// Folosește în logică condițională
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
Titlul documentului curent:
// Folosește pentru afișare
"În editare: " + meta.title
// Condițional în funcție de titlu
meta.title.contains("Draft") ? "în lucru" : "publicat"
Pentru un sintaxă mai curată când schema este cunoscută, dar identificatorul este dinamic:
// Abordare tradițională
documents.get("airports", meta.params.code).name
// Folosind ref() - schema separată de identificatorul dinamic
documents.ref("airports").get(meta.params.code).name
Ambele sunt echivalente, dar ref() face partea dinamică mai clară.
documents.get("schema", "identifier") // Preia un document
documents.get("schema", "id").fieldName // Preia un câmp specific
documents.find("schema") // Preia toate documentele
documents.find("schema", { "where": {...}}) // Interogare filtrată
documents.ref("schema").get(identifier) // Căutare în lanț
documents.translated("schema", "id", "fr") // Preia cu localizare explicită
meta.locale // "en-US", "ar-SA", etc.
meta.params.xyz // Parametrul de URL numit "xyz"
meta.segments // Calea URL ca array: ["articles", "intro"]
meta.segments[0] // Primul segment de cale
meta.docId // ID-ul documentului curent (sau null)
meta.title // Titlul documentului curent
doc.fieldName // Valoarea câmpului documentului curent (în contextul editorului)
// Comparație
== != < <= > >=
// Logică
&& || !
// Ternar (if-else)
condiție ? valoareDacăAdevărat : valoareDacăFals
// Apartenență
"valoare" in listOrMap
size(list) // Numără elementele
size(string) // Lungimea stringului
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // Verifică dacă proprietatea există
hasProperty(obj, "key") // Verifică dacă obiectul are cheia (sintaxă alternativă)
Dacă ceva nu merge, vei vedea unul dintre acestea:
| Eroare | Ce înseamnă |
|---|---|
SYNTAX_ERROR | O greșeală de scriere în script (ghilimele lipsă, operator greșit) |
TYPE_ERROR | Combini tipuri care nu funcționează împreună |
RUNTIME_ERROR | Scriptul a rulat, dar a întâmpinat o problemă (variabilă nedefinită) |
FETCH_LIMIT_EXCEEDED | Preiei prea multe documente (maxim 50) |
TIMEOUT | Scriptul a durat prea mult (maxim 5 secunde) |
AST_DEPTH_EXCEEDED | Expresia este prea adânc imbricată (adâncime maximă: 50) |
SCRIPT_TOO_LONG | Scriptul depășește limita de 5000 de caractere |
Motorul CEL este conceput pentru extensibilitate. Capacitățile planificate pentru viitor includ:
// Viitor: apelarea serviciilor externe via MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// Viitor: generare de conținut cu AI
ai.summarize(documents.get("article", meta.params.id).body, 100)
ai.translate(meta.params.text, meta.params.targetLang)
ai.classify(meta.params.input, ["pozitiv", "negativ", "neutru"])
Aceste capabilități vor fi adăugate prin sistemul de funcții înregistrate, menținând compatibilitatea inversă cu scripturile existente.
documents. sau meta. și editorul îți va arăta opțiunile disponibiledocuments.get("schema", "id"), apoi adaugă .fieldName!= null ? ... : ...documents.get() sau documents.find() se contorizează în limita de 50 de preluărihas(meta.params.category) înainte de a accesaAceastă secțiune acoperă modele avansate pentru legarea documentelor și construirea structurilor de conținut relaționale.
Forma cea mai simplă: un document face referire la altul prin identificator.
// Articolul stochează ID-ul autorului, preia numele autorului
documents.get("author", documents.get("article", "intro").authorId).name
Pentru o sintaxă mai curată când identificatorul este dinamic:
// Abordare tradițională
documents.get("country", documents.get("airport", meta.params.code).countryCode).name
// Folosind ref() - mai clar când schema este cunoscută, dar identificatorul este dinamic
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name
Construiește relații profunde prin înlănțuirea mai multor preluări:
// Aeroport → Țară → Regiune → Continent
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
Combină referințele de documente cu traduceri:
// Obține numele localizat al țării pentru un aeroport
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
Documentul stochează un ID care face referire la alt document.
// article / tech-news
{ "title": "Actualizare tech", "authorId": "author-123", "categoryId": "cat-tech" }
// Rezolvă numele autorului
documents.get("author", documents.get("article", meta.params.slug).authorId).name
// Rezolvă categoria cu rezervă
documents.get("article", meta.params.slug).categoryId != null
? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
: "Fără categorie"
Documentele se referă între ele prin coduri semantice mai degrabă decât UUID-uri.
// airport / JFK
{ "code": "JFK", "name": "John F. Kennedy International", "countryCode": "us" }
// country / us
{ "code": "us", "name": "Statele Unite", "currencyCode": "usd" }
// currency / usd
{ "code": "usd", "symbol": "$", "name": "Dolarul SUA" }
// Lanț Aeroport → Țară → Monedă
documents.get("currency",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).currencyCode
).symbol
// Pentru JFK: returnează "$"
Folosește doc pentru câmpuri calculate care fac referire la alte documente pe baza valorilor documentului curent.
// Într-un document de produs, preia detalii despre categoria asociată
documents.get("category", doc.categoryId).description
// Cost de livrare calculat pe baza țării de origine a produsului
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight
Când documentele se referă reciproc, ai grijă la limitele de preluare.
// Obține autorul articolului, apoi alte articole ale autorului (atenție la numărul de preluări)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })
Când un câmp poate face referire la scheme diferite:
// content-block / hero-1
{ "type": "hero", "sourceType": "article", "sourceId": "welcome-post" }
// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Căutare dinamică a schemei în funcție de 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
Fiecare apel documents.get(), documents.find() și documents.ref().get() este urmărit pentru invalidarea cache-ului. Când un document referit se schimbă, CMS știe ce expresii CEL trebuie reevaluate.
Dependențele urmărite includ:
schema:identifier - dependență de document specificschema:identifier - la fel ca get, prin sintaxa în lanțschema:* - dependență la nivel de schemă (orice document din schemă)// Greșit: preia același document de două ori
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio
// Mai bine: folosește o condiție pentru a verifica o singură dată
documents.get("article", "intro").authorId != null
? documents.get("author", documents.get("article", "intro").authorId).name
: "Autor necunoscut"
Această prezentare creează o pagină de destinație multilingvă accesibilă la /{lang}/landingPage.
În admin-ul CMS, creează o schemă personalizată numită 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" }
]
}
Creează documente pentru fiecare limbă:
Document: greeting/ko
{
"code": "ko",
"headline": "Bun venit",
"subheadline": "Bun venit pe platforma noastră",
"ctaText": "Începe",
"ctaUrl": "/ko/get-started"
}
Document: greeting/en
{
"code": "en",
"headline": "Bun venit",
"subheadline": "Bun venit pe platforma noastră",
"ctaText": "Începe",
"ctaUrl": "/en/get-started"
}
Document: greeting/ja
{
"code": "ja",
"headline": "Bun venit",
"subheadline": "Bun venit pe platforma noastră",
"ctaText": "Start",
"ctaUrl": "/ja/get-started"
}
Creează o pagină cu următoarea configurație:
/{lang}/landingPagelang → componenta language {
"lang": "language"
}
Adaugă un bloc hero în rută cu aceste scripturi CEL pentru fiecare câmp:
Câmpul Headline:
documents.get("greeting", meta.params.lang).headline
Câmpul Subheadline:
documents.get("greeting", meta.params.lang).subheadline
Câmpul CTA Text:
documents.get("greeting", meta.params.lang).ctaText
Câmpul CTA URL:
documents.get("greeting", meta.params.lang).ctaUrl
Adaugă o rută catch-all. ParametricRoutePage rezolvă pagina, extrage meta.params din URL, evaluează legăturile CEL pe server și redă fiecare bloc prin registrul tău — nu construiești singur contextul meta și nici nu apelezi clientul de nivel inferior.
// 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 })}
/>
);
}
Vizitează aceste URL-uri pentru a vedea conținutul localizat:
| URL | Titlul așteptat |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
Când un utilizator vizitează /ko/landingPage:
/{lang}/landingPagemeta.params.lang = "ko"languagedocuments.get("greeting", meta.params.lang) se rezolvă la conținutul coreeaninterface CelMeta {
/** Codul localizării curente (de ex., 'en-US') */
locale: string;
/** Parametrii de rută extrași din URL */
params: Record<string, string>;
/** Segmentele căii URL */
segments: string[];
/** ID-ul documentului curent (dacă editezi un document existent) */
docId: string | null;
/** Titlul documentului curent */
title: string;
}
Funcția extractParams procesează căile URL:
Model: /{country}/{lang}/products
Cale: /us/en/products
Algoritm:
1. Normalizează ambele (elimină slash-urile finale)
2. Împarte în segmente: ["us", "en", "products"] și ["{country}", "{lang}", "products"]
3. Potrivește numărul segmentelor (trebuie să fie egal)
4. Pentru fiecare pereche de segmente:
- Dacă modelul începe cu : sau {}, extrage ca parametru
- Altfel, trebuie să se potrivească exact
5. Returnează: { country: "us", lang: "en" }
// Asociere simplă (folosește câmpul "code" pentru căutare)
{ "lang": "language" }
// Asociere detaliată (câmp slug personalizat)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
Când preiei prin documents.get(schema, identifier):
idcontent.codecontent.slugtitleAcest lucru permite referințe flexibile la documente prin orice identificator unic.