her.esy.fun/src/Scratch/en/blog/Hakyll-setup/index.html
Yann Esposito (Yogsototh) 03610908ce
Old site match new style
2021-05-25 22:25:47 +02:00

521 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>YBlog - Hakyll setup</title>
<meta name="keywords" content="programming, hakyll, Haskell, nanoc" />
<link rel="shortcut icon" type="image/x-icon" href="../../../../Scratch/img/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/css/y.css" />
<link rel="stylesheet" type="text/css" href="/css/legacy.css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="../../../../Scratch/img/about/FlatAvatar@2x.png" />
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<!-- IndieAuth -->
<link href="https://twitter.com/yogsototh" rel="me">
<link href="https://github.com/yogsototh" rel="me">
<link href="mailto:yann.esposito@gmail.com" rel="me">
<link rel="pgpkey" href="../../../../pubkey.txt">
</head>
<body lang="en" class="article">
<div id="content">
<div id="header">
<div id="choix">
<span id="choixlang">
<a href="../../../../Scratch/fr/blog/Hakyll-setup/">French</a>
</span>
<span class="tomenu"><a href="#navigation">↓ Menu ↓</a></span>
<span class="flush"></span>
</div>
</div>
<div id="titre">
<h1>Hakyll setup</h1>
</div>
<div class="flush"></div>
<div id="afterheader" class="article">
<div class="corps">
<div>
<img src="../../../../Scratch/img/blog/Hakyll-setup/main.png" alt="Main image" />
</div>
<div class="intro">
<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> How I use <a href="http://jaspervdj.be/hakyll">hakyll</a>. Abbreviations, typography corrections, multi-language, use <code>index.html</code>, etc…</p>
</div>
<p>This website is done with <a href="http://jaspervdj.be/hakyll">Hakyll</a>.</p>
<p><a href="http://jaspervdj.be/hakyll">Hakyll</a> can be considered as a minimal <span class="sc"><abbr title="Content Management System">cms</abbr></span>. But more generally it is a library helping file generation. We can view it as an advanced build system (like <code>make</code>).</p>
<p>From the user perspective I blog this way:</p>
<ol type="1">
<li>I open an editor (vim in my case) and edit a markdown file. It looks like this</li>
</ol>
<div class="sourceCode" id="cb1"><pre class="sourceCode markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb1-1" title="1">A First Level Header</a>
<a class="sourceLine" id="cb1-2" title="2">====================</a>
<a class="sourceLine" id="cb1-3" title="3"></a>
<a class="sourceLine" id="cb1-4" title="4">A Second Level Header</a>
<a class="sourceLine" id="cb1-5" title="5">---------------------</a>
<a class="sourceLine" id="cb1-6" title="6"></a>
<a class="sourceLine" id="cb1-7" title="7">Who would cross the Bridge of Death must answer me</a>
<a class="sourceLine" id="cb1-8" title="8">these questions three, ere the other side he see.</a>
<a class="sourceLine" id="cb1-9" title="9">This is just a regular paragraph.</a>
<a class="sourceLine" id="cb1-10" title="10"></a>
<a class="sourceLine" id="cb1-11" title="11">Ask me the questions, bridgekeeper. I am not afraid.</a>
<a class="sourceLine" id="cb1-12" title="12"></a>
<a class="sourceLine" id="cb1-13" title="13"><span class="fu">### Header 3</span></a>
<a class="sourceLine" id="cb1-14" title="14"></a>
<a class="sourceLine" id="cb1-15" title="15">&gt;<span class="dt"> This is a blockquote.</span></a>
<a class="sourceLine" id="cb1-16" title="16"><span class="dt">&gt;</span></a>
<a class="sourceLine" id="cb1-17" title="17"><span class="dt">&gt; This is the second paragraph in the blockquote.</span></a>
<a class="sourceLine" id="cb1-18" title="18"><span class="dt">&gt;</span></a>
<a class="sourceLine" id="cb1-19" title="19"><span class="dt">&gt; ## This is an H2 in a blockquote</span></a></code></pre></div>
<ol start="2" type="1">
<li>I open a browser and reload time to time to see the change.</li>
<li>Once I finished Ive written a very minimal script which mainly do a <code>git push</code>. My blog is hosted on <a href="http://github.com">github</a>.</li>
</ol>
<p>Being short sighted one could reduce the role of Hakyll to:</p>
<blockquote>
<p>create (resp. update) <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> file when I create (resp. change) a markdown file.</p>
</blockquote>
<p>While it sounds easy, there are a lot of hidden details:</p>
<ul>
<li>Add metadatas like keywords.</li>
<li>Create an archive page containing a list of all the posts.</li>
<li>Deal with static files.</li>
<li>Creating an <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span> feed.</li>
<li>Filter the content with some function.</li>
<li>Dealing with dependencies.</li>
</ul>
<p>The work of Hakyll is to help you with these. But lets start with the basic concepts.</p>
<h2 id="the-concepts-and-syntax">The concepts and syntax</h2>
<div>
<img src="../../../../Scratch/img/blog/Hakyll-setup/overview.png" alt="Overview" />
</div>
<p>For each file you create, you have to provide:</p>
<ul>
<li>a destination path</li>
<li>a list of content filters.</li>
</ul>
<p>First, lets start with the simplest case: static files (images, fonts, etc…). Generally, you have a source directory (here is the current directory) and a destination directory <code>_site</code>.</p>
<p>The Hakyll code is:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb2-1" title="1"><span class="co">-- for each file in the static directory</span></a>
<a class="sourceLine" id="cb2-2" title="2">match <span class="st">&quot;static/*&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb2-3" title="3"> <span class="co">-- don't change its name nor directory</span></a>
<a class="sourceLine" id="cb2-4" title="4"> route idRoute</a>
<a class="sourceLine" id="cb2-5" title="5"> <span class="co">-- don't change its content</span></a>
<a class="sourceLine" id="cb2-6" title="6"> compile copyFileCompiler</a></code></pre></div>
<p>This program will copy <code>static/foo.jpg</code> to <code>_site/static/foo.jpg</code>. I concede this is a bit overkill for a simple <code>cp</code>. Now how to write a markdown file and generate an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> one?</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb3-1" title="1"><span class="co">-- for each file with md extension in the &quot;posts/&quot; directory</span></a>
<a class="sourceLine" id="cb3-2" title="2">match <span class="st">&quot;posts/*.md&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb3-3" title="3"> <span class="co">-- change its extension to html</span></a>
<a class="sourceLine" id="cb3-4" title="4"> route <span class="op">$</span> setExtension <span class="st">&quot;html&quot;</span></a>
<a class="sourceLine" id="cb3-5" title="5"> <span class="co">-- use pandoc library to compile the markdown content into html</span></a>
<a class="sourceLine" id="cb3-6" title="6"> compile <span class="op">$</span> pandocCompiler</a></code></pre></div>
<p>If you create a file <code>posts/foo.md</code>, it will create a file <code>_site/posts/foo.html</code>.</p>
<p>If the file <code>posts/foo.md</code> contains</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb4-1" title="1"><span class="fu"># Cthulhu</span></a>
<a class="sourceLine" id="cb4-2" title="2"></a>
<a class="sourceLine" id="cb4-3" title="3">ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn</a></code></pre></div>
<p>the file <code>_site/posts/foo.html</code>, will contain</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb5-1" title="1"><span class="kw">&lt;h1&gt;</span>Cthulhu<span class="kw">&lt;/h1&gt;</span></a>
<a class="sourceLine" id="cb5-2" title="2"><span class="kw">&lt;p&gt;</span>ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn<span class="kw">&lt;/p&gt;</span></a></code></pre></div>
<p>But horror! <code>_site/posts/cthulhu.html</code> is not a complete <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> file. It doesnt have any header nor footer, etc… This is where you use templates. I simply add a new directive in the compile block.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb6-1" title="1">match <span class="st">&quot;posts/*.md&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb6-2" title="2"> route <span class="op">$</span> setExtension <span class="st">&quot;html&quot;</span></a>
<a class="sourceLine" id="cb6-3" title="3"> compile <span class="op">$</span> pandocCompiler</a>
<a class="sourceLine" id="cb6-4" title="4"> <span class="co">-- use the template with the current content</span></a>
<a class="sourceLine" id="cb6-5" title="5"> <span class="highlight"><span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/post.html&quot;</span> defaultContext</span></a></code></pre></div>
<p>Now if <code>templates/posts.html</code> contains:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb7-1" title="1"><span class="kw">&lt;html&gt;</span></a>
<a class="sourceLine" id="cb7-2" title="2"> <span class="kw">&lt;head&gt;</span></a>
<a class="sourceLine" id="cb7-3" title="3"> <span class="kw">&lt;title&gt;</span>How could I get the title?<span class="kw">&lt;/title&gt;</span></a>
<a class="sourceLine" id="cb7-4" title="4"> <span class="kw">&lt;/head&gt;</span></a>
<a class="sourceLine" id="cb7-5" title="5"> <span class="kw">&lt;body&gt;</span></a>
<a class="sourceLine" id="cb7-6" title="6"> <span class="highlight">$body$</span></a>
<a class="sourceLine" id="cb7-7" title="7"> <span class="kw">&lt;/body&gt;</span></a>
<a class="sourceLine" id="cb7-8" title="8"><span class="kw">&lt;/html&gt;</span></a></code></pre></div>
<p>our <code>cthulhu.html</code> contains (indentation added for readability):</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb8-1" title="1"><span class="kw">&lt;html&gt;</span></a>
<a class="sourceLine" id="cb8-2" title="2"> <span class="kw">&lt;head&gt;</span></a>
<a class="sourceLine" id="cb8-3" title="3"> <span class="kw">&lt;title&gt;</span>How could I get the title?<span class="kw">&lt;/title&gt;</span></a>
<a class="sourceLine" id="cb8-4" title="4"> <span class="kw">&lt;/head&gt;</span></a>
<a class="sourceLine" id="cb8-5" title="5"> <span class="kw">&lt;body&gt;</span></a>
<a class="sourceLine" id="cb8-6" title="6"> <span class="highlight"><span class="kw">&lt;h1&gt;</span>Cthulhu<span class="kw">&lt;/h1&gt;</span></span></a>
<a class="sourceLine" id="cb8-7" title="7"> <span class="highlight"><span class="kw">&lt;p&gt;</span>ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn<span class="kw">&lt;/p&gt;</span></span></a>
<a class="sourceLine" id="cb8-8" title="8"> <span class="kw">&lt;/body&gt;</span></a>
<a class="sourceLine" id="cb8-9" title="9"><span class="kw">&lt;/html&gt;</span></a></code></pre></div>
<p>See, its easy But we have a problem. How could we change the title or add keywords?</p>
<p>The solution is to use <code>Context</code>s. For this, we first need to add some <em>metadatas</em> to our markdown<a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a>.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb9-1" title="1"><span class="highlight">--- </span></a>
<a class="sourceLine" id="cb9-2" title="2"><span class="highlight">title: Cthulhu</span></a>
<a class="sourceLine" id="cb9-3" title="3"><span class="highlight">--- </span></a>
<a class="sourceLine" id="cb9-4" title="4"><span class="fu"># Cthulhu</span></a>
<a class="sourceLine" id="cb9-5" title="5"></a>
<a class="sourceLine" id="cb9-6" title="6">ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn</a></code></pre></div>
<p>And modify slightly our template:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb10-1" title="1"><span class="kw">&lt;html&gt;</span></a>
<a class="sourceLine" id="cb10-2" title="2"> <span class="kw">&lt;head&gt;</span></a>
<a class="sourceLine" id="cb10-3" title="3"> <span class="kw">&lt;title&gt;</span><span class="highlight">$title$</span><span class="kw">&lt;/title&gt;</span></a>
<a class="sourceLine" id="cb10-4" title="4"> <span class="kw">&lt;/head&gt;</span></a>
<a class="sourceLine" id="cb10-5" title="5"> <span class="kw">&lt;body&gt;</span></a>
<a class="sourceLine" id="cb10-6" title="6"> $body$</a>
<a class="sourceLine" id="cb10-7" title="7"> <span class="kw">&lt;/body&gt;</span></a>
<a class="sourceLine" id="cb10-8" title="8"><span class="kw">&lt;/html&gt;</span></a></code></pre></div>
<p>As Sir Robin said just before dying before the Bridge of Death:</p>
<blockquote>
<p><strong>“Thats EASY!”</strong></p>
<p> <cite>Sir Robin, the Not-Quite-So-Brave-As-Sir-Lancelot</cite></p>
</blockquote>
<h2 id="real-customization">Real customization</h2>
<p>Now that we understand the basic functionality. How to:</p>
<ul>
<li>use SASS?</li>
<li>add keywords?</li>
<li>simplify <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span>?</li>
<li>create an archive page?</li>
<li>create an <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span> feed?</li>
<li>filter the content?</li>
<li>add abbreviations support?</li>
<li>manage two languages?</li>
</ul>
<h3 id="use-sass">Use SASS</h3>
<p>Thats easy. Simply call the executable using <code>unixFilter</code>. Of course youll have to install SASS (<code>gem install sass</code>). And we also use compressCss to gain some space.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb11-1" title="1">match <span class="st">&quot;css/*&quot;</span> <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb11-2" title="2"> route <span class="op">$</span> setExtension <span class="st">&quot;css&quot;</span></a>
<a class="sourceLine" id="cb11-3" title="3"> compile <span class="op">$</span> getResourceString <span class="op">&gt;&gt;=</span></a>
<a class="sourceLine" id="cb11-4" title="4"> withItemBody (unixFilter <span class="st">&quot;sass&quot;</span> [<span class="st">&quot;--trace&quot;</span>]) <span class="op">&gt;&gt;=</span></a>
<a class="sourceLine" id="cb11-5" title="5"> <span class="fu">return</span> <span class="op">.</span> <span class="fu">fmap</span> compressCss</a></code></pre></div>
<h3 id="add-keywords">Add keywords</h3>
<p>In order to help to reference your website on the web, it is nice to add some keywords as meta datas to your <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> page.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb12-1" title="1"><span class="kw">&lt;meta</span><span class="ot"> name=</span><span class="st">&quot;keywords&quot;</span></a>
<a class="sourceLine" id="cb12-2" title="2"><span class="ot"> content=</span><span class="st">&quot;Cthulhu, Yog-Sothoth, Shub-Niggurath&quot;</span><span class="kw">&gt;</span></a></code></pre></div>
<p>In order to add keywords, we could not directly use the markdown metadatas. Because, without any, there should be any meta tag in the <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span>.</p>
<p>An easy answer is to create a <code>Context</code> that will contains the meta tag.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb13-1" title="1"><span class="co">-- metaKeywordContext will return a Context containing a String</span></a>
<a class="sourceLine" id="cb13-2" title="2"><span class="ot">metaKeywordContext ::</span> <span class="dt">Context</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb13-3" title="3"><span class="co">-- can be reached using $metaKeywords$ in the templates</span></a>
<a class="sourceLine" id="cb13-4" title="4"><span class="co">-- Use the current item (markdown file)</span></a>
<a class="sourceLine" id="cb13-5" title="5">metaKeywordContext <span class="ot">=</span> field <span class="st">&quot;metaKeywords&quot;</span> <span class="op">$</span> \item <span class="ot">-&gt;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb13-6" title="6"> <span class="co">-- tags contains the content of the &quot;tags&quot; metadata</span></a>
<a class="sourceLine" id="cb13-7" title="7"> <span class="co">-- inside the item (understand the source)</span></a>
<a class="sourceLine" id="cb13-8" title="8"> tags <span class="ot">&lt;-</span> getMetadataField (itemIdentifier item) <span class="st">&quot;tags&quot;</span></a>
<a class="sourceLine" id="cb13-9" title="9"> <span class="co">-- if tags is empty return an empty string</span></a>
<a class="sourceLine" id="cb13-10" title="10"> <span class="co">-- in the other case return</span></a>
<a class="sourceLine" id="cb13-11" title="11"> <span class="co">-- &lt;meta name=&quot;keywords&quot; content=&quot;$tags$&quot;&gt;</span></a>
<a class="sourceLine" id="cb13-12" title="12"> <span class="fu">return</span> <span class="op">$</span> <span class="fu">maybe</span> <span class="st">&quot;&quot;</span> showMetaTags tags</a>
<a class="sourceLine" id="cb13-13" title="13"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb13-14" title="14"> showMetaTags t <span class="ot">=</span> <span class="st">&quot;&lt;meta name=\&quot;keywords\&quot; content=\&quot;&quot;</span></a>
<a class="sourceLine" id="cb13-15" title="15"> <span class="op">++</span> t <span class="op">++</span> <span class="st">&quot;\&quot;&gt;\n&quot;</span></a></code></pre></div>
<p>Then we pass this <code>Context</code> to the <code>loadAndApplyTemplate</code> function:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb14-1" title="1">match <span class="st">&quot;posts/*.md&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb14-2" title="2"> route <span class="op">$</span> setExtension <span class="st">&quot;html&quot;</span></a>
<a class="sourceLine" id="cb14-3" title="3"> compile <span class="op">$</span> pandocCompiler</a>
<a class="sourceLine" id="cb14-4" title="4"> <span class="co">-- use the template with the current content</span></a>
<a class="sourceLine" id="cb14-5" title="5"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/post.html&quot;</span></a>
<a class="sourceLine" id="cb14-6" title="6"> (defaultContext <span class="highlight"><span class="op">&lt;&gt;</span> metaKeywordContext</span>)</a></code></pre></div>
<blockquote>
<p>☞ Here are the imports I use for this tutorial.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb15-1" title="1"><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></a>
<a class="sourceLine" id="cb15-2" title="2"><span class="kw">import</span> <span class="dt">Control.Monad</span> (forM,forM_)</a>
<a class="sourceLine" id="cb15-3" title="3"><span class="kw">import</span> <span class="dt">Data.List</span> (sortBy,isInfixOf)</a>
<a class="sourceLine" id="cb15-4" title="4"><span class="kw">import</span> <span class="dt">Data.Monoid</span> ((&lt;&gt;),mconcat)</a>
<a class="sourceLine" id="cb15-5" title="5"><span class="kw">import</span> <span class="dt">Data.Ord</span> (comparing)</a>
<a class="sourceLine" id="cb15-6" title="6"><span class="kw">import</span> <span class="dt">Hakyll</span></a>
<a class="sourceLine" id="cb15-7" title="7"><span class="kw">import</span> <span class="dt">System.Locale</span> (defaultTimeLocale)</a>
<a class="sourceLine" id="cb15-8" title="8"><span class="kw">import</span> <span class="dt">System.FilePath.Posix</span> (takeBaseName,takeDirectory</a>
<a class="sourceLine" id="cb15-9" title="9"> ,(<span class="op">&lt;/&gt;</span>),splitFileName)</a></code></pre></div>
</blockquote>
<h3 id="simplify-url">Simplify <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span></h3>
<p>What I mean is to use url of the form:</p>
<pre><code>http://domain.name/post/title-of-the-post/</code></pre>
<p>I prefer this than having to add file with <code>.html</code> extension. We have to change the default Hakyll route behavior. We create another function <code>niceRoute</code>.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb17-1" title="1"><span class="co">-- replace a foo/bar.md by foo/bar/index.html</span></a>
<a class="sourceLine" id="cb17-2" title="2"><span class="co">-- this way the url looks like: foo/bar in most browsers</span></a>
<a class="sourceLine" id="cb17-3" title="3"><span class="ot">niceRoute ::</span> <span class="dt">Routes</span></a>
<a class="sourceLine" id="cb17-4" title="4">niceRoute <span class="ot">=</span> customRoute createIndexRoute</a>
<a class="sourceLine" id="cb17-5" title="5"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb17-6" title="6"> createIndexRoute ident <span class="ot">=</span></a>
<a class="sourceLine" id="cb17-7" title="7"> takeDirectory p <span class="op">&lt;/&gt;</span> takeBaseName p <span class="op">&lt;/&gt;</span> <span class="st">&quot;index.html&quot;</span></a>
<a class="sourceLine" id="cb17-8" title="8"> <span class="kw">where</span> p<span class="ot">=</span>toFilePath ident</a></code></pre></div>
<p>Not too difficult. But! There might be a problem. What if there is a <code>foo/index.html</code> link instead of a clean <code>foo/</code> in some content?</p>
<p>Very simple, we simply remove all <code>/index.html</code> to all our links.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb18-1" title="1"><span class="co">-- replace url of the form foo/bar/index.html by foo/bar</span></a>
<a class="sourceLine" id="cb18-2" title="2"><span class="ot">removeIndexHtml ::</span> <span class="dt">Item</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Compiler</span> (<span class="dt">Item</span> <span class="dt">String</span>)</a>
<a class="sourceLine" id="cb18-3" title="3">removeIndexHtml item <span class="ot">=</span> <span class="fu">return</span> <span class="op">$</span> <span class="fu">fmap</span> (withUrls removeIndexStr) item</a>
<a class="sourceLine" id="cb18-4" title="4"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb18-5" title="5"><span class="ot"> removeIndexStr ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb18-6" title="6"> removeIndexStr url <span class="ot">=</span> <span class="kw">case</span> splitFileName url <span class="kw">of</span></a>
<a class="sourceLine" id="cb18-7" title="7"> (dir, <span class="st">&quot;index.html&quot;</span>) <span class="op">|</span> isLocal dir <span class="ot">-&gt;</span> dir</a>
<a class="sourceLine" id="cb18-8" title="8"> _ <span class="ot">-&gt;</span> url</a>
<a class="sourceLine" id="cb18-9" title="9"> <span class="kw">where</span> isLocal uri <span class="ot">=</span> <span class="fu">not</span> (isInfixOf <span class="st">&quot;://&quot;</span> uri)</a></code></pre></div>
<p>And we apply this filter at the end of our compilation</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb19-1" title="1">match <span class="st">&quot;posts/*.md&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb19-2" title="2"> <span class="highlight">route <span class="op">$</span> niceRoute</span></a>
<a class="sourceLine" id="cb19-3" title="3"> compile <span class="op">$</span> pandocCompiler</a>
<a class="sourceLine" id="cb19-4" title="4"> <span class="co">-- use the template with the current content</span></a>
<a class="sourceLine" id="cb19-5" title="5"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/post.html&quot;</span> defaultContext</a>
<a class="sourceLine" id="cb19-6" title="6"> <span class="highlight"><span class="op">&gt;&gt;=</span> removeIndexHtml</span></a></code></pre></div>
<h3 id="create-an-archive-page">Create an archive page</h3>
<p>Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in <code>2013-03-20-My-New-Post.md</code>.</p>
<p>I migrated from an older blog and didnt want to change my <span class="sc"><abbr title="Uniform Ressource Locator">url</abbr></span>. Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata <code>published</code>. And the solution is here:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb20-1" title="1">match <span class="st">&quot;archive.md&quot;</span> <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb20-2" title="2"> route <span class="op">$</span> niceRoute</a>
<a class="sourceLine" id="cb20-3" title="3"> compile <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb20-4" title="4"> body <span class="ot">&lt;-</span> getResourceBody</a>
<a class="sourceLine" id="cb20-5" title="5"> <span class="fu">return</span> <span class="op">$</span> renderPandoc body</a>
<a class="sourceLine" id="cb20-6" title="6"> <span class="highlight"><span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/archive.html&quot;</span> archiveCtx</span></a>
<a class="sourceLine" id="cb20-7" title="7"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="highlight"><span class="st">&quot;templates/base.html&quot;</span></span> defaultContext</a>
<a class="sourceLine" id="cb20-8" title="8"> <span class="op">&gt;&gt;=</span> removeIndexHtml</a></code></pre></div>
<p>Where <code>templates/archive.html</code> contains</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb21-1" title="1">$body$</a>
<a class="sourceLine" id="cb21-2" title="2"></a>
<a class="sourceLine" id="cb21-3" title="3"><span class="kw">&lt;ul&gt;</span></a>
<a class="sourceLine" id="cb21-4" title="4"> $posts$</a>
<a class="sourceLine" id="cb21-5" title="5"><span class="kw">&lt;/ul&gt;</span></a></code></pre></div>
<p>And <code>base.html</code> is a standard template (simpler than <code>post.html</code>).</p>
<p><code>archiveCtx</code> provide a context containing an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> representation of a list of posts in the metadata named <code>posts</code>. It will be used in the <code>templates/archive.html</code> file with <code>$posts$</code>.</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb22-1" title="1">archiveCtx <span class="ot">=</span></a>
<a class="sourceLine" id="cb22-2" title="2"> defaultContext <span class="op">&lt;&gt;</span></a>
<a class="sourceLine" id="cb22-3" title="3"> metaKeywordContext <span class="op">&lt;&gt;</span></a>
<a class="sourceLine" id="cb22-4" title="4"> <span class="highlight">field <span class="st">&quot;posts&quot;</span> (\_ <span class="ot">-&gt;</span> postList createdFirst)</span></a></code></pre></div>
<p><code>postList</code> returns an <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is <code>post-item.html</code>:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb23-1" title="1"><span class="kw">&lt;li&gt;&lt;a</span><span class="ot"> href=</span><span class="st">&quot;$url$&quot;</span><span class="kw">&gt;</span>$published$ - $title$<span class="kw">&lt;/a&gt;&lt;/li&gt;</span></a></code></pre></div>
<p>Here is how it is done:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb24-1" title="1"><span class="ot">postList ::</span> [<span class="dt">Item</span> <span class="dt">String</span>] <span class="ot">-&gt;</span> <span class="dt">Compiler</span> [<span class="dt">Item</span> <span class="dt">String</span>]</a>
<a class="sourceLine" id="cb24-2" title="2"> <span class="ot">-&gt;</span> <span class="dt">Compiler</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb24-3" title="3">postList sortFilter <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb24-4" title="4"> <span class="co">-- sorted posts</span></a>
<a class="sourceLine" id="cb24-5" title="5"> posts <span class="ot">&lt;-</span> loadAll <span class="st">&quot;post/*&quot;</span> <span class="op">&gt;&gt;=</span> sortFilter</a>
<a class="sourceLine" id="cb24-6" title="6"> itemTpl <span class="ot">&lt;-</span> loadBody <span class="st">&quot;templates/post-item.html&quot;</span></a>
<a class="sourceLine" id="cb24-7" title="7"> <span class="co">-- we apply the template to all post</span></a>
<a class="sourceLine" id="cb24-8" title="8"> <span class="co">-- and we concatenate the result.</span></a>
<a class="sourceLine" id="cb24-9" title="9"> <span class="co">-- list is a string</span></a>
<a class="sourceLine" id="cb24-10" title="10"> list <span class="ot">&lt;-</span> applyTemplateList itemTpl defaultContext posts</a>
<a class="sourceLine" id="cb24-11" title="11"> <span class="fu">return</span> list</a></code></pre></div>
<p><code>createdFirst</code> sort a list of item and put it inside <code>Compiler</code> context. We need to be in the <code>Compiler</code> context to access metadatas.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb25-1" title="1"><span class="ot">createdFirst ::</span> [<span class="dt">Item</span> <span class="dt">String</span>] <span class="ot">-&gt;</span> <span class="dt">Compiler</span> [<span class="dt">Item</span> <span class="dt">String</span>]</a>
<a class="sourceLine" id="cb25-2" title="2">createdFirst items <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb25-3" title="3"> <span class="co">-- itemsWithTime is a list of couple (date,item)</span></a>
<a class="sourceLine" id="cb25-4" title="4"> itemsWithTime <span class="ot">&lt;-</span> forM items <span class="op">$</span> \item <span class="ot">-&gt;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb25-5" title="5"> <span class="co">-- getItemUTC will look for the metadata &quot;published&quot; or &quot;date&quot;</span></a>
<a class="sourceLine" id="cb25-6" title="6"> <span class="co">-- then it will try to get the date from some standard formats</span></a>
<a class="sourceLine" id="cb25-7" title="7"> utc <span class="ot">&lt;-</span> getItemUTC defaultTimeLocale <span class="op">$</span> itemIdentifier item</a>
<a class="sourceLine" id="cb25-8" title="8"> <span class="fu">return</span> (utc,item)</a>
<a class="sourceLine" id="cb25-9" title="9"> <span class="co">-- we return a sorted item list</span></a>
<a class="sourceLine" id="cb25-10" title="10"> <span class="fu">return</span> <span class="op">$</span> <span class="fu">map</span> <span class="fu">snd</span> <span class="op">$</span> <span class="fu">reverse</span> <span class="op">$</span> sortBy (comparing <span class="fu">fst</span>) itemsWithTime</a></code></pre></div>
<p>It wasnt so easy. But it works pretty well.</p>
<h3 id="create-an-rss-feed">Create an <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span> feed</h3>
<p>To create an <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span> feed, we have to:</p>
<ul>
<li>select only the lasts posts.</li>
<li>generate partially rendered posts (no css, js, etc…)</li>
</ul>
<p>We could then render the posts twice. One for <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> rendering and another time for <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span>. Remark we need to generate the <span class="sc"><abbr title="Rich Site Summary">rss</abbr></span> version to create the <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> one.</p>
<p>One of the great feature of Hakyll is to be able to save snapshots. Here is how:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb26-1" title="1">match <span class="st">&quot;posts/*.md&quot;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb26-2" title="2"> route <span class="op">$</span> setExtension <span class="st">&quot;html&quot;</span></a>
<a class="sourceLine" id="cb26-3" title="3"> compile <span class="op">$</span> pandocCompiler</a>
<a class="sourceLine" id="cb26-4" title="4"> <span class="co">-- save a snapshot to be used later in rss generation</span></a>
<a class="sourceLine" id="cb26-5" title="5"> <span class="highlight"><span class="op">&gt;&gt;=</span> saveSnapshot <span class="st">&quot;content&quot;</span></span></a>
<a class="sourceLine" id="cb26-6" title="6"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/post.html&quot;</span> defaultContext</a></code></pre></div>
<p>Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed dont need a source markdown file. Then we create a new file from no one. Instead of using <code>match</code>, we use <code>create</code>:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb27-1" title="1">create [<span class="st">&quot;feed.xml&quot;</span>] <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb27-2" title="2"> route idRoute</a>
<a class="sourceLine" id="cb27-3" title="3"> compile <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb27-4" title="4"> <span class="co">-- load all &quot;content&quot; snapshots of all posts</span></a>
<a class="sourceLine" id="cb27-5" title="5"> loadAllSnapshots <span class="st">&quot;posts/*&quot;</span> <span class="st">&quot;content&quot;</span></a>
<a class="sourceLine" id="cb27-6" title="6"> <span class="co">-- take the latest 10</span></a>
<a class="sourceLine" id="cb27-7" title="7"> <span class="op">&gt;&gt;=</span> (<span class="fu">fmap</span> (<span class="fu">take</span> <span class="dv">10</span>)) <span class="op">.</span> createdFirst</a>
<a class="sourceLine" id="cb27-8" title="8"> <span class="co">-- renderAntom feed using some configuration</span></a>
<a class="sourceLine" id="cb27-9" title="9"> <span class="op">&gt;&gt;=</span> renderAtom feedConfiguration feedCtx</a>
<a class="sourceLine" id="cb27-10" title="10"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb27-11" title="11"><span class="ot"> feedCtx ::</span> <span class="dt">Context</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb27-12" title="12"> feedCtx <span class="ot">=</span> defaultContext <span class="op">&lt;&gt;</span></a>
<a class="sourceLine" id="cb27-13" title="13"> <span class="co">-- $description$ will render as the post body</span></a>
<a class="sourceLine" id="cb27-14" title="14"> <span class="highlight">bodyField <span class="st">&quot;description&quot;</span></span></a></code></pre></div>
<p>The <code>feedConfiguration</code> contains some general informations about the feed.</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb28-1" title="1"><span class="ot">feedConfiguration ::</span> <span class="dt">FeedConfiguration</span></a>
<a class="sourceLine" id="cb28-2" title="2">feedConfiguration <span class="ot">=</span> <span class="dt">FeedConfiguration</span></a>
<a class="sourceLine" id="cb28-3" title="3"> { feedTitle <span class="ot">=</span> <span class="st">&quot;Great Old Ones&quot;</span></a>
<a class="sourceLine" id="cb28-4" title="4"> , feedDescription <span class="ot">=</span> <span class="st">&quot;This feed provide information about Great Old Ones&quot;</span></a>
<a class="sourceLine" id="cb28-5" title="5"> , feedAuthorName <span class="ot">=</span> <span class="st">&quot;Abdul Alhazred&quot;</span></a>
<a class="sourceLine" id="cb28-6" title="6"> , feedAuthorEmail <span class="ot">=</span> <span class="st">&quot;abdul.alhazred@great-old-ones.com&quot;</span></a>
<a class="sourceLine" id="cb28-7" title="7"> , feedRoot <span class="ot">=</span> <span class="st">&quot;http://great-old-ones.com&quot;</span></a>
<a class="sourceLine" id="cb28-8" title="8"> }</a></code></pre></div>
<p>Great idea certainly steal from <a href="http://nanoc.ws">nanoc</a> (my previous blog engine)!</p>
<h3 id="filter-the-content">Filter the content</h3>
<p>As I just said, <a href="http://nanoc.ws">nanoc</a> was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a <em>lot</em> of filters in nanoc. I lost some because I dont use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type <code>String -&gt; String</code>.</p>
<p>Also we generally want prefilters (to filter the markdown) and postfilters (to filter the <span class="sc"><abbr title="HyperText Markup Language">html</abbr></span> after the pandoc compilation).</p>
<p>Here is how I do it:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb29-1" title="1">markdownPostBehavior <span class="ot">=</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb29-2" title="2"> route <span class="op">$</span> niceRoute</a>
<a class="sourceLine" id="cb29-3" title="3"> compile <span class="op">$</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb29-4" title="4"> body <span class="ot">&lt;-</span> getResourceBody</a>
<a class="sourceLine" id="cb29-5" title="5"> <span class="highlight">prefilteredText <span class="ot">&lt;-</span> <span class="fu">return</span> <span class="op">$</span> (<span class="fu">fmap</span> preFilters body)</span></a>
<a class="sourceLine" id="cb29-6" title="6"> <span class="highlight"><span class="fu">return</span> <span class="op">$</span> renderPandoc prefilteredText</span></a>
<a class="sourceLine" id="cb29-7" title="7"> <span class="highlight"><span class="op">&gt;&gt;=</span> applyFilter postFilters</span></a>
<a class="sourceLine" id="cb29-8" title="8"> <span class="op">&gt;&gt;=</span> saveSnapshot <span class="st">&quot;content&quot;</span></a>
<a class="sourceLine" id="cb29-9" title="9"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/post.html&quot;</span> yContext</a>
<a class="sourceLine" id="cb29-10" title="10"> <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/boilerplate.html&quot;</span> yContext</a>
<a class="sourceLine" id="cb29-11" title="11"> <span class="op">&gt;&gt;=</span> relativizeUrls</a>
<a class="sourceLine" id="cb29-12" title="12"> <span class="op">&gt;&gt;=</span> removeIndexHtml</a></code></pre></div>
<p>Where</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb30-1" title="1">applyFilter strfilter str <span class="ot">=</span> <span class="fu">return</span> <span class="op">$</span> (<span class="fu">fmap</span> <span class="op">$</span> strfilter) str</a>
<a class="sourceLine" id="cb30-2" title="2"><span class="ot">preFilters ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb30-3" title="3"><span class="ot">postFilters ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">String</span></a></code></pre></div>
<p>Now we have a simple way to filter the content. Lets augment the markdown ability.</p>
<h3 id="add-abbreviations-support">Add abbreviations support</h3>
<p>Comparing to <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>, a very annoying markdown limitation is the lack of abbreviations.</p>
<p>Fortunately we can filter our content. And here is the filter I use:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb31-1" title="1"><span class="ot">abbreviationFilter ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb31-2" title="2">abbreviationFilter <span class="ot">=</span> replaceAll <span class="st">&quot;%[a-zA-Z0-9_]*&quot;</span> newnaming</a>
<a class="sourceLine" id="cb31-3" title="3"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb31-4" title="4"> newnaming matched <span class="ot">=</span> <span class="kw">case</span> M.lookup (<span class="fu">tail</span> matched) abbreviations <span class="kw">of</span></a>
<a class="sourceLine" id="cb31-5" title="5"> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> matched</a>
<a class="sourceLine" id="cb31-6" title="6"> <span class="dt">Just</span> v <span class="ot">-&gt;</span> v</a>
<a class="sourceLine" id="cb31-7" title="7"><span class="ot">abbreviations ::</span> <span class="dt">Map</span> <span class="dt">String</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb31-8" title="8">abbreviations <span class="ot">=</span> M.fromList</a>
<a class="sourceLine" id="cb31-9" title="9"> [ (<span class="st">&quot;html&quot;</span>, <span class="st">&quot;&lt;span class=\&quot;sc\&quot;&gt;html&lt;/span&gt;&quot;</span>)</a>
<a class="sourceLine" id="cb31-10" title="10"> , (<span class="st">&quot;css&quot;</span>, <span class="st">&quot;&lt;span class=\&quot;sc\&quot;&gt;css&lt;/span&gt;&quot;</span>)</a>
<a class="sourceLine" id="cb31-11" title="11"> , (<span class="st">&quot;svg&quot;</span>, <span class="st">&quot;&lt;span class=\&quot;sc\&quot;&gt;svg&lt;/span&gt;&quot;</span>)</a>
<a class="sourceLine" id="cb31-12" title="12"> , (<span class="st">&quot;xml&quot;</span>, <span class="st">&quot;&lt;span class=\&quot;sc\&quot;&gt;xml&lt;/span&gt;&quot;</span>)</a>
<a class="sourceLine" id="cb31-13" title="13"> , (<span class="st">&quot;xslt&quot;</span>, <span class="st">&quot;&lt;span class=\&quot;sc\&quot;&gt;xslt&lt;/span&gt;&quot;</span>) ]</a></code></pre></div>
<p>It will search for all string starting by % and it will search in the <code>Map</code> if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.</p>
<p>Do you really believe I type</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode html wrap"><code class="sourceCode html"><a class="sourceLine" id="cb32-1" title="1"><span class="kw">&lt;span</span><span class="ot"> style=</span><span class="st">&quot;text-transform: uppercase&quot;</span><span class="kw">&gt;</span>L<span class="kw">&lt;sup</span><span class="ot"> style=</span><span class="st">&quot;vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em&quot;</span><span class="kw">&gt;</span>a<span class="kw">&lt;/sup&gt;</span>T<span class="kw">&lt;sub</span><span class="ot"> style=</span><span class="st">&quot;vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em&quot;</span><span class="kw">&gt;</span>e<span class="kw">&lt;/sub&gt;</span>X<span class="kw">&lt;/span&gt;</span></a></code></pre></div>
<p>each time I write <span style="text-transform: uppercase">L<sup style="vertical-align: 0.15em; margin-left: -0.36em; margin-right: -0.15em; font-size: .85em">a</sup>T<sub style="vertical-align: -0.5ex; margin-left: -0.1667em; margin-right: -0.125em; font-size: 1em">e</sub>X</span>?</p>
<h3 id="manage-two-languages">Manage two languages</h3>
<p>Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb33-1" title="1"><span class="kw">&lt;a</span><span class="ot"> href=</span><span class="st">&quot;$otherLanguagePath$&quot;</span></a>
<a class="sourceLine" id="cb33-2" title="2"><span class="ot"> onclick=</span><span class="st">&quot;setLanguage('$otherlanguage$')&quot;</span><span class="kw">&gt;</span></a>
<a class="sourceLine" id="cb33-3" title="3"> <span class="highlight">$changeLanguage$</span> <span class="kw">&lt;/a&gt;</span></a></code></pre></div>
<p>First I create a Map containing all translations.</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb34-1" title="1"><span class="kw">data</span> <span class="dt">Trad</span> <span class="ot">=</span> <span class="dt">Trad</span> {<span class="ot"> frTrad ::</span> <span class="dt">String</span>,<span class="ot"> enTrad ::</span> <span class="dt">String</span> }</a>
<a class="sourceLine" id="cb34-2" title="2"></a>
<a class="sourceLine" id="cb34-3" title="3"><span class="ot">trads ::</span> <span class="dt">Map</span> <span class="dt">String</span> <span class="dt">Trad</span></a>
<a class="sourceLine" id="cb34-4" title="4">trads <span class="ot">=</span> M.fromList <span class="op">$</span> <span class="fu">map</span> toTrad [</a>
<a class="sourceLine" id="cb34-5" title="5"> (<span class="st">&quot;changeLanguage&quot;</span>,</a>
<a class="sourceLine" id="cb34-6" title="6"> (<span class="st">&quot;English&quot;</span></a>
<a class="sourceLine" id="cb34-7" title="7"> , <span class="st">&quot;Français&quot;</span>))</a>
<a class="sourceLine" id="cb34-8" title="8"> ,(<span class="st">&quot;switchCss&quot;</span>,</a>
<a class="sourceLine" id="cb34-9" title="9"> (<span class="st">&quot;Changer de theme&quot;</span></a>
<a class="sourceLine" id="cb34-10" title="10"> ,<span class="st">&quot;Change Theme&quot;</span>))</a>
<a class="sourceLine" id="cb34-11" title="11"> ,(<span class="st">&quot;socialPrivacy&quot;</span>,</a>
<a class="sourceLine" id="cb34-12" title="12"> (<span class="st">&quot;Ces liens sociaux préservent votre vie privée&quot;</span></a>
<a class="sourceLine" id="cb34-13" title="13"> ,<span class="st">&quot;These social sharing links preserve your privacy&quot;</span>))</a>
<a class="sourceLine" id="cb34-14" title="14"> ]</a>
<a class="sourceLine" id="cb34-15" title="15"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb34-16" title="16"> toTrad (key,(french,english)) <span class="ot">=</span></a>
<a class="sourceLine" id="cb34-17" title="17"> (key, <span class="dt">Trad</span> { frTrad <span class="ot">=</span> french , enTrad <span class="ot">=</span> english })</a></code></pre></div>
<p>Then I create a context for all key:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb35-1" title="1"><span class="ot">tradsContext ::</span> <span class="dt">Context</span> a</a>
<a class="sourceLine" id="cb35-2" title="2">tradsContext <span class="ot">=</span> <span class="fu">mconcat</span> (<span class="fu">map</span> addTrad (M.keys trads))</a>
<a class="sourceLine" id="cb35-3" title="3"> <span class="kw">where</span></a>
<a class="sourceLine" id="cb35-4" title="4"><span class="ot"> addTrad ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Context</span> a</a>
<a class="sourceLine" id="cb35-5" title="5"> addTrad name <span class="ot">=</span></a>
<a class="sourceLine" id="cb35-6" title="6"> field name <span class="op">$</span> \item <span class="ot">-&gt;</span> <span class="kw">do</span></a>
<a class="sourceLine" id="cb35-7" title="7"> lang <span class="ot">&lt;-</span> itemLang item</a>
<a class="sourceLine" id="cb35-8" title="8"> <span class="kw">case</span> M.lookup name trads <span class="kw">of</span></a>
<a class="sourceLine" id="cb35-9" title="9"> <span class="dt">Just</span> (<span class="dt">Trad</span> lmap) <span class="ot">-&gt;</span> <span class="kw">case</span> M.lookup (<span class="dt">L</span> lang) lmap <span class="kw">of</span></a>
<a class="sourceLine" id="cb35-10" title="10"> <span class="dt">Just</span> tr <span class="ot">-&gt;</span> <span class="fu">return</span> tr</a>
<a class="sourceLine" id="cb35-11" title="11"> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="fu">return</span> (<span class="st">&quot;NO TRANSLATION FOR &quot;</span> <span class="op">++</span> name)</a>
<a class="sourceLine" id="cb35-12" title="12"> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="fu">return</span> (<span class="st">&quot;NO TRANSLATION FOR &quot;</span> <span class="op">++</span> name)</a></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>The full code is <a href="http://github.com/yogsototh/yblog.git">here</a>. And except from the main file, I use literate Haskell. This way the code should be easier to understand.</p>
<p>If you want to know why I switched from nanoc:</p>
<p>My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.</p>
<p>So I had to do something about it. I had two choices:</p>
<ol type="1">
<li>Correct my old code (in Ruby)</li>
<li>Duplicate the core functionalities with Hakyll (in Haskell)</li>
</ol>
<p>I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.</p>
<p>So far I am very happy with the switch. A complete build is about 4x faster. I didnt broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.</p>
<p>I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:</p>
<p>In nanoc I could take a file like this as source:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb36-1" title="1"><span class="fu"># Title</span></a>
<a class="sourceLine" id="cb36-2" title="2"></a>
<a class="sourceLine" id="cb36-3" title="3">content</a>
<a class="sourceLine" id="cb36-4" title="4"></a>
<a class="sourceLine" id="cb36-5" title="5">&lt;code file=&quot;foo.hs&quot;&gt;</a>
<a class="sourceLine" id="cb36-6" title="6">main = putStrLn &quot;Cthulhu!&quot;</a>
<a class="sourceLine" id="cb36-7" title="7">&lt;/code&gt;</a></code></pre></div>
<p>And it will create a file <code>foo.hs</code> which could then be downloaded.</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb37-1" title="1"><span class="kw">&lt;h1&gt;</span>Title<span class="kw">&lt;/h1&gt;</span></a>
<a class="sourceLine" id="cb37-2" title="2"></a>
<a class="sourceLine" id="cb37-3" title="3"><span class="kw">&lt;p&gt;</span>content<span class="kw">&lt;/p&gt;</span></a>
<a class="sourceLine" id="cb37-4" title="4"></a>
<a class="sourceLine" id="cb37-5" title="5"><span class="kw">&lt;a</span><span class="ot"> href=</span><span class="st">&quot;code/foo.hs&quot;</span><span class="kw">&gt;</span>Download foo.hs<span class="kw">&lt;/a&gt;</span></a>
<a class="sourceLine" id="cb37-6" title="6"><span class="kw">&lt;pre&gt;&lt;code&gt;</span>main = putStrLn &quot;Cthulhu!&quot;<span class="kw">&lt;/code&gt;&lt;/pre&gt;</span></a></code></pre></div>
<section class="footnotes">
<hr />
<ol>
<li id="fn1"><p>We could also add the metadatas in an external file (<code>foo.md.metadata</code>).<a href="#fnref1" class="footnote-back"></a></p></li>
</ol>
</section>
</div>
<div id="afterarticle">
<div id="social">
<a href="/rss.xml" target="_blank" rel="noopener noreferrer nofollow" class="social">RSS</a>
·
<a href="https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Hakyll-setup/%20via%20@yogsototh" target="_blank" rel="noopener noreferrer nofollow" class="social">Tweet</a>
·
<a href="http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Hakyll-setup/" target="_blank" rel="noopener noreferrer nofollow" class="social">FB</a>
<br />
<a class="message" href="../../../../Scratch/en/blog/Social-link-the-right-way/">These social sharing links preserve your privacy</a>
</div>
<div id="navigation">
<a href="../../../../">Home</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/blog">Blog</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/softwares">Softwares</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/about">About</a>
</div>
<div id="totop"><a href="#header">↑ Top ↑</a></div>
<div id="bottom">
<div>
Published on 2013-03-16
</div>
<div>
<a href="https://twitter.com/yogsototh">Follow @yogsototh</a>
</div>
<div>
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US">Yann Esposito©</a>
</div>
<div>
Done with
<a href="http://www.vim.org" target="_blank" rel="noopener noreferrer nofollow"><strike>Vim</strike></a>
<a href="http://spacemacs.org" target="_blank" rel="noopener noreferrer nofollow">spacemacs</a>
<span class="pala">&amp;</span>
<a href="http://nanoc.ws" target="_blank" rel="noopener noreferrer nofollow"><strike>nanoc</strike></a>
<a href="http://jaspervdj.be/hakyll" target="_blank" rel="noopener noreferrer nofollow">Hakyll</a>
</div>
<hr />
<div style="max-width: 100%">
<a href="https://cardanohub.org">
<img src="../../../../Scratch/img/ada-logo.png" class="simple" style="height: 16px;
border-radius: 50%;
vertical-align:middle;
display:inline-block;" />
ADA:
</a>
<code style="display:inline-block;
word-wrap:break-word;
text-align: left;
vertical-align: top;
max-width: 85%;">
DdzFFzCqrhtAvdkmATx5Fm8NPJViDy85ZBw13p4XcNzVzvQg8e3vWLXq23JQWFxPEXK6Kvhaxxe7oJt4VMYHxpA2vtCFiP8fziohN6Yp
</code>
</div>
</div>
</div>
</div>
</div>
</body>
</html>