Переменные и переиспользование

Переменные — это то, что превращает один шаблон в сеть сайтов. Правильно спроектированные переменные — и 100 сайтов рендерятся из одного источника. Неправильно — и вы копипастите текст в каждый пресет руками.

Три источника, одна общая область

В момент рендера большинство движков сливают переменные из трёх мест в одну общую таблицу. Когда шаблон читает %SomeName%, резолвер идёт по этой таблице и подставляет значение.

  1. Локальные помощники шаблона — объявленные через #set внутри тела шаблона.
  2. Переменные сайта — заданные для конкретного тенанта, по одной записи на сайт, общие для всех шаблонов этого сайта.
  3. Runtime-переменные — передаются в резолвер в момент вызова (контекст статьи, системный контекст, пользовательский контекст).

Проектирование сводится к одному вопросу: на каком слое живёт каждый факт.

Локальные помощники через #set

Используйте #set для коротких помощников внутри одного шаблона:

#set %Lead% = {Добро пожаловать|Приветствуем|Здравствуйте},
%Lead% добро пожаловать в %brand_name%!

Хорошие применения:

  • помощники одного шаблона, которые иначе захламляли бы тело повторами;
  • длинные фразы, используемые несколько раз внутри одного шаблона;
  • читаемость, когда вложенность становится слишком глубокой.

Плохие применения:

  • специфичные для сайта факты — им место в переменных сайта;
  • всё, что уже предоставляет runtime — локальный #set проиграет по приоритету.

Синтаксические подводные камни

  • Имена переменных регистронезависимы.
  • Только ASCII: буквы, цифры, подчёркивания. Никаких пробелов и дефисов.
  • #set работает только в начале строки.
  • Комментарии — /# ... #/, вырезаются до обработки.
  • Неизвестные переменные остаются буквальными. %MissingVar% отрендерится как %MissingVar%, а не пустой строкой и не ошибкой. Оставшиеся %...% в результате — QA-фейл.

Переменные сайта — множитель для множества сайтов

Именно переменные сайта позволяют общему шаблону обслуживать множество сайтов, не читаясь одинаково на всех доменах.

Абстрактный пресет сайта выглядит так:

#set %BrandTone% = {практичный|по делу|без воды}
#set %Industry% = SaaS-аналитика
#set %TopFeatures% = [<minsize=3;maxsize=4;sep=", ";lastsep=" и ">дашборды|алерты|аудит-логи|SSO|ролевой доступ]
#set %Audience% = {команды|продакт-лиды|операционщики}

Любой общий шаблон теперь может читать %BrandTone%, %TopFeatures% и т. д., и результат меняется на каждом сайте без единой правки в шаблоне.

Когда заводить переменную сайта

СигналДействие
Фраза встречается в двух и более шаблонахВынести в переменную сайта.
Факт меняется от сайта к сайтуОбязательно переменная сайта.
Список должен перемешиваться или отличаться по сайтамПеременная сайта с permutation внутри.
Используется один раз в одном шаблонеОбычно оставить inline.

Runtime-переменные

Runtime-переменные приходят из контекста вызова: из самой статьи, текущего пользователя, системных часов. Они перебивают и переменные сайта, и локальные помощники шаблона при совпадении имени.

Типичные runtime-переменные (конкретные имена зависят от движка):

  • %year% — текущий год
  • %lang% — код текущего языка
  • %site_domain% — хост текущего сайта
  • %brand_name%, %product_name% — бренд/продукт, о котором статья
  • %article_topic%, %category% — метаданные уровня статьи

Из шаблона их не назначают. Достаточно читать.

Приоритет переменных

Если одно имя встречается на нескольких слоях — побеждает самый сильный. Стандартный порядок от сильнейшего к слабейшему:

  1. Runtime-переменные
  2. Переменные сайта
  3. Системные переменные
  4. Локальный #set

Практическое следствие: #set %brand_name% = Demo внутри шаблона не делает ничего, если runtime передаёт %brand_name%. Runtime побеждает. Называйте локальных помощников так, чтобы не пересекаться с runtime.

Соглашения об именах

Внутренняя консистентность одного пресета важнее любого конкретного стиля. Разумный дефолт:

  • Runtime-переменные: обычно lowercase_snake_case. Они живут вне вашего контроля.
  • Переменные сайта: PascalCase для обычных строк, PascalCaseС_суффиксом для грамматических вариантов (см. гайд №4 про русские падежи).
  • Переменные-списки: во множественном числе (%TopFeatures%, %SupportedLanguages%).
  • Локальные помощники: короткие и описательные — %Lead%, %Closing%.

Составные переменные

Переменные сайта могут ссылаться друг на друга. Резолвер пресета подставляет межпеременные ссылки первыми, но оставляет вложенный spintax сырым, чтобы последующие reroll'ы продолжали работать:

