<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://cecil.app/xsl/atom.xsl" media="all"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://cecil.app/how-to/</id>
  <title>Cecil - How to?</title>
  <subtitle><![CDATA[Cecil is a command-line PHP application that merges Markdown pages, medias and Twig templates to generate a static website.]]></subtitle>
  <link href="https://cecil.app/how-to/atom.xml" rel="self" type="application/atom+xml" />
  <link href="https://cecil.app/how-to/" rel="alternate" type="text/html" />
  <updated>2026-10-07T21:40:49+00:00</updated>
  <author>
    <name>Cecil</name>
    <uri>https://cecil.app</uri>
  </author>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/deploy-github-pages/</id>
    <title>Deploy to GitHub Pages with GitHub Actions</title>
    <published>2026-10-07T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/deploy-github-pages/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>With the <a href="https://github.com/Cecilapp/Cecil-Action" target="_blank" rel="noopener noreferrer">Cecil Action</a>, every push to your repository builds the site and publishes it to <strong>GitHub Pages</strong>, with no server to manage.</p>
<h2 id="enable-github-pages">Enable GitHub Pages</h2>
<p>In your repository, go to <strong>Settings</strong> → <strong>Pages</strong> and, under <strong>Build and deployment</strong>, set <strong>Source</strong> to <strong>GitHub Actions</strong>.</p>
<h2 id="add-the-workflow">Add the workflow</h2>
<p>Create the file <code translate="no">.github/workflows/build-and-deploy.yml</code>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> <span class="hljs-string">[master,</span> <span class="hljs-string">main]</span>
  <span class="hljs-attr">workflow_dispatch:</span>
<span class="hljs-attr">concurrency:</span>
  <span class="hljs-attr">group:</span> <span class="hljs-string">pages</span>
  <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">source</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v6</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">Cecilapp/Cecil-Action@v4</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v5</span></code></pre>
<p>The <code translate="no">build</code> job downloads Cecil, installs themes (if a <code translate="no">composer.json</code> file exists), builds the site and uploads the output directory as a Pages artifact. The <code translate="no">deploy</code> job then publishes it.</p>
<h2 id="base-url">Base URL</h2>
<p>You don’t need to change <code translate="no">baseurl</code> in <code translate="no">cecil.yml</code>: the action builds the site with the URL provided by GitHub Pages (e.g. <code translate="no">https://&lt;user&gt;.github.io/&lt;repository&gt;/</code>), using the <code translate="no">--baseurl</code> option.</p>
<h2 id="customize-the-build">Customize the build</h2>
<p>The action accepts the following optional inputs:</p>
<pre><code class="language-yaml hljs yaml" translate="no">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">Cecilapp/Cecil-Action@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">version:</span> <span class="hljs-string">'9.6.2'</span>       <span class="hljs-comment"># Cecil version (latest by default)</span>
          <span class="hljs-attr">install_themes:</span> <span class="hljs-string">'no'</span>   <span class="hljs-comment"># skip themes installation (`yes` by default)</span>
          <span class="hljs-attr">options:</span> <span class="hljs-string">'-v --drafts'</span> <span class="hljs-comment"># build command options (`-v` by default)</span></code></pre>
<aside class="note note-tip"><p>To speed up builds, you can also restore and save the <code translate="no">.cache</code> directory between runs: see the full workflow in the <a href="/documentation/deploy/#github-pages">GitHub Pages deployment documentation</a>, and the list of <a href="/documentation/commands/#build">build options</a>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/output-rss-json-feed/</id>
    <title>Publish an RSS or JSON feed</title>
    <published>2026-10-06T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/output-rss-json-feed/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>Cecil renders feeds with <strong>output formats</strong>: by default, the home page, sections and taxonomy terms are already published in HTML <strong>and Atom</strong> (e.g. <code translate="no">/atom.xml</code>, <code translate="no">/blog/atom.xml</code>). Add the <code translate="no">rss</code> or <code translate="no">jsonfeed</code> formats to publish RSS 2.0 or <a href="https://www.jsonfeed.org" target="_blank" rel="noopener noreferrer">JSON Feed</a> files too.</p>
<h2 id="enable-feeds-for-all-list-pages">Enable feeds for all list pages</h2>
<p>Set the formats applied to each page type with <code translate="no">output.pagetypeformats</code>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">pagetypeformats:</span>
    <span class="hljs-attr">homepage:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom,</span> <span class="hljs-string">rss,</span> <span class="hljs-string">jsonfeed]</span>
    <span class="hljs-attr">section:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom,</span> <span class="hljs-string">rss,</span> <span class="hljs-string">jsonfeed]</span></code></pre>
