Довідник синтаксису Spintax

Повний довідник із шаблонної розмітки spintax.

Переліки { }

Випадковим чином вибирає один варіант зі списку.

{option1|option2|option3}

Приклади

{blue|grey|clear}
{|free|paid} plan                 ← empty option = sometimes nothing
{Acme {Pro|Lite}}                ← nested enumerations
{order {|#42-A} confirmed}       ← nesting with empty option

Правила

  • Обмежувачі: { та }
  • Роздільник варіантів: |
  • Підтримує вкладеність довільної глибини
  • Порожні варіанти допустимі (дають порожній рядок)
  • Розв’язання йде від найглибшого виразу назовні

Перестановки [ ]

Вибирає N елементів, перемішує їх і об’єднує з роздільниками.

Прості перестановки

Усі елементи включені, розділені пробілами:

[1|2|3|4]

Приклади результату: 1 4 3 2, 2 3 4 1, 3 2 4 1

З роздільником

Єдиний роздільник указується в < > на початку:

[<, > 1|2|3|4]

Приклади результату: 2, 1, 4, 3 · 4, 3, 2, 1

Важливо: Між [ та <роздільник> немає пробілу.

Роздільники для кожного елемента

Кожен варіант може мати власний роздільник, заданий через <sep> перед попереднім |. Роздільник переміщується разом зі своїм елементом під час перемішування.

[<, > 1|2|3 < and >|4]

Приклади результату: 1, 3, 2 and 4 · 3, 1, 2 and 4

Авто-пробіли: Словесні роздільники на кшталт <і> чи <або> автоматично доповнюються пробілами: <і> дає  і . Розділові знаки (<,>) не доповнюються.

Перестановки з комбінаціями

Налаштовувана мін./макс. кількість елементів і роздільники:

[<minsize=1;maxsize=3;sep=", ";lastsep=" and "> apple|plum|orange|apricot]

Приклади результату: apple, plum and orange · apple and apricot · orange

Параметри конфігурації

ПараметрТиповоОпис
minsizeкількість усіхМінімальна кількість вибраних елементів
maxsizeкількість усіхМаксимальна кількість вибраних елементів
sep" " (пробіл)Роздільник між елементами (крім останнього)
lastsepяк sepРоздільник перед останнім елементом

Правила перестановок

  • Обмежувачі: [ та ]
  • Блок конфігурації <...> має йти одразу після [
  • Параметри конфігурації розділяються крапкою з комою
  • Рядкові значення в конфігурації беруться в лапки: sep=", "
  • Переліки та перестановки можуть бути вкладені у варіанти
  • HTML-елементи можуть бути варіантами
  • sep з'єднує все до останньої пари, lastsep — саму пару: за двох вибраних елементів з'являється лише lastsep, за одного — жоден

Змінні %var%

Визначає змінну для багаторазового використання, яка підставляється всюди, де трапляється. Оголосити її можна двома директивами, і вибір не косметичний: #set — макрос, його значення підставляється заново при кожному посиланні, і spintax усередині перекидається; #def розкриває значення один раз за рендер і віддає цей єдиний результат усім посиланням. (Один рендер — це один вивід; той самий seed відтворює його.) Поки значення — простий текст, різниці немає; вона з’являється тієї миті, коли всередині з’являється вибір.

#set %VARIABLE_NAME% = value or spintax structure

#def %VARIABLE_NAME% = value or spintax structure

Приклади

#set %name% = John
#set %greeting% = {Hello|Hi|Hey}
#set %items% = [<minsize=2;maxsize=3;sep=", ";lastsep=" and "> apples|oranges|bananas]
Some text with %name% and %greeting%, also %items%.
And once more, %greeting% — a second reference.

/# %greeting% above may differ between the two references — #set re-rolls.
   #def picks once and keeps it: #/
#def %tone% = {friendly|warm|upbeat}
A %tone% intro, and a %tone% outro — always the same word.

Правила змінних

  • #set і #def мають починатися з початку рядка
  • Імена змінних беруться в %: %name%
  • Імена складаються з букв, цифр і підкреслень, а посилання нечутливі до регістру: %Tone% і %tone% — одна змінна
  • Значення можуть містити будь-який синтаксис spintax (переліки, перестановки, інші змінні)
  • #set — макрос: розкривається при посиланні, а не при визначенні, і його значення — разом зі spintax усередині — підставляється заново й перекидається при кожному посиланні
  • #def розкриває значення один раз за рендер і тримає результат скрізь. Саме цим тримаються повтори: іменник і побудовані від нього відмінкові форми, число для блоку {plural}, будь-яка фраза, що має повторюватися слово в слово
  • #def робить змінну узгодженою із самою собою, але не пов’язує дві змінні. #def %Noun% і #def %NounGen% — два незалежні розкати, вони можуть випасти на різні слова: форми, що мають узгоджуватися, повинні йти з одного розкату — одна основа в #def, на яку посилається кожна відмінкова форма, або синоніми, що відмінюються однаково, із закінченням поза визначенням
  • Ім’я визначається один раз. Друге визначення того самого імені позначається як definition.duplicate-name, але рендер усе одно триває: між двома однаковими директивами перемагає остання, а якщо ім’я ділять #set і #def, перемагає #def — байдуже, яка з них була першою
  • Посилання без визначення друкує саме себе: %missing% залишається у виводі, а не зникає
  • Жодна з директив не переходить через #include: включений шаблон не бачить локальних змінних батька, а його власні не витікають нагору. Розкатана форма потрапляє всередину лише як змінна часу виконання
  • Рядки #set і #def вилучаються з результату
  • Докладно: див. посібник зі змінних — області видимості та підступ із перекидом, і граматично безпечну синонімізацію — відмінкові сім’ї

Області видимості змінних

Хост може подавати змінні з кількох місць. Якщо те саме ім’я є в кількох, перемагає найсильніше:

  1. Змінні часу виконання (найсильніші) — те, що хост передає у виклик рендера: context у @spintax/core, атрибути шорткоду в плагіні WordPress: [spintax slug="greeting" name="Alice"]
  2. Локальні змінні — оголошені через #set або #def всередині шаблону
  3. Глобальні змінні (найслабші) — значення за замовчуванням на рівні хоста, наприклад сторінка налаштувань плагіна

Умови {?VAR?then|else}

Умовний синтаксис — власна конструкція мови: у прототипі GTW нічого подібного не було. Якщо {a|b} — це рівномірний випадковий вибір, який не дивиться на змінні, то {?VAR?then|else} вибирає за тим, чи має %VAR% значення.

Використовуйте для рішень за значенням: показувати рядок про безкоштовний тариф лише коли тариф є, рендерити блок про-можливостей лише коли користувач на платному тарифі, ховати CTA, який зараз не застосовний.

Попередній прохід відпрацьовує до розкриття %var% і до випадкового вибору гілок, тому falsy-гілка відкидається повністю — ніщо всередині неї не обчислюється.

Форми

{?VAR?then}                ← truthy ⇒ then; falsy ⇒ empty
{?VAR?then|else}           ← truthy ⇒ then; falsy ⇒ else
{?!VAR?then|else}          ← inverted
{?HasFreeTier? — free tier available since %founded%|, trusted since %founded%}

Truthy та falsy

Правило простіше, ніж у JavaScript — truthy = хоча б один непробільний символ:

Значення %VAR%Truthy?
не оголошенаfalsy
порожній рядокfalsy
лише пробілиfalsy
"0", "false"truthy (непорожні рядки)
будь-який інший текст чи HTMLtruthy

Правила умов

  • Імена змінних відповідають тому ж regex, що й %var% (без урахування регістру)
  • Префікс ! інвертує перевірку: {?!VAR?немає даних}
  • Перший | на нульовій глибині розділяє then і else; наступні | лишаються літералами в else
  • Вкладені умови розкриваються outer-first — falsy-гілки короткозамикаються
  • Складена логіка (&&, ||, порівняння) не підтримується — обчисліть guard-змінну в асемблері
  • Криві форми ({??yes}, {?VAR}) не падають — пісочниця позначає їх попередженнями
  • Докладно: див. гайд з умовного spintax із прикладами та анти-патернами

Плюрали {plural %n%: мова|мови|мов}

Підбирає граматично правильну форму слова під число. Лічильник іде до двокрапки, форми — після, через |.

Форму вибирає локаль рендера, а не шаблон, тому кількість форм залежить від локалі: українській потрібні три, англійській — дві.

{plural %n%: form1|form2}          ← 2-form locale (en, de, es…)

{plural %n%: form1|form2|form3}    ← 3-form locale (ru, uk, sr…)
#def %LangCount% = 5
supports %LangCount% {plural %LangCount%: language|languages}
← supports 5 languages

Форми за локалями

Локаль зіставляється за мовним субтегом, тож uk-UA та uk поводяться однаково:

ЛокальФормВибір за числом
ru, uk, be, sr, hr, bs31 · 2–4 · 5 і більше
усі інші, включно з en2рівно 1 · усе інше

Якщо форм не стільки, скільки потрібно локалі, рушій віддає plural.arity і лишає блок видимим у повноширинних дужках — мовчки неправильна форма в продакшен не поїде.

Правила плюралів

  • Відкривальна частина літеральна, разом із пробілом: {plural . {plural: x} та {pluralN: x} плюралами не є
  • Двокрапка обов’язкова — вона відділяє лічильник від форм
  • Лічильник — це посилання %Var% або цілочисловий літерал; змінні в лічильнику підставляються до вибору форми
  • Від’ємні числа беруться за модулем; 0 отримує форму «все інше» (українською — «мов»)
  • Змінна-лічильник має бути #def, а не #set#set це макрос, тому значення на кшталт {1|4|9} на момент вибору форми все ще нерозв’язаний spintax, і блок відрендериться порожнім. У пісочниці це plural.count-macro
  • Нечисловий чи невизначений лічильник стирає блок, а не вгадує форму
  • Докладно: див. гайд із плюралів — правила трьох форм і розбори прикладів

Включення #include

Вбудовує інший шаблон у позицію директиви. #include — єдина конструкція, яку рушій не може забезпечити сам: сховища шаблонів він не має, тож резолвер, що перетворює посилання на текст шаблону, дає хост. Там, де резолвера не встановлено — у пісочниці та на MCP-сервері цього сайту, і те й інше навмисно, — директива інертна й лишається у виводі звичайним текстом.

#include "hero-text"

/# wrong: text before the directive on the same line leaves it literal #/
Intro: #include "hero-text"

Правила включень

  • Директива має займати весь рядок. Відступ ліворуч припустимий, текст після посилання — ні: Текст #include "hero" лишається літералом
  • Посилання береться в подвійні лапки; одинарні лапки або їх відсутність — це вже не директива
  • Розв’язання посилання — справа хоста: плагін WordPress шукає за slug або числовим ID, JavaScript-хост передає includeResolver
  • Без резолвера рядок лишається у виводі літералом; якщо в резолвера такого шаблону немає, рядок натомість видаляється — невідома ціль мовчки коштує вам цілого блоку
  • Включені шаблони можуть містити власні змінні та spintax, а також власні #include
  • Включення розв’язуються після того, як у батька розіграно переліки й перестановки: include у гілці, що виграла, вбудовується, а у відкинутій не стається взагалі
  • Ланцюжки працюють (шаблон включає шаблон, який включає наступний); шаблон, що включає сам себе — прямо чи по циклу, — обривається на першому повторі, без помилки й без діагностики
  • Дочірні шаблони успадковують глобальні змінні та змінні часу виконання, але не локальні #set / #def батька, і своїх нагору не віддають
  • Включення не може бути значенням визначення: #def %x% = #include "y" відхиляється як def.include-in-value
  • До рендера validate() повідомляє про невідому ціль, лише якщо хост передав список відомих посилань; extract() повертає посилання, потрібні шаблону, — так хост їх передзавантажує
  • Докладно: див. посібник із композиції шаблонів — патерн асемблера, яким користується більшість конвеєрів

Коментарі /#...#/

Текст між маркерами коментарів видаляється з результату до будь-якої іншої обробки.

/#
  This is a comment section.
  It can span multiple lines.
  It won't appear in output.
#/

Правила коментарів

  • Відкривальний обмежувач: /#
  • Закривальний обмежувач: #/
  • Може займати кілька рядків
  • Вкладеність не підтримується
  • Видаляються до початку будь-якої іншої обробки

Вкладеність

Усі елементи синтаксису можуть бути вкладені один в одного на довільну глибину:

{option1|[<, > sub1|sub2|sub3]|option3}

[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> {red|blue} apples|{big|small} oranges|bananas]

#set %var% = {a|[b|c]}

Постобробка

Рушій автоматично коригує текст після генерації:

  1. Захист URL, email-адрес, доменів, десяткових чисел та абревіатур від зміни регістру
  2. Видалення повторюваних пробілів і табуляцій
  3. Видалення пробілів перед розділовими знаками (, . ! ?)
  4. Додавання пробілу після розділових знаків там, де його бракує
  5. Велика літера на початку результату (з пропуском HTML-тегів)
  6. Велика літера після знаків кінця речення
  7. Велика літера після блокових HTML-тегів
  8. Велика літера після переносів рядків
  9. Відновлення захищених заповнювачів

Зведення синтаксису

МожливістьСинтаксисПоведінка
Перелік{a|b|c}Вибрати один випадковий варіант
Перестановка[a|b|c]Вибрати N, перемішати, об’єднати
Роздільник[<sep> a|b|c]Перестановка з єдиним роздільником
Роздільник елемента[<, > a|b <x>|c]Перестановка з власними роздільниками
Комбінації[<config> a|b|c]Перестановка з мін./макс. кількістю
Змінна#set %var% = {a|b}Підставляється заново при кожному посиланні — spintax усередині перекидається
Змінна (раз за рендер)#def %var% = {a|b}Один розкат за рендер, результат тримається скрізь — так узгоджуються форми й закінчення
Умова{?VAR?then|else}then, якщо truthy; інакше else
Плюрал{plural %n%: мова|мови|мов}Узгоджує форму слова з числом за локаллю
Включення#include "slug"Вбудовування іншого шаблону — посилання розв’язує хост
Коментар/#...#/Видаляється з результату

Мова виросла зі свого прототипу — Generating The Web (GTW); шаблони, написані для GTW, і далі працюють без змін.