#set %FoundedLine% = запущен в %FoundedYear%, база в %HQ%
#set %Pitch% = {быстрый|легковесный|self-hosted} %ProductCategory%

Составные переменные позволяют один раз скомпоновать повторяющиеся факты и переиспользовать их во всех шаблонах.

Ловушка повторного решения

Самый частый источник путаницы у новых авторов. Если переменная содержит сырой spintax, каждое её упоминание решается независимо.

#set %Tone% = {надёжный|проверенный}
%Tone% и %Tone%

Возможный результат:

Надёжный и проверенный

Не рассчитывайте, что %Tone% разрешится один раз и дальше просто повторится. Если нужно точное повторение — перепишите предложение так, чтобы переменная встретилась только один раз. Если нужны два разных прилагательных — заведите две переменные.

Необязательные фрагменты

Пустая ветка в перечислении — это необязательный фрагмент:

{|официальный }сайт
{быстрые|безопасные|} выплаты

Пробел ставьте внутрь необязательной ветки, когда фрагмент может исчезнуть — иначе получите двойные пробелы или слипшиеся слова. Для необязательного списка (permutation, которая может быть пустой) оборачивайте весь список:

{|[<minsize=2;maxsize=3;sep=", ";lastsep=" и ">Slack|Jira|Linear]}

Движок не может выбрать ноль элементов из permutation. Оборачивание — единственный способ сделать «списка совсем нет» возможным исходом.

Коллизии сепараторов

Частая ошибка рендера: список-переменная уже содержит «и», а окружающий текст добавляет ещё одно «и».

%Integrations% и другие инструменты

Если %Integrations% разрешается в Slack, Jira и Linear, итог выглядит так:

Slack, Jira и Linear и другие инструменты

Способы починить:

  • поставить запятую: %Integrations%, и другие инструменты;
  • перестроить: {Помимо|Вместе с} %Integrations%, другие инструменты...;
  • убрать финальный союз и использовать двоеточие или тире.

Та же проблема — у permutation с lastsep=" и ", за которой идёт фиксированный текст, начинающийся с «и». Перед выкаткой посмотрите несколько сэмплов.

Переменные vs inline-spintax

Использовать переменнуюИспользовать inline
Фраза повторяется между шаблонамиОдноразовый синоним внутри одного предложения
Факт меняется от сайта к сайтуОбычный синоним глагола или существительного
Список должен отличаться по тенантамМаленький фиксированный одноразовый список
Грамматической форме нужно несколько написаний (см. русские падежи в гайде №4)Слово используется только в одной грамматической позиции

Общее правило: сначала выносите в переменные повторяющиеся грамматически чувствительные фразы, и только потом добавляйте маленькие inline-слоты синонимов. Переменная даёт одно место, где чинить ошибки. Inline разбрасывает их по тексту.

Типичные ошибки с переменными

Не надоПочемуВместо этого
Хардкодить специфичный для сайта факт в общий шаблонВсе сайты выдают одинаковый текст, весь смысл multi-site теряется.Вынести факт в переменную сайта.
Через #set пытаться перекрыть runtime-переменнуюRuntime всегда побеждает, ваш override молча ничего не делает.Переименуйте помощника, чтобы не пересекаться с runtime.
Предполагать, что %X% ... %X% повторяет одно словоКаждое упоминание решается заново. Можете получить два разных слова.Перепишите предложение или используйте две разные переменные.
Рассчитывать на ошибку при отсутствующей переменнойОна отрендерится буквально как %MissingVar%.Добавьте preview-проход, который ловит оставшиеся %...%.
Склеивать список-переменную с ещё одним «и»Выходит «A, B и C и другие вещи».Используйте запятую или перестраивайте фразу.
Забывать про пробел в необязательном фрагментеПолучаете двойные пробелы или слипшиеся слова.Пробел — внутри необязательной ветки.

Чеклист проектирования переменных

  • Каждый факт, специфичный для тенанта, живёт в переменной сайта, а не в общем шаблоне.
  • Каждый факт, специфичный для статьи, живёт в runtime, а не в #set.
  • Ни одно имя локального #set не пересекается с именами runtime-переменных.
  • Имена переменных ASCII, без пробелов и дефисов.
  • Каждая повторяющаяся переменная проверена на эффект reroll.
  • Каждый необязательный фрагмент обрабатывает пробелы внутри ветки.
  • Каждая список-переменная, за которой идёт союз, проверена на коллизию сепараторов.
  • Пять сэмплов рендера не содержат оставшихся %...%.

Структура готова? В следующем гайде — permutations на практике, где и живёт настоящая вариативность.


Продолжить серию