<p>Cecil now generates <code translate="no">rss.xml</code> and <code translate="no">feed.json</code> next to each <code translate="no">index.html</code> of the home page and sections (e.g. <code translate="no">/blog/rss.xml</code>, <code translate="no">/blog/feed.json</code>).</p>
<aside class="note note-info"><p>Formats are replaced, not merged: keep <code translate="no">html</code> in the list. See <a href="/documentation/configuration/#output-pagetypeformats"><code translate="no">output.pagetypeformats</code></a> and the list of <a href="/documentation/configuration/#output-formats">default formats</a>.</p></aside>
<h2 id="enable-a-feed-for-a-single-section">Enable a feed for a single section</h2>
<p>To publish a feed only for one section, use the <a href="/documentation/content/#output"><code translate="no">output</code></a> variable in the front matter of the section index page:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">Blog</span>
<span class="hljs-attr">output:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">rss]</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="advertise-the-feed">Advertise the feed</h2>
<p>If your templates include the <a href="/documentation/configuration/#metatags">metatags partial</a>, <code translate="no">&lt;link rel="alternate"&gt;</code> tags pointing to the feeds of the current page are added automatically in the <code translate="no">&lt;head&gt;</code>.</p>
<p>Otherwise, add the link yourself with the <code translate="no">url()</code> function and its <code translate="no">format</code> option:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"alternate"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"application/rss+xml"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ site.title }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page, {canonical: true, format: 'rss'}) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span></code></pre>
<h2 id="customize-the-feed-template">Customize the feed template</h2>
<p>Feeds are rendered by the <a href="/documentation/templates/#built-in-templates">built-in templates</a> <code translate="no">_default/list.rss.twig</code>, <code translate="no">_default/list.atom.twig</code> and <code translate="no">_default/list.jsonfeed.twig</code>. Following the <a href="/documentation/templates/#lookup-rules">lookup rules</a>, create <code translate="no">layouts/list.rss.twig</code> (all list pages) or <code translate="no">layouts/blog/list.rss.twig</code> (<code translate="no">blog</code> section only) to override them.</p>
<p>You can extend the built-in template and redefine only the <code translate="no">item</code> block, for example to publish an excerpt instead of the full content:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">extends</span></span> '_default/list.rss.twig' %}</span><span class="xml">

</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">block</span></span> item %}</span><span class="xml">
      <span class="hljs-tag">&lt;<span class="hljs-name">guid</span>&gt;</span></span><span class="hljs-template-variable">{{ url(item, {canonical: true}) }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">guid</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span></span><span class="hljs-template-variable">{{ item.title|e }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">pubDate</span>&gt;</span></span><span class="hljs-template-variable">{{ item.<span class="hljs-name">date</span>|<span class="hljs-keyword">date</span>('r') }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">pubDate</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">link</span>&gt;</span></span><span class="hljs-template-variable">{{ url(item, {canonical: true}) }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>&lt;![CDATA[</span><span class="hljs-template-variable">{{ item.content|excerpt_html }}</span><span class="xml">]]&gt;<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endblock</span></span> %}</span></code></pre>
<aside class="note note-tip"><p>Set <a href="/documentation/configuration/#baseurl"><code translate="no">baseurl</code></a> in <code translate="no">cecil.yml</code>: feeds use absolute URLs.</p>
<p>The RSS feed can also be styled in browsers by enabling the <code translate="no">xsl/rss</code> <a href="/documentation/configuration/#pages-default">default page</a>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">default:</span>
    <span class="hljs-attr">xsl/rss:</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span></code></pre></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/templates-pagination/</id>
    <title>Paginate a list of pages</title>
    <published>2026-10-05T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/templates-pagination/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>When a section contains many pages, Cecil can split its list into several pages (e.g.: <code translate="no">/blog/</code>, <code translate="no">/blog/page/2/</code>, <code translate="no">/blog/page/3/</code>, etc.) and give you a <em>paginator</em> to build the navigation links.</p>
<h2 id="configure-pagination">Configure pagination</h2>
<p>Pagination is enabled by default for list pages (<em>homepage</em>, <em>section</em> and <em>term</em>), with 5 entries per page. Change it in <code translate="no">cecil.yml</code>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">pagination:</span>
    <span class="hljs-attr">max:</span> <span class="hljs-number">10</span>    <span class="hljs-comment"># maximum number of entries per page</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">page</span> <span class="hljs-comment"># path to the paginated page</span></code></pre>
<h2 id="override-it-for-a-section">Override it for a section</h2>
<p>Set the <code translate="no">pagination</code> variable in the front matter of the section index file (e.g.: <code translate="no">pages/blog/index.md</code>):</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">Blog</span>
<span class="hljs-attr">pagination:</span>
  <span class="hljs-attr">max:</span> <span class="hljs-number">20</span>
<span class="hljs-meta">---</span></code></pre>
<p>Or disable it for this section only:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">pagination:</span> <span class="hljs-literal">false</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="display-the-paginated-pages">Display the paginated pages</h2>
<p>In the list template (e.g.: <code translate="no">layouts/blog/list.html.twig</code>), loop over <code translate="no">page.paginator.pages</code> and fall back to <code translate="no">page.pages</code> when pagination is disabled:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> p in page.paginator.pages ?? page.pages %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(p) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ p.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ p.<span class="hljs-name">date</span>|<span class="hljs-keyword">date</span>('c') }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ p.<span class="hljs-name">date</span>|<span class="hljs-keyword">date</span>('j M Y') }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre>
<h2 id="add-navigation-links">Add navigation links</h2>
<p>Paginator links are page IDs, so use the <code translate="no">url()</code> function to create working links:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator.links.prev is defined %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.prev) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"prev"</span>&gt;</span>Previous<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Page </span><span class="hljs-template-variable">{{ page.paginator.current }}</span><span class="xml"> of </span><span class="hljs-template-variable">{{ page.paginator.count }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator.links.next is defined %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.next) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"next"</span>&gt;</span>Next<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<p>To list every page number, iterate from <code translate="no">1</code> to <code translate="no">page.paginator.count</code>:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> i in 1..page.paginator.count %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> i == page.paginator.current %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-current</span>=<span class="hljs-string">"page"</span>&gt;</span></span><span class="hljs-template-variable">{{ i }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name">elseif</span> i == 1 %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.first) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ i }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name">else</span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.path ~ '/' ~ i) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ i }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<aside class="note note-info"><p>See the documentation of the <a href="/documentation/templates/#page"><code translate="no">page.paginator</code> variable</a>, the <a href="/documentation/configuration/#pages-pagination">pagination configuration</a> and the <a href="/documentation/content/#section">section <code translate="no">pagination</code> variable</a>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/content-multilingual/</id>
    <title>Translate a site into multiple languages</title>
    <published>2026-10-04T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/content-multilingual/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>Cecil handles multilingual sites natively: declare the languages, add translated pages, then link them together and translate your templates.</p>
<h2 id="declare-languages">Declare languages</h2>
<p>Define the main language and the list of available languages in <code translate="no">cecil.yml</code>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">language:</span> <span class="hljs-string">en</span>
<span class="hljs-attr">languages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">en</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">English</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">en_US</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">fr</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Français</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">fr_FR</span>
    <span class="hljs-attr">config:</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">"Mon site en français"</span></code></pre>
<p>Options stored under the <code translate="no">config</code> key of a language override the global ones (here the site <code translate="no">title</code>).</p>
<h2 id="translate-a-page">Translate a page</h2>
<p>Duplicate the reference page and suffix its file name with the language code:</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">pages/
├─ about.md    # the reference page
└─ about.fr.md # the french version</code></pre>
<p>Use the <code translate="no">slug</code> variable to translate the URL of the page:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">À</span> <span class="hljs-string">propos</span>
<span class="hljs-attr">slug:</span> <span class="hljs-string">a-propos</span>
<span class="hljs-meta">---</span></code></pre>
<p><code translate="no">about.md</code> is published to <code translate="no">/about/</code> and <code translate="no">about.fr.md</code> to <code translate="no">/fr/a-propos/</code>.</p>
<aside class="note note-tip"><p>To create a page that only exists in another language (not a translation), set <code translate="no">language: fr</code> in its front matter.</p></aside>
<h2 id="link-translated-pages">Link translated pages</h2>
<p>Each page exposes its translations with <code translate="no">page.translations</code>. Add a language switcher to your template:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> p in page.translations %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(p) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">hreflang</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ p.language }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ site.language.name(p.language) }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre>
<p>You can also include the built-in partial: <code translate="no">{{ include('partials/languages.html.twig') }}</code>.</p>
<h2 id="translate-template-strings">Translate template strings</h2>
<p>Wrap texts with the <code translate="no">trans</code> tag or filter:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> %}</span><span class="xml">Read more</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ 'Read more'|trans }}</span></code></pre>
<p>Then add a translation file named after the language locale in the <code translate="no">translations</code> directory:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-comment"># translations/messages.fr_FR.yaml</span>
<span class="hljs-attr">Read more:</span> <span class="hljs-string">Lire</span> <span class="hljs-string">la</span> <span class="hljs-string">suite</span></code></pre>
<p>Extract the strings of your templates with:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar util:translations:extract --locale=fr_FR --save</code></pre>
<aside class="note note-info"><p>See the documentation of <a href="/documentation/content/#multilingual">multilingual content</a>, <a href="/documentation/configuration/#languages">languages configuration</a> and <a href="/documentation/templates/#localization">templates localization</a>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/templates-metatags/</id>
    <title>Apply SEO features in templates</title>
    <published>2026-06-09T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/templates-metatags/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>Cecil exposes its SEO feature through the <code translate="no">partials/metatags.html.twig</code> template. Include it in the <code translate="no">&lt;head&gt;</code> of your base template so every page automatically gets <strong>meta tags, canonical links, social cards, and structured data</strong>.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ site.language }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>&gt;</span>
    </span><span class="hljs-template-variable">{{ <span class="hljs-name">include</span><span class="hljs-params">('partials/metatags.html.twig')</span> }}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    ...
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span></code></pre>
<p>The partial reads page front matter first, then falls back to site configuration when needed. See the <a href="/documentation/configuration/#metatags">metatags configuration documentation</a> for all available options.</p>
<p>If you need to override the default title or image, pass values directly to the partial:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ <span class="hljs-name">include</span><span class="hljs-params">('partials/metatags.html.twig', {title: 'Custom title', image: og_image})</span> }}</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/markdown-performance-images/</id>
    <title>Optimize images in Markdown</title>
    <published>2025-06-01T00:00:00+00:00</published>
    <updated>2025-06-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/how-to/markdown-performance-images/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>One of the most effective ways to improve the performance of your website is to optimize images.</p>
<p>Cecil can manage the following optimizations for images in Markdown, automatically:</p>
<ol>
<li><strong>Dimensions</strong>: Images dimensions are set to ensure proper layout and prevent layout shifts</li>
<li><strong>Compression</strong>: The image is compressed to reduce file size without significant loss of quality</li>
<li><strong>Image formats</strong>: Cecil generates AVIF and WebP formats for the image</li>
<li><strong>Responsive images</strong>: Cecil generates two different sizes of the image (based on the configuration) to serve the appropriate size for different devices</li>
<li><strong>Lazy loading</strong>: The image is set to load lazily, meaning it will only load when it comes into the viewport</li>
<li><strong>Decoding</strong>: The image is set to decode asynchronously, improving the initial page load time</li>
<li><strong>Placeholder</strong>: A color placeholder is used while the image is loading</li>
</ol>
<aside class="note note-info"><p>See documentation for more details on the <a href="/documentation/configuration/#assets-images">global assets configuration</a> and the <a href="/documentation/configuration/#pages-body">pages configuration</a>.</p></aside>
<h2 id="example">Example</h2>
<p>Bellow an example with a PNG image 1920x1276 pixels.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml">![A star forming region in the sky](../../assets/arnaud-girault-IjEtFjxXweE-unsplash.jpg "Photo by Arnaud Girault"){placeholder=color}</span></code></pre>
<figure>
<picture title="Photo by Arnaud Girault">
<source type="image/avif" srcset="/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 1024w" width="1024" height="681" sizes="100vw">
<source type="image/webp" srcset="/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 1024w" width="1024" height="681" sizes="100vw">
<img src="/thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg" alt="A star forming region in the sky" loading="lazy" decoding="async" class="dark:brightness-90" width="1024" height="681" style=";max-width:100%;height:auto;background-color:rgb(30 41 43);" srcset="/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 1024w" sizes="100vw">
</picture>
<figcaption>Photo by <a href="https://unsplash.com/photos/a-star-forming-region-in-the-sky-IjEtFjxXweE" target="_blank" rel="noopener noreferrer">Arnaud Girault</a></figcaption>
</figure>
<h3 id="configuration">Configuration</h3>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-comment"># assets global configuration</span>
<span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">optimize:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">responsive:</span>
      <span class="hljs-attr">widths:</span> <span class="hljs-string">[768,</span> <span class="hljs-number">1024</span><span class="hljs-string">]</span>
<span class="hljs-comment"># configuration of images in Markdown</span>
<span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">images:</span>
      <span class="hljs-attr">formats:</span> <span class="hljs-string">[avif,</span> <span class="hljs-string">webp]</span>
      <span class="hljs-attr">responsive:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">lazy:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">decoding:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">placeholder:</span> <span class="hljs-string">color</span></code></pre>
<h3 id="generated-html">Generated HTML</h3>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">picture</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/avif"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 1024w,
            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 768w"</span>
    <span class="hljs-attr">sizes</span>=<span class="hljs-string">"100vw"</span>
    <span class="hljs-attr">width</span>=<span class="hljs-string">"1024"</span>
    <span class="hljs-attr">height</span>=<span class="hljs-string">"681"</span>
  &gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 1024w,
            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 768w"</span>
    <span class="hljs-attr">sizes</span>=<span class="hljs-string">"100vw"</span>
    <span class="hljs-attr">width</span>=<span class="hljs-string">"1024"</span>
    <span class="hljs-attr">height</span>=<span class="hljs-string">"681"</span>
  &gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg"</span>
    <span class="hljs-attr">alt</span>=<span class="hljs-string">"Photo by Arnaud Girault"</span>
    <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
    <span class="hljs-attr">decoding</span>=<span class="hljs-string">"async"</span>
    <span class="hljs-attr">width</span>=<span class="hljs-string">"1024"</span>
    <span class="hljs-attr">height</span>=<span class="hljs-string">"681"</span>
    <span class="hljs-attr">style</span>=<span class="hljs-string">";max-width:100%;height:auto;background-color:rgb(58, 56, 44);"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 1024w,
            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 768w"</span>
    <span class="hljs-attr">sizes</span>=<span class="hljs-string">"100vw"</span>
  &gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">picture</span>&gt;</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/templates-image-formats/</id>
    <title>Generate multiple formats of an image in templates</title>
    <published>2025-06-01T00:00:00+00:00</published>
    <link href="https://cecil.app/how-to/templates-image-formats/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>For performance and bandwidth optimization, Cecil can generate multiple formats of an image, such as AVIF and WebP. This allows the browser to select the best format based on its capabilities:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={formats: ['avif', 'webp']}) }}</span></code></pre>
<blockquote>
<p>AVIF and WebP are image formats that have superior compression and quality characteristics compared to their older JPEG and PNG counterparts. Encoding your images in these formats rather than JPEG or PNG means that they will load faster and consume less cellular data.</p>
<p>AVIF is supported in Chrome, Firefox, and Opera and offers smaller file sizes compared to other formats with the same quality settings.</p>
<p>WebP is supported in the latest versions of Chrome, Firefox, Safari, Edge, and Opera and provides better lossy and lossless compression for images on the web.</p>
</blockquote>
<p>– <a href="https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images" target="_blank" rel="noopener noreferrer">Chrome Lighthouse docs</a></p>
<h2 id="example">Example</h2>
<p>Below an example of how to generate AVIF and WebP formats of an image:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ html(asset('cecil-logo.png'), attributes={alt: 'Cecil logo'}, options={formats: ['avif', 'webp']}) }}</span></code></pre>
<h3 id="generated-html">Generated HTML</h3>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">picture</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/avif"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.avif"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.webp"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.png"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"250"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"250"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Cecil logo"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">picture</span>&gt;</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/how-to/templates-responsive-images/</id>
    <title>Render responsive images in templates</title>
    <published>2024-01-18T00:00:00+00:00</published>
    <updated>2025-05-12T00:00:00+00:00</updated>
    <link href="https://cecil.app/how-to/templates-responsive-images/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>The easiest way to render <a href="https://developer.mozilla.org/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images" target="_blank" rel="noopener noreferrer">responsive images</a> in templates is with the <a href="/documentation/templates/#html">html function</a>:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={responsive: true}) }}</span></code></pre>
<aside class="note note-important"><p>The default width values of the generated images are 480, 640, 768, 1024, 1366, 1600 and 1920. They can be modified in the <em>assets</em> section of the <a href="/documentation/configuration/#assets-images">configuration</a>.</p></aside>
<h2 id="example">Example</h2>
<p>Below an example with a PNG image 1000x1000 pixels.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ html(asset('cecil-logo-1000.png'), attributes={alt: 'Cecil logo'}, options={responsive: true}) }}</span></code></pre>
<h3 id="configuration">Configuration</h3>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">responsive:</span>
      <span class="hljs-attr">widths:</span> <span class="hljs-string">[768,</span> <span class="hljs-number">1024</span><span class="hljs-string">]</span></code></pre>
<h3 id="generated-html">Generated HTML</h3>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
  <span class="hljs-attr">alt</span>=<span class="hljs-string">"Cecil logo"</span>
  <span class="hljs-attr">width</span>=<span class="hljs-string">"1000"</span>
  <span class="hljs-attr">height</span>=<span class="hljs-string">"1000"</span>
  <span class="hljs-attr">src</span>=<span class="hljs-string">"/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png"</span>
  <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/thumbnails/768/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 768w,
          /cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 1000w"</span>
  <span class="hljs-attr">sizes</span>=<span class="hljs-string">"100vw"</span>
&gt;</span></code></pre>]]>
    </content>
  </entry>
</feed>
