<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Fabien Lasserre – Développeur web full stack</title>
    <subtitle></subtitle>
    <link href="https://www.fabienlasserre.dev/fr/blog/feed.xml" rel="self" />
    <link href="https://www.fabienlasserre.dev/" />
    
        <updated>2025-04-29T00:00:00Z</updated>
    
    <id>https://www.fabienlasserre.dev</id>
    <author>
        <name></name>
        <email></email>
    </author>
    
        
        <entry>
            <title>Une stack Symfony &amp; Vue.js conteneurisée avec DDEV</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/une-stack-symfony-and-vue-js-conteneurisee-avec-ddev/" />
            <updated>2025-04-29T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/une-stack-symfony-and-vue-js-conteneurisee-avec-ddev/</id>
            <content type="html">
                <![CDATA[
                <p>Depuis un peu plus d'un an maintenant, j'utilise <a href="https://ddev.com/">DDEV</a> dès que j'ai besoin de mettre en place un environnement de développement. Grâce à un simple fichier <code>.yaml</code>, cet outil très pratique est capable de fournir un espace de travail en conteneur avec PHP, une base de données MariaDB/PostreSQL, et Mailpit. C'est parfait pour des projets de toute taille, du simple site vitrine WordPress à l'application Symfony la plus complexe. Cependant, il n'est pas vraiment conçu pour être utilisé avec Node par défaut. Tout du moins, même s'il embarque Node de base, si vous souhaitez utiliser un backend PHP avec un frontend en TypeScript, cela demande un peu de configuration, comme nous allons le voir dans cet article. Il est inspiré de celui d'Andy Blum sur le blog de <a href="https://www.lullabot.com/articles/nodejs-development-ddev">Lullabot</a>. Mon approche est similaire, mais simplifiée.</p>
<hr>
<p>Ma stack web de prédilection est Symfony en mode API (soit en RESTful ou avec GraphQL) et un frontend qui utilise Vue.js. J'utilisais auparavant <a href="https://github.com/nvm-sh/nvm">NVM</a> ou <a href="https://volta.sh/">Volta</a> avec une version de Node installée localement, mais je désirais quelque chose de totalement portable - quelque chose où je peux simplement cloner un dépôt, lancer <code>ddev start</code> et me mettre au travail. Pour cela, nous allons suivre les étapes suivantes :</p>
<ul>
<li>Mettre en place le conteneur PHP et Symfony</li>
<li>Geler la version de Node et installer Vue.js</li>
<li>Ajouter un reverse proxy pour accéder au frontend</li>
<li>Améliorer la DX grâce à un Makefile</li>
</ul>
<p>Pour ce projet, nous allons utiliser un domaine fictif - disons <code>tinydev.ddev.site</code> - et avoir l'API sur <code>api.tinydev.ddev.site</code> et l'app web sur <code>app.tinydev.ddev.site</code>. Je pars du principe que vous avez déjà Docker et <a href="https://ddev.com/get-started/">DDEV installé</a> et prêt à l'emploi.</p>
<p>Pour que les choses soient bien rangées, nous allons utiliser la structure de dossier suivante :</p>
<pre class="language-conf"><code class="language-conf">/.ddev
 - config.yaml
/backend
 - Le projet Symfony...
/webapp
 - Le projet Vue.js...</code></pre>
<p>Le dossier <code>.ddev</code> va contenir le fichier de configuration de DDEV (<code>config.yaml</code>), l'API Symfony sera dans <code>/backend</code> et l'app front sera dans <code>/webapp</code>.</p>
<h2>Mise en place de l'app Symfony</h2>
<p>Commençons par initialiser le projet grâce à <code>ddev config</code>, en prenant soin de sélectionner <code>symfony</code> comme type d'application. Le docroot pour notre projet sera <code>backend/public</code>.</p>
<p>Une fois configuré, ouvrez <code>/.ddev/config.yaml</code> et ajoutez les hosts additionels (<code>api.tinydev</code> et <code>app.tinydev</code>). Le fichier de configuration devrait ressembler à ceci :</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">name</span><span class="token punctuation">:</span> tinydev
<span class="token key atrule">type</span><span class="token punctuation">:</span> symfony
<span class="token key atrule">docroot</span><span class="token punctuation">:</span> backend/public
<span class="token key atrule">php_version</span><span class="token punctuation">:</span> <span class="token string">'8.3'</span>
<span class="token key atrule">webserver_type</span><span class="token punctuation">:</span> nginx<span class="token punctuation">-</span>fpm
<span class="token key atrule">xdebug_enabled</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>
<span class="token key atrule">additional_hostnames</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'api.tinydev'</span><span class="token punctuation">,</span> <span class="token string">'app.tinydev'</span><span class="token punctuation">]</span>
<span class="token key atrule">additional_fqdns</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token key atrule">database</span><span class="token punctuation">:</span>
  <span class="token key atrule">type</span><span class="token punctuation">:</span> mariadb
  <span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">'10.11'</span>
<span class="token key atrule">use_dns_when_possible</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
<span class="token key atrule">composer_version</span><span class="token punctuation">:</span> <span class="token string">'2'</span>
<span class="token key atrule">web_environment</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token key atrule">corepack_enable</span><span class="token punctuation">:</span> <span class="token boolean important">false</span></code></pre>
<p>Démarrez DDEV avec <code>ddev start</code>, et entrez dans le conteneur grâce à <code>ddev ssh</code>. Nous allons <a href="https://symfony.com/doc/current/setup.html">installer Symfony</a> dans le dossier<code>backend</code> (au moment où j'écris ces lignes la dernière version de Symfony est la 7.2). Nous allons devoir effacer le dossier <code>backend</code> pour que Composer puisse l'initialiser correctement.</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> backend <span class="token operator">&amp;&amp;</span> <span class="token function">composer</span> create-project symfony/skeleton:<span class="token string">"7.2.x"</span> backend</code></pre>
<p>Une fois installé, créez un contrôleur simple pour vérifier que tout fonctionne correctement :</p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span>
<span class="token comment">// /backend/src/Controller/HomeController.php</span>

<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Bundle<span class="token punctuation">\</span>FrameworkBundle<span class="token punctuation">\</span>Controller<span class="token punctuation">\</span>AbstractController</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>HttpFoundation<span class="token punctuation">\</span>Response</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>Routing<span class="token punctuation">\</span>Attribute<span class="token punctuation">\</span>Route</span><span class="token punctuation">;</span>

<span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">HomeController</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractController</span>
<span class="token punctuation">{</span>
    <span class="token attribute"><span class="token delimiter punctuation">#[</span><span class="token attribute-content"><span class="token attribute-class-name class-name">Route</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/'</span><span class="token punctuation">)</span></span><span class="token delimiter punctuation">]</span></span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Response</span><span class="token punctuation">(</span><span class="token string double-quoted-string">"Hello, World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</span></code></pre>
<p>Vous pouvez naviguer à l'adresse <code>https://tinydev.ddev.site</code> et vous devriez voir le message « Hello, World! ». Si ce n'est pas le cas, n'hésitez pas à redémarrer DDEV avec <code>ddev restart</code>.</p>
<p>À ce moment, <code>https://api.tinydev.ddev.site</code> pointe toujours vers l'application Symfony. Nous allons régler cela tout de suite.</p>
<h2>Geler la version de Node et installer Vue.js</h2>
<p>Par défaut, DDEV embarque Node v22. Nous allons geler cette version pour éviter les mauvaises surprises pendant la phase de développement. Cela peut être fait très facilement dans <code>/.ddev/config.yaml</code></p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">nodejs_version</span><span class="token punctuation">:</span> <span class="token string">'22'</span></code></pre>
<p>Redémarrez DDEV (<code>ddev restart</code>) et entrez dans le conteneur avec <code>ddev ssh</code>. Nous allons maintenant installer Vue.js dans le dossier <code>webapp</code> :</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token function">npm</span> create vue@latest</code></pre>
<p>Choisissez les options qui vous conviennent, mais spécifiez <code>webapp</code> comme nom de projet, car c'est ce nom qui sera utilisé comme dossier de destination pour les fichiers de l'app Vue.js. Vous pouvez ensuite installer l'application elle-même :</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token builtin class-name">cd</span> webapp <span class="token operator">&amp;&amp;</span> <span class="token function">npm</span> <span class="token function">install</span></code></pre>
<p>Naviguons maintenant vers <code>https://app.tinydev.ddev.site</code> pour voir... rien ? C'est normal. L'app est installée, mais elle n'est pas encore accessible.</p>
<h2>Ajouter un reverse proxy pour accéder au frontend</h2>
<p>Pour solutioner ce problème, nous allons ajouter un fichier de configuration nginx dans <code>/.ddev/nginx_full/app.tinydev.conf</code>:</p>
<pre class="language-conf"><code class="language-conf">server {

    server_name app.tinydev.ddev.site;

    location / {
        proxy_pass http://localhost:5173;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    listen 80;
    listen 443 ssl;

    ssl_certificate /etc/ssl/certs/master.crt;
    ssl_certificate_key /etc/ssl/certs/master.key;

    include /etc/nginx/monitoring.conf;

    error_log /dev/stdout info;
    access_log /var/log/nginx/access.log;

    include /etc/nginx/common.d/*.conf;
    include /mnt/ddev_config/nginx/*.conf;
}</code></pre>
<p>Nous devons aussi éditer <code>vite.config.ts</code> pour autoriser ce nouvel hôte :</p>
<pre class="language-ts"><code class="language-ts"><span class="token comment">// /webapp/vite.config.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> fileURLToPath<span class="token punctuation">,</span> <span class="token constant">URL</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'node:url'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> defineConfig <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vite'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> vue <span class="token keyword">from</span> <span class="token string">'@vitejs/plugin-vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> vueDevTools <span class="token keyword">from</span> <span class="token string">'vite-plugin-vue-devtools'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">defineConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  server<span class="token operator">:</span> <span class="token punctuation">{</span>
    allowedHosts<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'app.tinydev.ddev.site'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token function">vue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">vueDevTools</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  resolve<span class="token operator">:</span> <span class="token punctuation">{</span>
    alias<span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token string-property property">'@'</span><span class="token operator">:</span> <span class="token function">fileURLToPath</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name"><span class="token constant">URL</span></span><span class="token punctuation">(</span><span class="token string">'./src'</span><span class="token punctuation">,</span> <span class="token keyword">import</span><span class="token punctuation">.</span>meta<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Redémarrez DDEV à nouveau (<code>ddev restart</code>), allez dans le conteneur (<code>ddev ssh</code>), et lancez <code>npm run dev</code> dans le dossier <code>/webapp</code>. N'oubliez pas d'exécuter <code>npm install</code> si cela n'est pas déjà fait.</p>
<p>Si vous naviguez désormais sur <code>https://app.tinydev.ddev.site</code>, vous devriez voir la page de présentation par défaut d'un nouveau projet Vite.</p>
<p>Vous avez désormais un projet utilisant Symfony et Vue.js, et entièrement conteneurisé grâce à DDEV ! Félicitations ! :tada:</p>
<p>Vous pouvez maintenant accéder à votre web app sur <code>https://app.tinydev.ddev.site</code>, en effectuant des appels à votre API qui se trouve sur <code>https://api.tinydev.ddev.site</code>.</p>
<p>Finissons ce tutoriel en ajoutant un peu de magie pour améliorer notre DX grâce à un simple fichier Makefile.</p>
<h2>Améliorer la DX grâce à un Makefile</h2>
<p>Devoir entrer dans le conteneur à chaque fois que nous voulons lancer le serveur web Vite peut rapidement devenir contraignant. De même, il serait bien plus pratique de ne pas avoir à le faire dès que nous voulons lancer des commandes symfony comme lancer une migration ou vider le cache. Heureusement, nous pouvons nous faciliter la tâche avec un Makefile et des raccourcis très pratiques :</p>
<p><strong>Important :</strong> Pensez à utiliser des tabs (et non des espaces) pour l'indentation de votre fichier !</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token comment"># /Makefile</span>

WEBAPP_FOLDER<span class="token operator">=</span>/var/www/html/webapp
API_FOLDER<span class="token operator">=</span>/var/www/html/backend

<span class="token target symbol">start</span><span class="token punctuation">:</span>
	ddev start

<span class="token target symbol">stop</span><span class="token punctuation">:</span>
	ddev stop

<span class="token target symbol">up</span><span class="token punctuation">:</span> start
	ddev exec --dir <span class="token variable">$</span><span class="token punctuation">(</span>WEBAPP_FOLDER<span class="token punctuation">)</span> npm run dev

<span class="token target symbol">cache-clear</span><span class="token punctuation">:</span>
<span class="token target symbol">	ddev exec --dir <span class="token variable">$</span>(API_FOLDER) php bin/console cache</span><span class="token punctuation">:</span>clear

<span class="token target symbol">cc</span><span class="token punctuation">:</span> cache-clear

<span class="token target symbol">migration</span><span class="token punctuation">:</span>
<span class="token target symbol">    ddev exec --dir <span class="token variable">$</span>(API_FOLDER) php bin/console make</span><span class="token punctuation">:</span>migration</code></pre>
<p>Gardez à l'esprit que vous devrez tout de même utiliser <code>ddev ssh</code> pour exécuter des commandes Composer, car <code>ddev composer</code> s'exécute à la racine du projet par défaut.</p>
<p>Et voilà ! Un environnement de développement simple et portable que vous pouvez utiliser n'importe où et partager avec d'autres.</p>
<p>Happy coding!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Maîtriser Eleventy - Partie 2 : i18n et assets</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/maitriser-eleventy-partie-2-i18n-et-assets/" />
            <updated>2024-04-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/maitriser-eleventy-partie-2-i18n-et-assets/</id>
            <content type="html">
                <![CDATA[
                <p>Dans <a href="/fr/blog/maitriser-eleventy-partie-1-la-structure-de-projet/">la première partie</a> de cette série d’articles consacrés à Eleventy, nous avions vu quelle structure de dossiers j’utilise pour bénéficier d’un environnement de travail sain et organisé.</p>
<p>Ici, nous allons voir comment utiliser Eleventy pour avoir un site multilingue, et gérer ses assets JavaScript et CSS à l’aide du processus de build.</p>
<h2>Compiler ses assets JS/SCSS</h2>
<p>Commençons donc par la compilation du JavaScript et de la CSS. Pour cela, et comme nous l’avons vu dans la partie précédente, je stocke ces fichiers dans le dossier <code>/src/_assets</code> de mon projet. Laissés tels quels, les fichiers ne sont pas considérés par Eleventy, et se retrouvent tout simplement ignorés pendant le build. Pour qu’ils soient pris en compte lors du processus de build du site, je crée donc un fichier spécifique avec l’extension <code>.11ty.js</code>.</p>
<h3>JavaScript</h3>
<p>Pour JavaScript, nous allons avoir besoin de la dépendance <code>esbuild</code> :</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">--save</span> esbuild</code></pre>
<p>Nous créons ensuite le fichier <code>/src/scripts.11ty.js</code> avec le code suivant :</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /src/scripts.11ty.js</span>
<span class="token keyword">const</span> esbuild <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'esbuild'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token constant">NODE_ENV</span> <span class="token operator">=</span> <span class="token string">'production'</span> <span class="token punctuation">}</span> <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">;</span>

<span class="token keyword">const</span> isProduction <span class="token operator">=</span> <span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'production'</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token keyword">class</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">eleventyExcludeFromCollections</span><span class="token operator">:</span> <span class="token boolean">true</span>
	<span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">async</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">await</span> esbuild<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  	<span class="token literal-property property">entryPoints</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'src/_assets/js/main.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">bundle</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">minify</span><span class="token operator">:</span> isProduction<span class="token punctuation">,</span>
  	<span class="token literal-property property">outdir</span><span class="token operator">:</span> <span class="token string">'dist/js'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">sourcemap</span><span class="token operator">:</span> <span class="token operator">!</span>isProduction<span class="token punctuation">,</span>
  	<span class="token literal-property property">target</span><span class="token operator">:</span> isProduction <span class="token operator">?</span> <span class="token string">'es6'</span> <span class="token operator">:</span> <span class="token string">'esnext'</span>
	<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>On peut voir ici l’une des grandes forces d’Eleventy : ce fichier va être attrapé à la volée et compilé de manière à prendre notre fichier source <code>/src/_assets/js/main.js</code> et en faire le fichier <code>/dist/js/main.js</code>, qui va non seulement embarquer les dépendances de notre projet mais sera aussi minifié.</p>
<h3>Feuilles de style</h3>
<p>Le principe est le même pour nos assets CSS. Pour cela nous allons avoir besoin de la dépendance <code>sass</code> :</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">--save</span> sass</code></pre>
<p>Le fichier d’entrée sera <code>/src/styles.11ty.js</code> avec le contenu suivant :</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> sass <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'sass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token keyword">class</span> <span class="token class-name">SassTemplate</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token string">'/css/main.min.css'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> sass<span class="token punctuation">.</span><span class="token function">compile</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'./_assets/scss/main.scss'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'compressed'</span>
	<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>css<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Là encore, c’est grâce à l'extension <code>.11ty.js</code> que le fichier est pris à la volée lors du build, et le point d’entrée est <code>/src/_assets/scss/main.scss</code>. Le fichier final généré va ainsi embarquer tous les partials et mixins définis dans notre source et finira sa course dans <code>/dist/css/main.min.css</code>, compilé et minifié, prêt à être utilisé dans nos templates.</p>
<h2>Internationalisation (i18n)</h2>
<p>Les assets JavaScript et CSS étant pris en compte, nous pouvons maintenant passer à l’internationalisation de notre site internet. Notons ici que je décris la méthode que j’ai décidé d’appliquer, mais elle n’est peut-être pas la plus pertinente ni la plus optimisée pour vous. Grâce à sa souplesse, Eleventy permet d’expérimenter et d’utiliser le système que l’on désire. Celui que j’ai décidé d’utiliser possède plusieurs avantages :</p>
<ul>
<li>La <strong>séparation du contexte</strong> : les langues possèdent leur propre dossier.</li>
<li>La <strong>flexibilité</strong> : il est possible d’ajouter des langues assez simplement.</li>
<li>La possibilité de <strong>lier des pages</strong> entre elles afin de créer un « pont » entre les langues.</li>
</ul>
<p>La première chose à faire est donc de séparer les langues dans différents dossiers. Ainsi, nous aurons le contenu du site en Français dans le dossier <code>/fr/</code> et la version anglaise sera dans <code>/en/</code>. Cela permet ainsi une séparation du contexte, mais aussi de pouvoir faire une liaison (ou non) entre deux pages équivalentes.</p>
<h3>Gérer le menu principal</h3>
<p>Le problème principal qui se pose est la gestion de la navigation principale : étant donné que notre site repose sur un système de layouts, il serait malvenu de décomposer ces derniers sur plusieurs langues. Encore une fois, ce serait possible, Eleventy nous permet ceci, mais la mutualisation des templates entre les différentes langues est préférée. Pour tout ce qui ne ressort pas du contenu rédactionnel, nous allons donc utiliser notre « base de données » qui réside dans <code>/_data/site.json</code> : il s’agit là d’un simple fichier JSON que nous pouvons utiliser comme bon nous semble. Je regroupe donc toutes les entrées du menu principal sous la clé headerLinks comme suit :</p>
<pre class="language-json"><code class="language-json"><span class="token comment">// /src/_data/site.json</span>
<span class="token punctuation">{</span>
  <span class="token property">"headerLinks"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"home"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"about"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/about/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/a-propos/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"projects"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/projects/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/projets/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"blog"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/blog/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/blog/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"contact"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/contact/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/contact/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Sous celle-ci, nous trouvons donc un tableau d’objets avec les clés suivantes:</p>
<ul>
<li><code>translationKey</code> est une valeur unique donnée à chacune des pages. C’est sur cette clé que tout repose.</li>
<li><code>url</code> spécifie, pour chacune des langues, la destination du lien.</li>
<li><code>external</code> est un booléen qui permet de spécifier si le lien doit s’ouvrir dans un nouvel onglet ou non.</li>
</ul>
<p>Ceci nous permet donc de pouvoir générer le menu principal du site :</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">item</span> <span class="token keyword">in</span> <span class="token variable">site</span><span class="token punctuation">.</span><span class="token variable">headerLinks</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>navbar-item <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">if</span> <span class="token punctuation">(</span><span class="token variable">page</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token operator">==</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">locale</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token keyword">and</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token string">"home"</span><span class="token punctuation">)</span> <span class="token keyword">or</span> <span class="token variable">page</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token operator">==</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">locale</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">url</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token keyword">or</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token variable">category</span> <span class="token delimiter punctuation">-%}</span></span>is-active<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span><span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">url</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">i18n</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<p>Dans cette boucle, nous vérifions si la page actuelle est :</p>
<ul>
<li>La home <strong>ou</strong></li>
<li>Une page faisant partie d’une catégorie donnée <strong>ou</strong></li>
<li>Une page de catégorie</li>
</ul>
<p>Ceci nous permet de spécifier une classe CSS <code>is-active</code>, qui démarque l’entrée actuellement active du menu par une bordure de couleur.</p>
<p>Comme vous pouvez le constater, nous nous reposons sur cette fameuse <code>translationKey</code> dans une autre partie du lien du menu :</p>
<p><code>{{ i18n[locale][item.translationKey] }}</code></p>
<p>Ceci fait appel à un autre fichier de « base de données », situé dans <code>/src/_data/i18n.json</code>. Ce fichier constitue le « dictionnaire » de notre site, et nous pouvons y stocker toutes les chaînes de caractères qui ont besoin d’une traduction. Voici un exemple très simplifié de ce fichier, pour que vous puissiez voir à quoi il ressemble :</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
    <span class="token property">"fr"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"aboutMe"</span><span class="token operator">:</span> <span class="token string">"À propos de moi"</span><span class="token punctuation">,</span>
        <span class="token property">"twitterShare"</span><span class="token operator">:</span> <span class="token string">"Vous avez aimé cet article ? Partagez-le sur Twitter !"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">"en"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"aboutMe"</span><span class="token operator">:</span> <span class="token string">"About me"</span><span class="token punctuation">,</span>
        <span class="token property">"twitterShare"</span><span class="token operator">:</span> <span class="token string">"Did you like this article? Share it on Twitter!"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Mais avant de pouvoir utiliser ce dictionnaire, il est nécessaire de spécifier une clé <code>locale</code> pour chacun de nos dossiers <code>/fr/</code> et <code>/en/</code>. Pour cela, nous pouvons utiliser une autre spécificité d’Eleventy qui nous permet de définir des données pour tout un dossier en utilisant une nomenclature de fichier simple : le fichier doit porter le nom de son dossier parent. Ainsi, nous créons un fichier <code>en.json</code> situé dans <code>/src/en/</code> et un fichier <code>fr.json</code> situé dans <code>/src/fr/</code>. Le fichier <code>en.json</code> va par exemple contenir :</p>
<pre class="language-json"><code class="language-json"><span class="token comment">// /src/en/en.json</span>
<span class="token punctuation">{</span>
  <span class="token property">"locale"</span><span class="token operator">:</span> <span class="token string">"en"</span>
<span class="token punctuation">}</span></code></pre>
<p>Ce petit fichier JSON est primordial pour le support multilingue de notre site internet car il autorise l’utilisation de la clé <code>locale</code> partout dans nos templates. Cela permet donc de préciser à Eleventy que tout ce qui réside dans ce dossier possède une clé <code>locale</code> qui a pour valeur <code>en</code>.</p>
<p>Nous pouvons désormais utiliser plusieurs langues pour des pages statiques. Par exemple, il est maintenant possible d’avoir une page <code>/en/about.md</code> et une page <code>/fr/about.md</code>. Ces deux pages possédant la clé <code>translateKey</code> à <code>about</code>, elles sont « reconnues » dans le menu principal et la distinction de la page active se fait correctement.</p>
<blockquote>
<p>Notez que j’utilise ici le même nom de fichier mais rien ne nous empêcherait d’utiliser <code>/en/about.md</code> et <code>/fr/a-propos.md</code>. C’est une simple convention que j’ai décidé d’appliquer.</p>
</blockquote>
<p>Mais qu’en est-il pour les pages secondaires, et particulièrement les articles de blog ? Voyons ceci immédiatement.</p>
<h3>Gérer les articles de blog</h3>
<p>La première chose à faire est donc de spécifier un fichier <code>blog.11tydata.js</code> (dans le dossier <code>/fr/blog/</code> par exemple) afin d’y ajouter le code suivant :</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /src/fr/blog/blog.11tydata.js</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'dotenv'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> isDevEnv <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">ELEVENTY_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> todaysDate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">showDraft</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> isDraft <span class="token operator">=</span> <span class="token string">'draft'</span> <span class="token keyword">in</span> data <span class="token operator">&amp;&amp;</span> data<span class="token punctuation">.</span>draft <span class="token operator">!==</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> isFutureDate <span class="token operator">=</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date <span class="token operator">></span> todaysDate<span class="token punctuation">;</span>
  <span class="token keyword">return</span> isDevEnv <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token operator">!</span>isDraft <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>isFutureDate<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span>
	<span class="token literal-property property">eleventyComputed</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token function-variable function">eleventyExcludeFromCollections</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">showDraft</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span> data<span class="token punctuation">.</span>eleventyExcludeFromCollections<span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token function-variable function">hasCover</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">const</span> year <span class="token operator">=</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getFullYear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">const</span> month <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">'0'</span> <span class="token operator">+</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getMonth</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">const</span> day <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">'0'</span> <span class="token operator">+</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getDate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

                <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">./src/img/blog/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>year<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>month<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>day<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/cover.webp</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>

                <span class="token keyword">return</span> fs<span class="token punctuation">.</span><span class="token function">existsSync</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token literal-property property">layout</span><span class="token operator">:</span> <span class="token string">'blogpost.njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">tags</span><span class="token operator">:</span> <span class="token string">'posts_fr'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">category</span><span class="token operator">:</span> <span class="token string">'blog'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token string">'/fr/blog/{{ title | slugify }}/'</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Ce fichier est un peu plus complexe que le simple <code>fr.json</code> que nous avons utilisé plus haut, mais il nous permet plusieurs choses :</p>
<ul>
<li>L’utilisation de la clé <code>eleventyComputed</code> nous permet d’effectuer des calculs afin de retourner un booléen pour certaines clés qui peuvent être ensuite utilisées dans les templates. Ici par exemple, je vérifie la présence d’un fichier <code>cover.webp</code> dans le dossier d’images correspondant au timestamp de l’article en cours, afin de préciser si ledit article possède une image de couverture ou non.</li>
<li>La spécification de différents paramètres communs à tous les articles, comme le fichier de layout à utiliser, mais surtout trois choses très importantes : les clés <code>tags</code>, <code>category</code> et <code>permalink</code>.</li>
</ul>
<p>La première clé <code>tags</code> nous permet de spécifier le nom d’une collection sur laquelle nous pouvons boucler dans notre fichier de template. Cela peut paraître un peu compliqué au premier abord, mais une fois cette chose mise en place, le gain de temps est considérable. C’est aussi la raison pour laquelle j’ai choisi Nunjucks comme moteur de template : nous pouvons reconstruire la clé à utiliser dans le template en utilisant la locale définie dans le dossier parent. Ainsi, si nous voulons boucler sur les articles dans la locale actuelle, nous pouvons utiliser le code suivant dans notre template :</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">post</span> <span class="token keyword">in</span> <span class="token variable">collections</span><span class="token punctuation">[</span><span class="token string">"posts_"</span> <span class="token operator">+</span> <span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token variable">reverse</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">post</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">post</span><span class="token punctuation">.</span><span class="token variable">data</span><span class="token punctuation">.</span><span class="token variable">title</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<p>Pour rappel, l’utilisation de <code>post.data.title</code> va utiliser le front-matter de nos fichiers Markdown d’articles.</p>
<p>La seconde clé <code>category</code> elle nous permet de bien s’assurer que nous somme dans la section “blog” du site dans le menu (cf. plus haut).</p>
<p>La troisième clé enfin, <code>permalink</code>, permet une génération à la volée d’un permalien unique, composé du titre de l’article, mis en forme avec la dépendance <code>slugify</code>.</p>
<blockquote>
<p><strong>Note :</strong> J’utilise aussi une clé calculée spécifique <code>eleventyExcludeFromCollections</code> qui permet de savoir si un article doit apparaître ou non : les articles marqués comme brouillon (avec une clé <code>draft</code> à <code>true</code> dans le front-matter) sont cachés sauf si nous sommes dans l’environnement de développement. Cela permet de pouvoir prévisualiser les articles en brouillon, tout en les excluant directement de la collection lors du déploiement en production.</p>
</blockquote>
<h3>Lier des articles entre eux</h3>
<p>Grâce à tout ceci, il est désormais possible de lier des pages entre elles avec l'utilisation des clés <code>locale</code> et <code>translationKey</code>. Par exemple, dans le template qui permet d'afficher un article de blog, voici comment je fais la liaison avec un article rédigé dans les deux langues (Français et Anglais) :</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">locale</span> <span class="token operator">==</span> <span class="token string">'fr'</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">set</span> <span class="token variable">otherLocale</span> <span class="token operator">=</span> <span class="token string">'en'</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">else</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">set</span> <span class="token variable">otherLocale</span> <span class="token operator">=</span> <span class="token string">'fr'</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>

<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">translationKey</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">otherPost</span> <span class="token keyword">in</span> <span class="token variable">collections</span><span class="token punctuation">[</span><span class="token string">"posts_"</span> <span class="token operator">+</span> <span class="token variable">otherLocale</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">-%}</span></span>
        <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">otherPost</span><span class="token punctuation">.</span><span class="token variable">data</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token variable">translationKey</span> <span class="token delimiter punctuation">-%}</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>is-translation-link noprint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>is-lang-switcher<span class="token punctuation">"</span></span> <span class="token attr-name">data-lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">otherPost</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">i18n</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">'postAlsoAvailable'</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
        <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<h2>Conclusion</h2>
<p>Nous pouvons donc désormais utiliser plusieurs langues sur notre site internet. Les traductions des contenus rédactionnels des pages se font directement grâce aux fichiers markdown qui se trouvent dans les dossiers idoines, et les données appartenant au reste du site (UI, boutons &amp; co) sont traduites grâce aux dictionnaires du site.</p>
<p>Alors certes, la configuration peut paraître lourde et il aurait été agréable d’avoir un support directement intégré à Eleventy. Mais c’est aussi ce qui fait sa grande force : c’est un générateur de sites statique qui aime mettre le développeur à la barre, et il nous laisse trouver la meilleure solution (ou tout du moins la plus confortable) pour les problèmes que nous pouvons rencontrer.</p>
<p>Dans la prochaine (et peut-être dernière ?) partie de cette série d’articles, nous verrons comment générer une sitemap et un fichier de flux RSS pour nos articles de blog, ainsi que quelques astuces pour se faciliter la vie sur les pages et articles du site.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Maîtriser Eleventy - Partie 1 : La structure de projet</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/maitriser-eleventy-partie-1-la-structure-de-projet/" />
            <updated>2024-02-23T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/maitriser-eleventy-partie-1-la-structure-de-projet/</id>
            <content type="html">
                <![CDATA[
                <h2>Préambule</h2>
<p>Ayant toujours baigné dans l’univers des langages interprétés, je dois reconnaître que même pour moi, le simple fait de « compiler » un site internet m’avait surpris au début. Et pourtant, quelque chose de spécial se passe lors de la compilation d'un site internet statique : les informations sont récoltées, découpées et mises en forme, des appels API sont effectués et des données transitent, et des assets sont transformés. Et une fois que tout est en place, voir s'imbriquer les systèmes tels des engrenages bien huilés apporte quelque chose de satisfaisant. Nous avons à disposition un ensemble de code prêt à être délivré, qui respecte des bonnes pratiques et est très rapide à l’exécution. Mais surtout, le tout est solide comme un roc, (presque) impénétrable aux attaques, et gravé dans le marbre numérique.</p>
<p>Et pourtant, le principe de base est simple : le générateur utilise une syntaxe Markdown pour le contenu rédactionnel, un moteur de templating pour mettre en forme ce dernier, et au final fournit un ensemble de fichiers HTML &amp; CSS qui peuvent alors être posés sur un serveur et délivrés par Apache ou Nginx. Pas de base de données, pas de code interprété, et donc par conséquent une sécurité et une vitesse d'exécution sans équivalence.</p>
<p>Au fil des ans, le site internet que vous êtes en train de lire aura utilisé plusieurs de ces générateurs. Faisons un petit tour d’horizon de ce que j’ai déjà utilisé, avec (à mon avis) leurs forces et faiblesses.</p>
<h3>Jekyll</h3>
<p>En premier, j'ai utilisé <a href="https://jekyllrb.com">Jekyll</a>. Précurseur de la mouvance JAMStack, il a été créé il y a plus de 15 ans par Tom Preston-Werner, le fondateur de GitHub. Aujourd’hui, Jekyll est probablement le générateur de site statique le plus connu et le plus utilisé au monde. C'est vraiment un système formidable, et sa documentation est excellente. Seulement voilà : Jekyll demande de posséder et maintenir un environnement Ruby. Et si comme moi vous n'êtes pas un développeur utilisant cet écosystème au quotidien, cela peut devenir un peu fastidieux.</p>
<h3>Hugo</h3>
<p>J'ai utilisé par la suite <a href="https://gohugo.io">Hugo</a>, « fils spirituel » de Jekyll, et dont la force majeure est sa vitesse d'exécution hors pair. Seulement voilà (encore) : Hugo est écrit en Go et sa syntaxe de templating est disons, pour rester poli, spéciale. Je n'ai jamais pu me faire ni à ses opérateurs, ni à sa syntaxe globale. Le développement de cette version s'est clairement fait dans la douleur, d'autant plus que sa documentation est très hermétique. Ceci dit, Hugo possède deux choses que j'ai trouvé extrêmement puissantes à l’époque : sa notion de « contexte » dans les feuilles de templates, et sa gestion du multilangue. Le fait de pouvoir lier du contenu entre plusieurs langues de manière transparente est quelque chose d'extrêmement pratique qui fait gagner beaucoup de temps.</p>
<h3>Eleventy (11ty)</h3>
<p>Finalement, la version actuelle du site a été faîte sous <a href="https://www.11ty.dev">Eleventy</a>. Là aussi, la documentation n'est pas des plus limpides, mais c'est un projet que je suis depuis longtemps et il bénéficie à la fois d’une communauté vivante et d’une approche très « ouverte », ce qui m’a donné l’envie de l'adopter. Le support i18n était inexistant jusqu'à il y a peu, mais de gros progrès ont été fait depuis. Son aspect le plus attrayant pour moi est le fait que Eleventy est écrit en JavaScript, et j'ai tout de suite été séduit par la liberté qu'il laisse à l'utilisateur (D'ailleurs je crois que son nom provient du nombre de moteurs de templates utilisables).</p>
<blockquote>
<p>Petite parenthèse en passant : le « branding » de ce projet est certainement l'un des plus maladroits qu'il m'ait été donné de voir. Le projet s'appelle « Eleventy » mais le domaine est épelé « 11ty », l'un des formats de templating acceptés possède l’extension .11ty.js, mais le fichier de configuration global s'appelle .eleventy.js. Cela peut être extrêmement troublant pour un néophyte, et je pense que le projet mériterait une meilleure cohésion autour de la « marque » Eleventy. Cela peut paraître anecdotique, mais en 2024 je pense que c’est un passage obligé pour pouvoir acquérir des utilisateurs, même pour un projet Open Source. Il suffit de voir le succès d’autres frameworks comme Astro ou Laravel pour se rendre compte à quel point l’image est importante dans le succès de tels produits. Allez, une petite dernière avant de fermer cette parenthèse : si vous cherchez le compte Twitter du projet, vous le trouverez sous le pseudo… <a href="https://twitter.com/eleven_ty/">@eleven_ty</a> :zany_face:</p>
</blockquote>
<p>Mais revenons donc à cette liberté d'utilisation, qui peut parfois paraître déroutante. Certes, c’est une grande force pour un développeur, mais je pense qu’une meilleure organisation de travail est possible, et elle permet d’avoir un code plus propre avec une structure plus facile à naviguer. C'est pourquoi dans ce premier article d'une série consacrée à Eleventy, je vous propose de vous partager la manière dont j'organise mon travail afin de conserver un maximum d'efficacité.</p>
<h3>La structure que je recommande</h3>
<p>Par défaut, Eleventy fait des suppositions sur l'endroit où vous pouvez stocker vos données. Mais ces suppositions sont parfois trompeuses au niveau de leur nomenclature, et peuvent manquer de cohérence. Par exemple, le dossier attendu pour les templates s’appelle <code>/_includes</code>, celui pour les données <code>/_data</code>. Cela peut paraître anodin, mais une chose me dérange : ces dossiers se retrouvent mélangés avec l’arborescence du site final, avec les dossiers utilisés pour contenir du code qui devra être manipulé par la suite (SASS, JavaScript), et les fichiers de configuration du projet.</p>
<p>Personnellement, j’utilise donc cette structure :</p>
<pre class="language-txt"><code class="language-txt">/dist
/src
  /_assets
    /js
    /scss
  /_data
  /_layouts
  /en
    /about
    /…
  /fonts
  /fr
    /about
    /…
  /img</code></pre>
<p>Et voici l’explication de ce choix :</p>
<ul>
<li><code>/dist</code></li>
</ul>
<p><code>/dist</code> est le répertoire de destination du site généré. Par défaut, Eleventy utilise le dossier <code>/_site</code> mais comme je le précisais plus haut, cela vient se mélanger avec les dossiers de data et autres. Ce répertoire est à ajouter dans le fichier <code>.gitignore</code> car il ne doit pas être commit.</p>
<ul>
<li><code>/src</code></li>
<li><code>/src/_assets</code></li>
<li><code>/src/_data</code></li>
<li><code>/src/_layouts</code></li>
</ul>
<p><code>/src</code> devient le répertoire de travail principal. C’est dans ce dossier que va résider tout le code source du site à générer.</p>
<p><code>/src/_assets</code> est un dossier réservé aux assets compilés ou minifiés, comme le JavaScript ou le SASS. Ces fichiers sont manipulés lors du build par un fichier spécifique, pour générer leur version finale (nous verrons comment dans un prochain article).</p>
<p><code>/src/_data</code> est le dossier qui sert de « base de données » au site. Nous pouvons en effet utiliser des fichiers JSON comme sources de données sur lesquelles nous pouvons boucler, ou c’est encore ici que nous stockerons le dictionnaire multilingue. Ici je ne change pas la nomenclature d’11ty mais simplement l’emplacement du dossier.</p>
<p><code>/src/_layouts</code> est l’endroit qui va contenir tous nos fichiers de template. J’ai fait le choix d’utiliser Nunjucks, mais on peut utiliser Liquid ou un autre, c’est une question de préférence. Par défaut, 11ty s’attend à utiliser un dossier <code>/_includes</code> et là encore la nomenclature que j’utilise a plus de sens, mais surtout se trouve dans un dossier unique <code>/src</code>.</p>
<ul>
<li><code>/src/en</code></li>
<li><code>/src/fr</code></li>
<li><code>/src/en/about</code></li>
<li><code>/src/fr/about</code></li>
<li><code>/src/en/...</code></li>
<li><code>/src/fr/...</code></li>
</ul>
<p><code>/src/en</code>, <code>/src/fr</code> et leurs sous-dossiers font partie de l’arborescence principale du site compilé. C’est dans ces dossiers que nous retrouverons les différentes sections de notre site, avec leur contenu Markdown équivalent. Étant donné que je crée un site multilingue, j’ai décidé d’utiliser un sous-dossier par langue, comme le préconise la documentation. J’expliquerai dans un prochain article comment je fais le pont entre les langues.</p>
<ul>
<li><code>/src/fonts</code></li>
</ul>
<p><code>/src/fonts</code> est un endroit où je stocke les polices de caractères, car je préfère les servir directement plutôt que d’utiliser le CDN de Google. Cela me permet d’éviter un appel HTTP externe et surtout de me passer du fingerprinting de Google.</p>
<ul>
<li><code>/src/img</code></li>
</ul>
<p><code>/src/img</code> est le dossier où vont résider toutes les images du site. Petite note en passant : si vous ne le faîtes pas déjà, je vous encourage à utiliser au maximum un format récent comme l’AVIF ou le WEBP qui sont nettement plus performants en termes de compression par rapport au JPG ou au PNG.</p>
<h3>Aides à la compilation</h3>
<p>Décider d’une arborescence est une chose, mais nous devons dire à 11ty où trouver ce qu’il cherche. Pour cela, nous devons configurer le fichier <code>/.eleventy.js</code> qui est utilisé lors du build.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /.eleventy.js</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
	<span class="token literal-property property">markdownTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">htmlTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">templateFormats</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'md'</span><span class="token punctuation">,</span> <span class="token string">'njk'</span><span class="token punctuation">,</span> <span class="token string">'html'</span><span class="token punctuation">,</span> <span class="token string">'11ty.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
	<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
	<span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Ici nous précisons que le moteur de template utilisé est Nunjucks, et que les fichiers qui doivent être considérés comme des templates par 11ty peuvent porter l’extension <code>.md</code>, <code>.njk</code>, <code>.html</code> ou <code>.11ty.js</code>. Ces fichiers là vont donc forcément passer par le moteur Nunjucks afin de générer leur contrepartie HTML ou autre (précisé dans le front-matter). Enfin, sous la clé <code>dir</code> nous précisons les dossiers utilisés pour l’input, la destination et les layouts.</p>
<p>Ainsi, la racine du projet peut se contenter de contenir tous les fichiers de configuration (<code>.eleventy.js</code>, <code>.gitignore</code>, <code>package.json</code>, etc…), le code source réside dans <code>/src</code> et le site généré va se retrouver dans <code>/dist</code>.</p>
<p>Dans un prochain article je décrirai comment compiler les assets JavaScript / SASS, ainsi que quelques astuces qui faciliteront le développement.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Comment se débarasser du Flash Of Unstyled Content</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/comment-se-debarasser-du-flash-of-unstyled-content/" />
            <updated>2020-04-21T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/comment-se-debarasser-du-flash-of-unstyled-content/</id>
            <content type="html">
                <![CDATA[
                <p>Cette semaine j'ai pris un peu de temps pour travailler sur les performances de chargement de mon site. J'ai commencé par basculer de <a href="https://kenwheeler.github.io/slick/">Slick</a> à <a href="https://glidejs.com/">Glide.js</a> afin de totalement supprimer jQuery comme dépendance. Cela m'a permis de diviser la taille du JavaScript et de la CSS chargés de moitié (!). J'ai ensuite ajouté un cookie de préférence de langue. Puis, afin d'améliorer l'expérience utilisateur, j'ai ajouté une fonction qui permet de faire cette bascule automatiquement en fonction de la langue du navigateur.</p>
<p>Tout se passait bien, mais je me suis rendu compte que mon site souffrait d'un <a href="https://fr.wikipedia.org/wiki/FOUC">Flash Of Unstyled Content</a>, ou &quot;FOUC&quot;. C'était assez visible même avec le nouveau JavaScript et CSS en place : une fois un lien cliqué, la page commençait à être rendue pratiquement immédiatement avant que la CSS ne soit appliquée. C'est un phénomène particulièrement ennuyeux car cela retire l'utilisateur de cette expérience lisse et presque instantanée que je cherchais à atteindre. Heureusement, on peut rapidement se débarrasser de ce FOUC avec quelques petits trucs faciles à mettre en place.</p>
<h2>Étape 1: On cache tout !</h2>
<p>La première chose à faire est tout simplement d'ajouter une instruction CSS au body afin de le cacher complètement jusqu'au moment où il est prêt à être affiché. Cela permet à la page d'être totalement chargée avant de pouvoir la présenter à l'utilisateur. Cela peut paraître contre-intuitif lorsque nous cherchons justement à gagner en performance et donc en vitesse, et là nous <em>ralentissons</em> les choses. Mais c'est un sacrifice nécessaire que nous faisons sur l'autel de l'expérience utilisateur.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
  <span class="token comment">&lt;!-- Your awesome website --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre>
<p>Nous pourrions aussi utiliser la propriété <code>opacity</code> à la place, et utiliser des transitions CSS pour ajouter un peu de magie.</p>
<h2>Étape 2 : Dévoiler quand tout est prêt</h2>
<p>Nous devons ensuite renverser cette propriété CSS <code>visibility</code> une fois que le DOM est chargé et prêt. Pour cela, j'utilise une petite fonction d'aide, un peu à la manière de la méthode <code>document.ready()</code> de jQuery. Cela appelle un callback une fois que le document est dans un état &quot;complete&quot; ou &quot;interactive&quot;.</p>
<p>Nous changeons donc simplement la propriété <code>visibility</code> du tag <code>&lt;body&gt;</code> à <code>visible</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Helper function</span>
<span class="token keyword">let</span> <span class="token function-variable function">domReady</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">cb</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  document<span class="token punctuation">.</span>readyState <span class="token operator">===</span> <span class="token string">'interactive'</span> <span class="token operator">||</span> document<span class="token punctuation">.</span>readyState <span class="token operator">===</span> <span class="token string">'complete'</span>
    <span class="token operator">?</span> <span class="token function">cb</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'DOMContentLoaded'</span><span class="token punctuation">,</span> cb<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">domReady</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token comment">// Display body when DOM is loaded</span>
  document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span>visibility <span class="token operator">=</span> <span class="token string">'visible'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Et voilà ! Notre FOUC est parti. Avec cette astuce simple, notre utilisateur a une meilleure expérience et n'a pas de contenu bizarre qui flashe à l'écran avant de pouvoir naviguer sur notre site.</p>
<h2>Le problème Firefox</h2>
<p>Les choses devraient rouler sous Chrome, mais il est toujours possible de voir un flash sous Firefox. J'ai peiné à trouver une solution à ce problème jusqu'à ce que je déniche un bug dans Firefox qui a été relevé <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1404468">il y a plus de 3 ans</a> et qui est encore actif. On essaie toujours d'y trouver un fix mais heureusement, il existe un petit hack que nous pouvons utiliser pour contourner ce problème. Il suffit d'ajouter un bout de code JavaScript &quot;fantôme&quot; juste après le tag <code>&lt;body&gt;</code> et tout devrait aller !</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token number">0</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre>
<p>Pas mal, non ? Un peu étrange aussi, je dois avouer. Mais bon, ça fait le boulot.</p>
<h2>Note : Pensez au noscript</h2>
<p>Il ne faut pas oublier que tout le monde ne peut pas ou ne veut pas exécuter du JavaScript. Dans ce cas, cette ligne juste avant la balise fermante <code>&lt;/body&gt;</code> permet à tout le monde de voir notre site.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><span class="token selector">body</span> <span class="token punctuation">{</span> <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span> <span class="token punctuation">}</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span></code></pre>
<p>Et voilà, tout est prêt ! Maintenant notre site devrait s'afficher correctement, sans aucun FOUC ! 🎉</p>
<p><strong>Mise à jour - 1er mai 2020</strong></p>
<p>On m'a averti que mon code cassait le validateur W3C. C'est parce qu'officiellement, la balise <code>&lt;style&gt;</code> ne peut pas être un enfant de <code>&lt;noscript&gt;</code>.</p>
<p>Pour remédier à cela, ce que nous pouvons faire est de supprimer cette balise <code>&lt;noscript&gt;</code>, et  ajouter une classe <code>no-js</code> à l'élément <code>body</code>. Ensuite, nous ajoutons simplement cette règle CSS dans le <code>&lt;head&gt;</code> du document :</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
    <span class="token selector">.no-js</span> <span class="token punctuation">{</span>
      <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code></pre>
<p>Et enfin nous basculons à nouveau le tout juste après la balise <code>&lt;body&gt;</code> grâce à ce JavaScript :</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
        document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'body'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'no-js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>Cela va non seulement rendre les choses compatibles avec le W3C, mais puisque nous avons ajouté un petit bout de JavaScript dans le <code>body</code> de notre document, le code JS fantôme que nous avons créé tout à l'heure devient obsolète ! Donc maintenant, tout le monde est content, et nous pouvons enfin siroter un verre d'eau bien fraîche sous le soleil.</p>
<p><img src="/img/blog/2020-04-21/sealofapproval.jpg" alt="Seal of approval"></p>
<p><strong>Mise à jour 06/05/2021 :</strong> Merci à <a href="https://profitlich.ch/">Moritz Profitlich</a> pour avoir corrigé une petite coquille dans le code source de cet article ! 😄</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>DevFest Toulouse 2019</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/devfest-toulouse-2019/" />
            <updated>2019-10-09T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/devfest-toulouse-2019/</id>
            <content type="html">
                <![CDATA[
                <p>Pour la troisième année consécutive, je me suis rendu dans la ville rose afin d'assister au <strong>DevFest Toulouse</strong>. C'est une conférence &quot;pour les développeurs par les développeurs&quot; se déroulant le long d'une journée, et où j'ai pu assister à des talks divers et variés autour du web, du métier de développeur et de la pléthore de technologies qui font mon métier. On a parlé Rust, Scala, Agilité, mais aussi Android, impression 3D et chocolatines. Petit retour d'expérience.</p>
<p>La journée a commencé par une <a href="https://www.youtube.com/watch?v=AFhHrQIAw3g">keynote de bienvenue</a> animée par <strong>Maxime Pawlak</strong> grimé en Steve Jobs, qui nous a fait une présentation en mode &quot;amazing&quot;. Le ton était donné dès la remise du badge d'entrée, entièrement retravaillé par leurs équipes de &quot;genius designers&quot; : deux fois plus grand, deux fois plus large, deux fois plus encombrant à porter autour du cou. Merci <s>Apple</s> le DevFest !</p>
<p>La journée de conférences commençait timidement, avec &quot;<a href="https://www.youtube.com/watch?v=LHzVkjHjSso"><em>Une histoire de l'informatique, du métier à tisser à la machine de Babbage</em></a>&quot; animé par <strong>Fabien Trégan</strong>. Je suis resté un peu sur ma faim, le choix de ce talk étant bien en deçà de la formidable keynote d'ouverture de l'an dernier où <strong>Laurent Victorino</strong> avait réussi à manipuler 700 personnes en faisant une &quot;fausse&quot; présentation interactive. Pendant 40 minutes, Fabien nous a donc retracé quelques grandes avancées technologiques et mathématiques qui tissent un fil rouge aux prémices de l'informatique modernes. C'était intéressant, surtout grâce au support de maquettes (il ne manquait que Jamy Gourmaud pour bien faire), mais un peu confus sur la longueur.</p>
<p>J'ai pu ensuite assister à &quot;<a href="https://www.youtube.com/watch?v=wPRwD4rLOVo"><em>The rise of the web</em></a>&quot;, un talk captivant où <strong>Loïc Ortola</strong> a retracé l'arrivée du Web, l'histoire de Netscape, la naissance de PHP et l'émergence de la déferlante JavaScript. C'était un sujet passionnant, magnifiquement bien raconté et illustré.</p>
<p>Sur cette lancée, je me suis plongé dans la conférence de <strong>Elliot Alderson</strong> (alias <em>@fs0c131y</em>, ou <strong>Baptiste Robert</strong> de sa véritable identité) qui nous a raconté &quot;<a href="https://www.youtube.com/watch?v=XyczLWRnD8M"><em>L'histoire de la découverte d'une backdoor signée OnePlus</em></a>&quot;. J'avoue être entré dans la salle sans savoir à quoi m'attendre. Je ne suis ni développeur Java, ni développeur Android, mais j'étais intrigué par cette histoire. Je ne connaissais l'auteur que via son fil Twitter, et je dois avouer que même si je suis très fan de la série <em>Mr Robot</em>, j'ai toujours un peu de mal avec les gens qui s'identifient d'un peu trop près à des personnages de fiction . Au final, le talk était extrêmement intéressant, et même s'il parlait d'un langage que je ne maîtrise pas du tout, Baptiste a réussi à captiver la salle par son histoire totalement hallucinante où en une soirée, complètement par hasard, il réussit à trouver une faille de sécurité permettant l'accès root à des miliers (des milions ?) de téléphones Android à travers le monde. J'ai eu la chance d'échanger avec lui par la suite. Baptiste est le genre de personne à qui on a envie de poser un million de questions, et toutes ses réponses piquent la curiosité. Il m'a confié ainsi l'avalanche de requête de la part de la presse dont il a été la cible (la victime ?) suite à la révélation de ce &quot;hack&quot;. Une célébrité aussi soudaine qu'éphémère qui lui aura permis de gagner en notoriété, mais aussi quelques enemis au passage.</p>
<p>Midi approchant rapidement, c'était l'heure pour les <em>quickies</em>, ces petits talks sur des sujets un peu plus légers. J'ai donc assisté à une <a href="https://www.youtube.com/watch?v=oh6o499ktMU">présentation de code collaboratif</a> sur VS Code via <em>Live Share</em>, une extension développée par Microsoft. <strong>Olivier Leplus</strong> et <strong>Tiffany Souterre</strong> nous ont donc présenté comment, grâce à VS Code, on peut partager du code source sans même avoir à cloner un repo. C'est une fonctionnalité que je connaissais pour l'avoir déjà testé brièvement il y a un an, mais j'ai trouvé intéressant de voir ce que les équipes de Microsoft ont ajouté depuis. Ainsi, on peut maintenant installer des plugins à cette extension (comme un timer pomodoro ou un whiteboard par exemple), et chose très pratique : on peut faire du routage de ports afin de partager un serveur à distance et même un terminal (en lecture / écriture !). C'était très intéressant, même si il faut l'avouer difficile à montrer en live. Olivier et Tiffany on dû faire une démonstration de travail collaboratif sur un seul écran et cela demandait une petite gymnastique mentale pour bien se représenter ce dont il était sujet.</p>
<p>Dernier quickie, <strong>Richard Fagot</strong> nous a présenté son &quot;<a href="https://www.youtube.com/watch?v=z72VgzUirL4"><em>distributeur automatique d'argent de poche pour les enfants</em></a>&quot;. Derrière une idée basique (comment distribuer de l'argent de poche à ses enfants ?) repose plus de deux mois de travail plein, de la R&amp;D, de la conception, un peu de chimie, de la mécanique, de l'impression 3D, du code, des cartes à puces, etc... Le sujet était totalement passionnant, et le produit final (Richard avait la machine avec lui) est très bien réalisé. Le but était d'apprendre à ses enfants la valeur de l'argent, mais je suis persuadé que Richard a lui-même appris énormément de choses en se retroussant les manches et en imaginant un tel objet. Bravo !</p>
<div class="is-blog-img"><img src="/img/blog/2019-10-09/devfest2.jpg" alt="Les speakers et l'équipe du DevFest"><p class="is-blog-img-title">Les speakers et l'équipe du DevFest</p></div>
<p>Retour aux choses &quot;sérieuses&quot; après la pause repas, <strong>Sylvain Wallez</strong> nous fait une démo de <a href="https://www.youtube.com/watch?v=LShM4QzMOxY">WebAudio et Tone.js</a>. C'était rigolo même si j'avoue n'avoir vu aucune application pratique pour moi. Chapeau à Sylvain cependant, faire une démo avec de l'audio pendant 40 minutes sans aucun plantage, c'est fort !</p>
<p><strong>Noël Macé</strong> a ensuite pris la parole pour un talk sur <a href="https://www.youtube.com/watch?v=-d_Ka7OE4Xk">le Javascript &quot;vanilla&quot;</a>. J'attendais beaucoup de ce talk, et je pensais que ça allait être un &quot;back to basics&quot;, à la manière des dernières présentations d'<strong>Hubert Sablonnière</strong> (conférencier que je suis avec beaucoup d'intérêt, ses talks sont captivants, comme l'an dernier sur les cookies HTTP). C'était en réalité un petit tour d'horizon des différents frameworks existants aujourd'hui, et une réflexion un peu plus généraliste sur la tendance actuelle à s'engager dans un framework sans une réelle recherche sur l'utilité d'une telle action. Bien présenté, mais cela manquait de profondeur, c'est domage.</p>
<p>La fin de journée commençait à se faire sentir mais je me dirigeais avec impatience vers la prochaine salle pour revoir un speaker que j'ai eu le plaisir de découvrir l'an dernier : <strong>Piotr Przybył</strong>. Son talk, sobrement intitulé &quot;<a href="https://www.youtube.com/watch?v=E9EKWrRcyYk"><em>Four diseases</em></a>&quot;, passait en revue quatre exemples de mauvaises pratiques que l'on peut rencontrer souvent dans notre métier. Les architectures trop lourdes, les mauvaises implémentations fonctionnelles, un typage trop permissif ou encore les pièges à vouloir recréer des choses déjà existantes. Piotr fait partie, avec Hubert cité plus haut, de ces speakers totalement fascinants qui ne font pas qu'uniquement présenter des slides mais amènent leur public à une réelle réflexion sur des sujets profonds. Hubert a une capacité incomparable à raconter de véritables histoires durant ses talks. Piotr introduit une certaine poésie à des réflexions qui sont parfois très techniques. J'en suis ressorti enrichi, avec une critique sur certaines bonnes pratiques qui restera avec moi. Pour moi, ce talk, avec ceux de <strong>Richard Fagot</strong>, <strong>fs0c131y</strong> et <strong>Loïc Ortola</strong> justifiait amplement le déplacement.</p>
<p>Entre les talks, nous avions la possibilité de rencontrer les différents speakers de la conférence, grâce à des &quot;Office hours&quot; organisées tout au long de la journée. J'ai pu échanger avec quelques développeurs, qui parfois font des choses qui n'ont absolument rien à voir avec mon domaine d'expertise. Ce genre de rencontre est toujours enrichissante, car cela permet d'aborder une nouvelle vision à des problèmes communs, ou tout simplement pour découvrir de nouveaux horizons. Tout un étage du Centre des Congrés était ainsi réservé à différents acteurs du numérique qui venaient présenter leur entreprise ou leurs produits. J'ai pu par exemple échanger avec GitHub, et récupérer un petit autocollant Octocat au passage. Parce qu'il faut être honnête, assister à une journée de conférence est un moyen déguisé de récupérer des stickers pour son laptop. 😋</p>
<p>J'ai aussi pu rencontrer <strong>Berger Levrault</strong>, éditeur de solutions numériques pour collectivités. C'était très intéressant d'échanger avec leurs équipes, et c'est exactement ce dont je parlais plus haut. Berger Levrault est dans un secteur qui ne me concerne pas du tout, mais leurs problématiques rejoignent un peu les miennes : le travail en remote, la gestion d'un code legacy, l'approche de migrations techniques, etc...</p>
<p>La keynote de fin, animée par <strong>Christian Fauré</strong>, portait sur une &quot;<a href="https://www.youtube.com/watch?v=aYA_a0-bplY"><em>analyse critique des Tech Trends</em></a>&quot;. Christian est une personne fascinante. Son expertise, alouée à sa grande expérience, lui permet une réflexion profonde et très intéressante sur le sujet de la prédiction des technologies informatiques. C'était un très bon choix pour finir cette journée, et faisait parfaitement écho à la keynote d'ouverture. Nous avions commencé la journée en étudiant le passé, nous la finissions en essayant de deviner le futur.</p>
<p>La journée se terminait. Pendant que certains allaient faire la fête, je me dirigeais prestement vers la gare afin de rentrer dans mes contrées. C'est dans le train que j'écris ce texte, fatigué mais la tête pleine d'idées, et excité à l'idée de tenter de nouvelles choses et d'apprendre tous les jours un peu plus. Un grand merci aux équipes du DevFest Toulouse qui ont encore une fois organisé un événement formidable.</p>
<p>À l'année prochaine !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Rendez votre site web plus rapide en 1 minute avec Instant.page</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/rendez-votre-site-web-plus-rapide-en-1-minute-avec-instant-page/" />
            <updated>2019-10-03T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/rendez-votre-site-web-plus-rapide-en-1-minute-avec-instant-page/</id>
            <content type="html">
                <![CDATA[
                <p>Derrière ce titre putaclic se cache un vrai petit outil très pratique. Je suis tombé dessus en surfant Hacker News il y a très longtemps, et à vrai dire il est actif sur ce site <a href="https://github.com/fbnlsr/my-website/commit/d862953f35a2ae0992ed11bd8c294bf8d7658a91">depuis février</a> mais je n'ai jamais pris le temps d'en parler. <strong>Instant.page</strong> est donc une petite librairie Javascript qui utilise un <em>&quot;préchargement juste à temps&quot;</em> sur les liens d'une page – c'est-à-dire que le script précharge une ancre juste avant qu'un utilisateur ne clique sur un lien hypertexte.</p>
<p>La technique utilisée est très simple : <strong>Instant.page</strong> va en effet calculer combien de temps un utilisateur laisse le curseur de sa souris sur un lien (ce qui jusqu'à preuve du contraire est le comportement par défaut lorsqu'on veut cliquer sur quelque chose) et va déclencher le préchargement si ce temps dépasse les 65ms. Étant donné qu'un humain lambda <a href="https://www.nngroup.com/articles/response-times-3-important-limits/">considère comme instantanée une action qui prend moins de 100ms</a>, <strong>Instant.page</strong> trompe le cerveau. Du coup, ce script vous permet de proposer à vos utilisateurs une meilleure expérience, car plus rapide.</p>
<p>L'installation est des plus simple : il suffit d'insérer la balise <code>&lt;script&gt;</code> contenant le lien du script juste avant la balise de fermeture <code>&lt;/body&gt;</code> do votre site, et c'est tout ! La librairie va se greffer automatiquement sur les ancres de la page. Elle ne pèse qu'1kB, et est gratuite et open source (MIT).</p>
<p><strong>Instant.page</strong> vous permet aussi de contrôler quel contenu doit être préchargé ou non. Vous pouvez par exemple (et c'est d'ailleurs fortement conseillé par son créateur) refuser le préchargement de certains liens, comme ceux de logout. Pour se faire, il suffit d'ajouter un attribut <code>data-no-instant</code> à ces liens et <strong>Instant.page</strong> va les ignorer. Vous pouvez même spécifier vouloir accepter le préchargement des liens externes.</p>
<p>Simple, légère et facile. Si vous voulez tester par vous-même, retrouvez <strong>Instant.page</strong> ici : <a href="https://instant.page/">https://instant.page/</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>SBBT Architecture</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/sbbt-architecture/" />
            <updated>2018-09-27T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/sbbt-architecture/</id>
            <content type="html">
                <![CDATA[
                <p>Pour cette deuxième collaboration avec <a href="https://nysb.paris">NySB</a>, nous avons travaillé sur le site internet de <a href="https://sbbt-architecture.com/">SBBT Architecture</a>. La plateforme choisie est Wordpress, le client désirant un système d'administration complet lui permettant de modifier tous les éléments de son site.</p>
<p>J'ai dû construire un carrousel sur-mesure faisant l'utilisation d'images en plein écran. Le tout est géré par Advanced Custom Fields, laissant au client la possibilité de modifier tous les aspects des diapositives : de l'image de fond au texte principal, de la couleur à la direction du dégradé, et ainsi de suite.</p>
<p>Pour la grille sur la page projets, j'ai utilisé <a href="https://vestride.github.io/Shuffle/">Shuffle.js</a>, une grille responsive qui est catégorisable, triable et filtrable.</p>
<p>Le problème principal que nous avons rencontré sur ce site est son poids. Il utilise énormément d'images en plein-écran, et étant donné qu'elles représentent des projets architecturaux, il était impératif d'avoir des photos de qualité. J'ai trouvé la solution en utilisant l'API de <a href="http://reSmush.it">reSmush.it</a>, qui compresse à la volée les images qu'un administrateur envoie dans la galerie de médias.</p>
<p>Pour ce projet, nous avons fait l'utilisation de Wordpress, SCSS, ShuffleJS, jQuery et Webpack.</p>
<p>Rendez-vous sur <a href="https://sbbt-architecture.com">https://sbbt-architecture.com</a> pour voir le résultat !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>NySB</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/nysb/" />
            <updated>2018-07-31T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/nysb/</id>
            <content type="html">
                <![CDATA[
                <p><a href="https://nysb.paris">NySB</a> est une agence de communication graphique implantée sur Paris qui réalise pour ses clients identités visuelles, branding et packaging. Ayant déjà un parcours solidement ancré dans le design graphique, ils ont décidé de mettre plus en avant leur créativité sur les plateformes numériques. Ils avaient besoin de quelqu'un pour les aider à construire un site internet pouvant communiquer leurs idées. &quot;Être audacieux sans se perdre dans le process&quot;. C'est avec cette approche qu'ils ont conçu le design de leur site, et j'ai vraiment le sentiment que le résultat final est une bonne représentation de ce à quoi ils aspirent dans leur travail.</p>
<p>Ce fût un réel plaisir de travailler avec Thomas et Pierre-Mathieu pour ce premier projet. Ayant une image précise de leurs projets, ils sont complémentaires dans leur approche et accordent tous deux énormément de valeur à leur travail. Ils m'ont fait confiance pour concrétiser leurs idées de design, tout en ayant l'esprit ouvert aux conseils que je pouvais leur donner quant aux impératifs liés au responsive design ou encore à des implémentations d'UX.</p>
<p>Pour ce projet, la base de travail était Wordpress, Bulma et Webpack.</p>
<p>Rendez-vous sur <a href="https://nysb.paris">https://nysb.paris</a> pour voir ce que nous avons réalisé !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Forestry - Un CMS statique</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/forestry-un-cms-statique/" />
            <updated>2018-06-15T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/forestry-un-cms-statique/</id>
            <content type="html">
                <![CDATA[
                <p>L’utilisation de sites internet statiques se répand de plus en plus sur le web. Au delà d’un “retour aux sources” numérique, ces systèmes ont de multiples avantages. Ils sont en effet souvent plus rapides, car ils se limitent à servir des pages web simples, sans travail côté serveur. La logique est laissée à la charge du client, le plus souvent par le biais d’API, et il est beaucoup plus facile de mettre en cache et d’utiliser un CDN lors du déploiement. Ces derniers sont d’ailleurs atomiques, avec une invalidation de cache instantanée. La délivrabilité du site est ainsi assurée à 100%. Les sites statiques sont aussi souvent beaucoup plus sécurisés étant donné qu’ils ne permettent pas l’envoi direct d’informations au serveur.</p>
<p>Néanmoins, la mise à jour et le déploiement d’un site statique se fait obligatoirement par le biais d’une machine qui bénéficie d’un environnement de développement idoine. Un terminal et des outils comme Git, Node, NPM, Ruby, et autres sont parfois nécessaires. Le site doit en effet être recompilé, soit en amont, soit par un système automatisé comme GitHub Pages ou Netlify.</p>
<h2>De l’utilité d’un CMS statique</h2>
<p>L’intérêt d’utiliser un CMS pour un site statique devient donc pertinent pour plusieurs raisons. Premièrement cela permet de s’affranchir de cet environnement de développement. Dans le cas d’un blog, la création d’articles devient beaucoup plus fluide, et le fait de pouvoir se détacher de cet environnement technique permet un meilleur workflow du processus de création de contenu. Ensuite, cela permet à des utilisateurs non techniciens de pouvoir prendre la main sur leur site et de leur laisser libre court à la création et la gestion de contenu éditorial.</p>
<p>Aujourd’hui je m’intéresse à Forestry, un service hébergé qui utilise l’API GitHub pour permettre à un site statique d’être géré à distance.</p>
<p><img src="/img/blog/2018-06-15/forestry-logo.jpg" alt="Logo de Forestry" title="Logo de Forestry"></p>
<h2>Installation</h2>
<p>L’installation du CMS est extrêmement simple : j’ai dû créer un compte sur le site de Forestry, le lier à mon compte GitHub et choisir le dépôt du site en question. Il suffit ensuite de spécifier si le site est généré par Hugo (ce qui est mon cas) ou par Jekyll. Petite note au passage : j’ai dû préciser la version de hugo que j’utilise, et celle proposée par Forestry ne correspondra pas forcément à la votre. Cela ne m’a pas posé de problème car j’ai configuré Forestry pour qu’il ne gère pas la  génération de mon site, mais c’est un détail qui peut avoir son importance.</p>
<h2>Configuration</h2>
<p>La configuration de Forestry est toute aussi simple que son installation car le système va automatiquement naviguer dans le dépôt et va générer les types de contenu en fonction de ce qu’il trouve. Il m’a fallu ainsi cinq minutes pour avoir un backoffice fonctionnel et être tout de suite productif.</p>
<p>Il est même possible de venir greffer le CMS sur son propre site, et passer par une URL que l’on va spécifier (par exemple <a href="http://www.exemple.com/admin">www.exemple.com/admin</a>), ce qui permet de ne jamais quitter son propre site internet.</p>
<h2>Utilisation</h2>
<p>Forestry fonctionne grâce à l’API de GitHub, Gitlab ou Bitbucket. C’est un CMS très pratique à utiliser avec une interface claire. À gauche, un panneau listant les types de contenu permet d’accéder aux différentes catégories du site, et à droite, un panneau type Wordpress donne accès aux champs à remplir ainsi qu’à l’éditeur de texte principal.</p>
<p><img src="/img/blog/2018-06-15/forestry1.jpg" alt="L'admin de Forestry" title="L'admin de Forestry"></p>
<p>Dans mon cas, Forestry a fait un travail impeccable pour trouver mes articles de blog et mes projets, mais il a été un peu plus limité pour trouver le contenu des pages. Ainsi, pour une raison inconnue, le système a fait la différence entre la page d’accueil et la page “À propos”, et ces deux pages sont considérées comme deux sections différentes.</p>
<p>À noter que malgré l’utilisation de l’API GitHub, il n’est pas possible pour l’instant d’accéder à l’historique des contenus. Il m'a cependant été possible de renommer le fichier (généré automatiquement par le CMS) afin de pouvoir bénéficier de la gestion du multi-langue de Hugo. Ainsi, en ajoutant simplement l’extension <code>.fr</code> à un slug existant, j’ai pu laisser Hugo lier automatiquement deux articles et proposer la version traduite aux visiteurs.</p>
<p><img src="/img/blog/2018-06-15/forestry2.jpg" alt="Renommage d'un fichier" title="Renommage d'un fichier"></p>
<p>Dans mon cas, un projet est configuré exclusivement grâce à l’utilisation du Front Matter. En modifiant une ou deux lignes du fichier de configuration, j’ai pu me débarrasser de l’éditeur de texte afin de me retrouver exclusivement avec les champs de configuration d’un projet. Pour les utilisateurs de Wordpress, cela revient à avoir une page composée à l’aide d’Advanced Custom Fields, mais générée grâce à un fichier YAML.</p>
<p><img src="/img/blog/2018-06-15/forestry3.jpg" alt="Édition d'un projet" title="Édition d'un projet"></p>
<h2>Avantages / Inconvénients du CMS</h2>
<p>Le premier inconvénient, qui saute aux yeux dès la première utilisation du CMS, est le côté “technique” qui reste très présent et ce malgré l’utilisation d’une interface graphique. Il faut maîtriser le nommage de fichiers afin de permettre à Hugo de s’y retrouver, et il y a parfois certains aspects qui pourraient effrayer les utilisateurs.</p>
<p>Il aurait été judicieux de permettre le nommage automatique et silencieux des fichiers d’articles par l’utilisation d’un système de slug spécifié dans un fichier de configuration, et confier la bascule de la langue via un champ de sélection dans l'interface.</p>
<p>L’ajout de comptes “utilisateurs” avec restriction de droits est réservé à l’offre payante “Business” qui est à $9 par mois et par utilisateur. Dans sa version gratuite, il est possible d’ajouter des comptes “invités” qui ont pratiquement tous les droits sur le site, ce qui peut être très problématique.</p>
<p>Il faut savoir par exemple que Forestry impose l'utilisation du format TOML pour le Front Matter et les fichiers de configuration d'un site généré par Hugo. Dans mon cas, Forestry a détecté des “menus” pour les projets, qui ne sont pas du tout utilisés. En voulant faire un test et en modifiant l’ordre des projets, il a décidé de réécrire 47 fichiers, y compris le fichier de configuration globale de Hugo (en écrivant du TOML dans un fichier YAML), ce qui m’a complètement bloqué le build. J’ai dû passer par Git pour défaire les modifications et revenir à une version antérieure qui était fonctionnelle.</p>
<p>Pour l’aspect technique, il faut considérer le fait que Forestry utilise l’API GitHub et va donc générer une multitude de commits qui ne vont certainement pas respecter votre convention. Si c’est un point crucial pour vous, cela peut être problématique.</p>
<h2>Conclusion</h2>
<p>Après avoir écrit ceci, on pourrait penser que je n'ai pas apprécié Forestry mais c'est tout le contaire. Le système possède quelques lacunes évidentes, certes, mais l'équipe qui travaille dessus est constament en train de l'améliorer. Ceci étant dit, et lorsqu’on considère sa rapidité d’installation et sa souplesse d’utilisation, je pense que Forestry est un choix solide comme système de gestion de contenu pour un site statique. Il permet ainsi de retrouver une certaine souplesse à l’administration d’un site internet tout en continuant de bénéficier de tous les avantages de rapidité, de sécurité et d'optimisations qu’offrent l'utilisation d'un générateur de sites web statiques.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>10 extensions indispensable pour VS Code</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/10-extensions-indispensable-pour-vs-code/" />
            <updated>2018-04-19T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/10-extensions-indispensable-pour-vs-code/</id>
            <content type="html">
                <![CDATA[
                <p>J'utilise <a href="https://code.visualstudio.com/">Visual Studio Code</a> comme principal éditeur de code depuis plus de deux ans maintenant. J'avais pour habitude de travailler sous Sublime Text, qui est formidable (surtout venant de Notepad++). Mais il faut reconnaître que Microsoft fourni un travail énorme pour faire de son éditeur le meilleur qui soit, et les mises à jour mensuelles montrent à quel point ils sont dédiés à la tâche.</p>
<p>C'est donc mon éditeur de choix, mais un bon éditeur ne serait rien sans de bonnes extensions. J'ai compilé une liste des 10 extensions (et un peu plus) sans lesquelles je ne pourrais pas vivre. Elles me facilitent le travail et me permettent de gagner tellement de temps sur le long terme. Les voici (sans ordre spécial) :</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=bierner.color-info">Color Info</a></strong></p>
<p>VS Code fournit une petite boîte de pré-visualisation pour les couleurs dans les fichiers CSS. Color Info permet d'avoir une bien meilleure vue de la couleur sur laquelle votre souris s'arrête, avec plein d'infos pratiques comme les valeurs CMJN ou Alpha. Elle peut même être utilisée comme color picker, ce qui est tout aussi pratique.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/color-infos.png" alt="Color Info"><p class="is-blog-img-title">Color Info</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=bierner.emojisense">:emojisense:</a></strong></p>
<p>Vous savez que <a href="/fr/blog/parlons-un-peu-des-emojis/">j'adore les emojis</a>. Cette extensions permet d'insérer directement des emojis ou des shortcodes dans vos fichiers. Par exemple, imaginons que je veuille insérer l'emoji joystick. Il suffit de commencer à taper <code>:joy</code> et une fenêtre d'auto-complétion apparaît qui permet d'insérer directement 🕹 la bonne icône. Si vous tapez <code>::joy</code> cela permet d'insérer <code>:joystick:</code> avec une pré-visualisation de l'emoji concerné. C'est parfait !</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=waderyan.gitblame">Git Blame</a></strong></p>
<p>Comme son nom l'indique, cette extension simple montre le résultat du <code>git blame</code> de la ligne active dans la barre de statut.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/git-blame.gif" alt="Git Blame"><p class="is-blog-img-title">Git Blame</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one">Markdown All In One</a></strong></p>
<p>J'adore Markdown. En fait ce site fait grand usage de ce format car il est compilé avec Hugo. Cette extension permet d'écrire du Markdown en ajoutant des raccourcis pratiques comme <code>Cmd + B</code> pour mettre un texte en gras, <code>Cmd + I</code> pour les italiques, et ainsi de suite. Très pratique !</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">Settings Sync</a></strong></p>
<p>Si comme moi vous travaillez sur plusieurs machines (parfois virtuelles), cette extension est formidable ! Elle permet d'utiliser un Gist Github pour synchroniser tous les paramètres et les extensions de votre éditeur.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight">TODO Highlight</a></strong></p>
<p>Comme son nom l'indique, elle surligne les TODOs, FIXMEs et autres mot-clés que vous configurez. Écrivez juste <code>TODO:</code> quelque part et non seulement il sera surligné, mais l'extension va aussi lister tous les mot-clés déjà écrits n'importe où dans votre projet.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/todo-highlight.png" alt="Todo Highlight"><p class="is-blog-img-title">Todo Highlight</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur">Vetur</a></strong></p>
<p>Cette extension est un véritable couteau suisse pour Vue. De l'auto-complétion aux snippets, c'est un &quot;must-have&quot; pour tout développeur front.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ban.spellright">Spell Right</a></strong></p>
<p>Un correcteur orthographique multi-langue, offline et &quot;léger&quot;. Spell Right utilise le dictionnaire intégré à votre machine pour trouver les erreurs de frappe, et peut vérifier n'importe quel (et même plusieurs à la fois) langage n'importe où dans votre projet. Attention cependant avec les gros fichiers, cela peut prendre un peu de temps pour faire la vérification. Je l'ai désactivé par défaut. C'est très facile de lui demander de faire une correction orthographique : il suffit de cliquer sur une petite icône dans la barre des tâches.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig">EditorConfig for VS Code</a></strong></p>
<p>Étrangement, VS Code ne supporte pas <a href="http://editorconfig.org/">EditorConfig</a> par défaut. Installez simplement cette application et il va automatiquement commencer à surveiller tous les fichiers <code>.editorconfig</code> qu'il rencontre.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks">Bookmarks</a></strong></p>
<p>En voici une que j'utilise constamment ! Bookmarks est une extension qui ajoute de petits marque-pages dans la gouttière de votre éditeur. C'est très pratique lorsqu'on veut basculer rapidement de position au sein d'un même fichier, ou si l'on doit se faire un petit rappel n'importe où dans un projet. J'ai configuré le mien avec les raccourcis <code>shift + cmd + =</code> (ajouter/supprimer un marque-page) et <code>shift + cmd + -</code> (marque-page suivant) et grâce à cela je peux sauter dans mes fichiers sans quitter le confort de mon clavier. Cette extension ajoute aussi un petit panneau sous l'explorateurs de fichier de VS Code qui liste tous les marque-pages actifs du projet actuel. À avoir absolument.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/bookmarks.png" alt="Bookmarks"><p class="is-blog-img-title">Bookmarks</p></div>
<h2>Mentions spéciales</h2>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=JerryHong.autofilename">AutoFileName</a></strong></p>
<p>Cette extension est plutôt basique. Tapez simplement le début du nom d'un fichier/dossier et elle va le compléter pour vous. C'est très pratique quand on doit pointer vers un fichier perdu dans les méandres de <code>node_modules</code>.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=JakeWilson.vscode-cdnjs">cdnjs</a></strong></p>
<p>La plupart du temps je gère mes injections de dépendance Javascript avec Webpack. Mais lorsque j'ai besoin de faire un mockup rapide, cette extension me fait gagner un temps fou. En utilisant la palette de commandes, vous pouvez insérer l'URL ou les balises script/style de toutes les librairies hébergées chez cdnjs. Très pratique.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/cdnjs.gif" alt="cdnjs"><p class="is-blog-img-title">cdnjs</p></div>
<p><strong>Vous avez une suggestion ?</strong></p>
<p>Voici donc quelques extensions que j'utilise tous les jours. Si vous en avez une que je devrais tester, n'hésitez pas à laisser un commentaire ou <a href="https://twitter.com/fbnlsr">envoyez-moi un tweet</a> !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Parlons un peu des emojis</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/parlons-un-peu-des-emojis/" />
            <updated>2018-01-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/parlons-un-peu-des-emojis/</id>
            <content type="html">
                <![CDATA[
                <p>Les emojis sont partout. De Twitter à Facebook Chat, ils sont devenus le <a href="http://time.com/4114886/oxford-word-of-the-year-2015-emoji/">Mot de l'Année 2015</a> pour Oxford, et on a même pu les voir dans un <a href="https://www.rottentomatoes.com/m/the_emoji_movie">film épouvantable</a>. Mais qu'en est-il en dehors des SMS et de la messagerie instantanée ? Quid de l'utilisation des emojis dans les commentaires de code ou même les messages de commit ? Voyons voir comment utiliser au mieux ces petites images marrantes.</p>
<p>Contrairement à ce que peuvent penser les gens, les emojis existent depuis bien longtemps. Le premier emoji date de 1999, et a été créé par Shigetaka Kurita, un employé de la société japonaise de télécommunications NTT Docomo. Tout d'abord utilisé uniquement au Japon, il aura fallu attendre dix ans pour que certains d'entre eux soient ajoutés à l'espace de caractères Unicode. Ainsi, en octobre 2010, le Standard Unicode 6.0 sort officiellement, et avec lui 722 emojis. Ils ne vivent pas dans leur bloc dédié cependant et sont disséminés un peu partout dans les tables Unicode. Il a fallu des années pour que de multiples ingénieurs de Google et Apple arrivent à convaincre le Comité Technique de Unicode de les ajouter. Maintenant, les emojis font partie de la vie de tous les jours.</p>
<p>Il existe même des bizarreries et autres faits marrants autour de ces petites images. Par exemple : les emojis peuvent varier d'une plateforme à une autre. À cause de ceci, l'emoji &quot;calendrier&quot; est représenté pour toujours montrer la date du 17 juillet sur les produits Apple (date représentant l'annonce de iCal en 2002). C'est ainsi que par &quot;erreur&quot;, le 17 juillet a été déclaré comme le Jour Mondial de l'Emoji.</p>
<p>Les emojis sont aussi représentés différemment entre les plateformes, et peuvent donc être <em>interprétés</em> différemment. Prenez par exemple l'emoji <code>astonished face</code>. Le premier est l'interprétation d'Apple, le second celui de Samsung.</p>
<p><img src="/img/blog/2018-01-11/emoji1.png" alt="emoji1"></p>
<p>Celle d'Apple semble plus &quot;sage&quot; que celle de Samsung, vous ne trouvez pas ?</p>
<p>Parfois, c'est le contraire. Dans cet exemple, l'interprétation de Samsung pour la <code>pouting face</code> semble moins &quot;énervée&quot; que celle de Twitter.</p>
<p><img src="/img/blog/2018-01-11/emoji2.png" alt="emoji2"></p>
<p>Mais assez d'histoire, parlons de code.</p>
<h2>Les emojis dans les messages de commit</h2>
<p>Github a popularisé le support des emojis dans leur écosystème dans un <a href="https://github.com/blog/1289-emoji-autocomplete">post de 2012</a> grâce à leur fameux raccourci &quot;<code>:</code>&quot;. Grâce à ce dernier, si vous voulez utiliser l'emoji <code>fox face</code> 🦊 quelque part dans Github (un message de commit, une issue ou un gist), vous pouvez simplement utiliser <code>:fox_face:</code> à la place et ce sera automatiquement interprété et remplacé.</p>
<p>Utiliser des raccourcis est une solution élégante face au problème des emojis non interprétés. Vous ne courrez pas le risque de casser quelque chose et même s'ils ne sont pas (ou mal) rendus, le message est toujours lisible.</p>
<p>Les emojis peuvent aussi ajouter plus de clarté aux messages de commit. Comparez ces deux séquences :</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> Fix editing user not being saved to the database
<span class="token list punctuation">-</span> Cleanup code
<span class="token list punctuation">-</span> Add the ability to edit a user
<span class="token list punctuation">-</span> Fix bad function callback on API request</code></pre>
<p> </p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> 🐛 Fix editing user not being saved to the database
<span class="token list punctuation">-</span> 📝 Cleanup code
<span class="token list punctuation">-</span> ✨ Add the ability to edit a user
<span class="token list punctuation">-</span> 🐛 Fix bad function callback on API request</code></pre>
<p>On peut tout de suite voir où les bugs ont été corrigés et où de nouvelles fonctionnalités ont été ajoutées.</p>
<p>Sur une plateforme qui ne supporte pas les emojis, cela donne ceci :</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> :bug: Fix editing user not being saved to the database
<span class="token list punctuation">-</span> :memo: Cleanup code
<span class="token list punctuation">-</span> :sparkles: Add the ability to edit a user
<span class="token list punctuation">-</span> :bug: Fix bad function callback on API request</code></pre>
<p>Clairement pas aussi fun, mais toujours totalement lisible.</p>
<p>L'industrie s'est appropriée ces raccourcis et est allé <a href="https://www.webpagefx.com/tools/emoji-cheat-sheet/">beaucoup plus loin</a> que de simples emojis. Bien sûr c'est sympa d'utiliser 🐛 pour parler de la résolution d'un bug, mais essayez d'utiliser <code>:trollface:</code> sur Slack ou Redmine. Boom, vous êtes désormais le nouveau mec cool du quartier. Mais attention à ne pas trop les utiliser, vous ne voulez pas être <em>ce gars</em>.</p>
<p><strong>Mon conseil :</strong> N'hésitez pas à utiliser des emojis dans les messages de commit, mais préférez les raccourcis. Je vous suggère aussi de ne pas trop en faire et garder une liste de quelques un pour noter les actions majeures (résolution de bug, fonctionnalité, style, nettoyage de code, etc...).</p>
<p>Si vous ne savez pas par où commencer ou voulez suggérer une ligne à suivre à votre équipe, je recommande chaudement le <a href="https://gitmoji.carloscuesta.me/">Gitmoji</a> de Carlos Cuesta. Il existe même une ligne de commande très pratique (simplement appelée <a href="https://github.com/carloscuesta/gitmoji-cli"><code>gitmoji-cli</code></a>) qui vous aidera à écrire vos messages de commit par le biais d'une interface interactive. Gitmoji est même utilisé dans la <a href="https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages">ligne directrice de contribution</a> d'Atom.</p>
<h2>Les emojis dans du code</h2>
<p>Techniquement, vous <em>pourriez</em> utiliser des emojis dans du code informatique, mais vous devez faire très attention. Les emojis sont interprétés comme des chaînes de caractères en Javascript, mais leur longueur peut varier.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string">"🐼"</span><span class="token punctuation">.</span>length         <span class="token comment">// retourne 2</span>
<span class="token string">"🇨🇦"</span><span class="token punctuation">.</span>length         <span class="token comment">// retourne 4</span></code></pre>
<p>Il ne faut pas oublier que les emojis peuvent être connectés (un peu à la manière des ligatures de Fira Code que certains apprécient tant). C'est de cette manière que vous pouvez avoir des modificateurs de couleur de peau (appelés <code>EMOJI MODIFIER FITZPATRICK TYPE-1</code>, <code>-2</code>, <code>-3</code>, <code>-4</code>, <code>-5</code> et <code>-6</code>. Je ne plaisante pas). Ou même mieux, si vous combinez les emojis 👨, 👩 et 👧 vous obtenez une famille complète 👨‍👩‍👧 ! Voyons celui-ci dans Javascript.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string">"👨‍👩‍👧"</span><span class="token punctuation">.</span>length         <span class="token comment">// retourne 5</span></code></pre>
<p>Pourquoi 5 ? Car non seulement vous obtenez la longueur de chacun des symboles dont cet emoji est fait, mais il utilise aussi deux <code>ZWJ</code> (Zero Width Joiner) comme &quot;colle&quot;. Vous pouvez voir ceci en action : copiez/collez cet emoji dans VS Code par exemple, et il vous faudra cinq fois la touche &quot;flèche&quot; pour le traverser.</p>
<p><strong>Mon conseil :</strong> N'utilisez pas d'emoji dans du code, tout simplement. Mais vous pouvez toujours les utiliser dans vos vues. Les navigateurs web ont de très bonnes capacités en ce qui concerne les emojis, et savent comment basculer vers une police qui <em>va</em> afficher votre icône &quot;pouce levé&quot;. Mais attention lors de l'utilisation de raccourcis dans des interprètes de code dans ces vues, en particulier si vous affichez des blocs de code sur votre site. Cela peut vous jouer des tours et par exemple interpréter <code>h:m:s</code> comme <code>hⓂ️️s</code>, ce qui rend le bloc inutile.</p>
<h2>Les emojis dans les commentaires</h2>
<p>Qu'en est-il donc des commentaires de code ? Mettez des emojis partout ! Autant que je sache, vous n'êtes pas susceptible de casser quelque chose en les utilisant dans les commentaires. Les éditeurs de code modernes (Atom, VS Code, Sublime, Intellij...) ont un très bon support des emojis. Ils peuvent même se rendre très utiles pour faire ressortir quelque chose.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/**
 * WARNING: Do NOT change this file.
 */</span></code></pre>
<p>Comparé avec :</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/*
 * 🛑 WARNING: Do NOT change this file.
 */</span></code></pre>
<h2>Conclusion</h2>
<p>Les emojis sont comme une épée à double tranchant. Ils nous permettent d'exprimer des sentiments complexes de manière rapide et fun. Ils sont l'extension naturelle des emoticônes que nous utilisions à l'époque faste d'IRC. Ils peuvent être utilisés comme décorateurs, ajoutant un sentiment à une phrase qui peut paraître fade au premier abord. Ils peuvent aussi être utilisés comme marqueurs pour faire ressortir quelque chose, et même devenir un outil de communication à part entière lorsqu'ils sont utilisés seuls.</p>
<p>Ceci étant dit, étant donné qu'ils ne sont pas dessinés et interprétés de manière uniforme à travers les plateformes, ils peuvent être source de mésentente. La communication repose sur la stabilité de ses moyens de propagation. Si un symbole change entre l'émetteur et le destinataire, le message n'est plus le même. En tant que caractères, ils doivent aussi être mis dans leur contexte. C'est pour cela que certains ont du être changés. Par exemple l'emoji <code>:gun:</code> 🔫 qui était représenté par un vrai pistolet est maintenant un pistolet à eau.</p>
<p>En ce qui concerne le code par contre, je suis pour l'utilisation des emojis. Pas dans le code lui même, comme je l'ai dit plus haut, mais plutôt dans les commentaires et les messages de commit. Ils embellissent le message auquel ils sont rattachés, lorsqu'ils sont utilisés comme pointeurs. Et grâce aux raccourcis, vous pouvez les utiliser sans craindre de casser quoi que ce soit.</p>
<p>Si vous voulez en savoir plus sur les emojis, je vous conseille les travaux de <a href="https://meowni.ca/">Monica Dinculescu</a>, et en particulier ses présentations.</p>
<p>Je recommande aussi le billet d'Angela Guzman sur la <a href="https://medium.com/@agzmn/the-making-of-apples-emoji-how-designing-these-tiny-icons-changed-my-life-16317250a9ee">création des emojis d'Apple</a>. Angela écrit comment elle et son mentor Raymond on designé plus de 500 emojis pendant son stage en 2008. Cela a changé sa vie, et son travail est maintenant dans les mains de millions de gens.</p>
<p>Alors allez-y et utilisez des emojis partout ! Vous améliorerez la lecture de votre travail et casserez avec la monotonie d'un écran fade rempli de lignes de code. 😄</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Ma stratégie de sauvegarde</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/ma-strategie-de-sauvegarde/" />
            <updated>2018-01-03T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/ma-strategie-de-sauvegarde/</id>
            <content type="html">
                <![CDATA[
                <p>La protection des données numériques est une industrie pesant plusieurs millions de dollars. Qu'il s'agisse de données à portée militaire, scientifique ou financière, chacune de ces industries doit se préparer en cas de perte, et planifier leur sécurité. Elles déploient parfois des mesures extrêmes, allant jusqu'à avoir leur propre (et double) lignes électriques dédiées. Mais qu'en est-il des photos du dernier barbecue de votre ami ? Ou la vidéo des premiers pas de votre petit dernier ? Voici comment j'ai appris ma leçon suite à une défaillance tragique d'un système et ce à quoi mon équipement actuel ressemble.</p>
<h2>Une défaillance catastrophique</h2>
<p>En 2008, je me suis fabriqué un NAS (<strong>Network Attached Storage</strong>) fait sur mesure à partir de vieilles pièces informatiques, un tas de disques durs de 500 GB et une copie de <strong>FreeNAS</strong>. L'OS tournait sur une petite mémoire flash montée sur IDE, et l'assortiment des disques était configuré pour utiliser du <strong>RAID5</strong>. Cela voulait dire qu'au cas où un disque venait à être endommagé, je pouvais en théorie remplacer le disque défaillant et les données se reconstruisaient automatiquement. En théorie. Car tout a bien fonctionné jusqu'à notre déménagement en 2010. Et nous avons stocké le NAS dans un carton à côté d'un haut-parleur pendant un mois. Et deux disques sont morts.</p>
<p>J'ai passé des semaines à chercher une solution pour récupérer les données perdues. Mais après un moment, j'ai dû me rendre à l'évidence. C'était en vain. Des années de photos et vidéos de famille, une collection entière de MP3, tous mes jeux vidéo... Tout était perdu, à tout jamais. Ma petite amie était en larmes et ma &quot;fierté de geek&quot; après avoir passé tout ce temps à planifier et monter ce système en prit un sacré coup. Bien sûr que j'avais des solutions de recours, d'où le RAID5, mais je ne m'étais pas préparé à une catastrophe de cette amplitude. Et lorsqu'on parle de sécurité informatique, on <em>doit</em> prévoir le pire.</p>
<p>Des années plus tard, j'ai retenu la leçon. Voici comment je gère ma vie numérique désormais.</p>
<h2>Mon setup actuel</h2>
<p>Ma stratégie actuelle est construite autour de deux choses : un nouveau NAS, que j'ai acheté et non construit, et une suite logicielle qui automatise comment les données sont gérées.</p>
<p>Le NAS que j'utilise est un <strong>Synology DiskStation DS214se</strong>. C'est une machine très simple, qui tourne grâce à un CPU double coeur cadencé à 800 MHz, avec 256 MB de RAM et deux baies pour disque dur. J'ai mis dedans <strong>deux 2 TB Western Digital Green</strong>, et configuré une seule grappe en <strong>RAID1</strong>: tout ce qui est écrit sur un disque est répliqué sur le second. Cela veut dire que je perds la moitié du stockage théorique, mais si un disque dur crame je peux le changer et les données vont se reconstruire automatiquement.</p>
<p>Le NAS est connecté à un <strong>onduleur APC</strong>. Si le courant est coupé dans mon appartement, le NAS continue de tourner et je peux le couper manuellement (et en toute sécurité) soit via son bouton physique (qui envoie une commande de PWR OFF) ou même depuis mon téléphone (mon routeur étant lui aussi connecté à l'onduleur, j'ai toujours du réseau pendant quelques minutes même sans électricité).</p>
<p>La stratégie principale de sauvegarde est gérée par un logiciel très pratique : <strong>SyncBack Free</strong>. Ce logiciel me permet de mettre en place plusieurs scénarios de sauvegarde, appelés &quot;profiles&quot;. Le profil principal est un <strong>backup physique</strong> vers un disque dur externe. Lorsque j'ai acheté le NAS Synology, j'ai pris un troisième disque dur de 2 TB qui est maintenant utilisé comme sauvegarde. C'est ma première sécurité. C'est ce qui manquait à mon système précédent. Une fois que la tâche de sauvegarde est exécutée, ce disque est stocké <strong>hors-ligne</strong> et <strong>hors-site</strong>, de manière à le protéger en cas que panne électrique. Et en cas d'incendie ou d'inondation, mes données sont sauves.</p>
<p>SyncBack lance ensuite deux autre tâches. De toutes les données perdues dans la destruction de mon précédent setup, les photos de famille furent le plus difficile à accepter. On peut toujours remplacer la musique ou les films qu'on aime par de nouveaux, car le flux de divertissement à consommer est sans fin. Mais les souvenirs s'effacent et sont impossible à récupérer. J'ai donc décidé d'ajouter une couche supplémentaire de redondance dans ma stratégie de sauvegarde en ce qui concerne les photos et je les stocke en ligne, dans mon <strong>Google Drive</strong>. SyncBack compare le contenu du dossier dans le NAS et mon Google Drive, et met à jour les données en effectuant une <strong>Vérification en Redondance Cyclique</strong> de chacun des fichiers pour voir s'ils sont identique de chaque côté.</p>
<p>Je dois noter ici que je pourrais utiliser deux applications qui existent nativement sur les NAS Synology : <strong>USB Copy</strong> et <strong>Hyper Backup</strong>. Après avoir essayé ces applications dans différents scénarios, j'ai décidé de ne pas les utiliser car soit elles stockent les données dans un format propriétaire (Hyper Backup) ou ajoutent un tas de fichier de métadonnées commençant par <code>._</code> à mes dossiers existants (USB Copy). J'apprécie le fait que si je dois récupérer mes fichiers en dehors de l'écosystème Synology, je peux utiliser une bonne vieille commande <code>cp</code> pour retrouver mes données.</p>
<h2>Mais ce n'est pas tout !</h2>
<p>Mes données sont donc dans une grappe en RAID1, et sur un disque dur externe. Et mes photos sont sauvegardées en ligne sur mon Google Drive. J'aurais pu m'arrêter là mais je me suis dit que ce n'était pas assez. Grâce à mon abonnement <strong>Amazon Prime</strong>, je peux envoyer autant de photo que je veux sur <strong>Amazon Drive</strong> sans que cela affecte mon quota. Alors profitons de cette opportunité ! Un nouveau profil SyncBack sauvegarde le contenu de mon dossier Photos sur les serveurs d'Amazon. J'apprécie le fait que mes données soient stockées chez deux fournisseurs de stockage différents. Google et Amazon ont chacun leur infrastructure.</p>
<p>Mais pourquoi s'arrêter là ? Mes photos sont stockées dans quatre endroits différents maintenant (le NAS, le disque dur externe, Google Drive et Amazon Drive). Mais qu'en est-il du reste ? Ma musique, mes documents, mes vidéos de famille ? Bien sûr, tout se trouve sur le NAS et le disque dur externe, mais je me suis dit que j'avais besoin d'une protection supplémentaire. Car jusqu'à présent toutes mes stratégies de sauvegarde reposent sur ce qui peut constituer un <strong>point individuel de défaillance</strong> : SyncBack. Si le logiciel se comporte mal ou un de mes profils de backup n'est pas bien configuré, je peux me retrouver avec rien d'autre qu'une mauvaise sauvegarde à plusieurs endroits. Je n'ai aussi pas accès au disque dur externe aussi facilement, ce qui fait que si j'ai besoin de faire une sauvegarde à n'importe quel moment, je dois me préparer au moins un jour à l'avance.</p>
<p>C'est la raison pour laquelle j'ai pris un abonnement à <strong><a href="https://c2.synology.com/en-us">Synology C2</a></strong>. C'est un service totalement intégré qui tourne nativement sur <strong>DSM</strong> (<em>DiskStation Manager:</em> le système d'exploitation de Synology) et qui permet d'envoyer l'intégralité du NAS (moins les films et séries TV, car ce n'est pas très important) vers les serveurs de Synology. Il utilise l'AES-256 pour chiffrer les données en local avant de les envoyer sur le réseau. Je l'ai configuré pour qu'il fasse une sauvegarde automatique tous les lundi, et une vérification de l'intégrité des données deux jours plus tard.</p>
<p>J'ai aussi considéré l'offre de Online <a href="https://www.online.net/en/c14">C14</a>, car elle est très peu chère et on peut envoyer les fichier via (S)FTP mais malheureusement ils ne supportent pas Synology.</p>
<h2>Évolutions possibles</h2>
<p>Voici donc à quoi ressemble mon setup actuel :</p>
<div class="is-blog-img"><img src="/img/blog/2018-01-03/backup-strategy.jpg" alt="Mon setup actuel"><p class="is-blog-img-title">Mon setup actuel</p></div>
<p>Chaque fichier est stocké physiquement sur jusqu'à 6 endroits différents, avec différents niveaux de sécurité.</p>
<p>Est-ce que ce setup est parfait ? Bien sûr que non. Tout d'abord, tout ceci manque d'automatisation. Je dois toujours démarrer chacune des tâches de sauvegarde à la main (à part celle qui envoie les données vers C2), et je ne suis pas à l'abris d'une erreur. Je travaille avec des données vivantes, donc la grappe est en constante évolution, mais c'est une sauvegarde, pas un stockage froid. Et le disque dur externe que j'utilise doit être transporté et manipulé, c'est donc un point de défaillance supplémentaire.</p>
<p>Une chose que je risque changer dans peu de temps est le modèle des disques que j'utilise. les WD Green sont &quot;bien&quot; mais ils ne sont pas fait pour être utilisé dans un NAS. Je pense donc les remplacer par soit des WD Red ou des Seagate Ironwolf, et peut-être en profiter pour faire une mise à jour de capacité vers 3 ou 4 TB.</p>
<p>L'un dans l'autre, le problème principal avec les stratégies de sauvegarde est qu'elles ne sont jamais parfaites. Regardez ce qui <a href="https://techcrunch.com/2017/02/01/gitlab-suffers-major-backup-failure-after-data-deletion-incident/">s'est passé chez GitLab</a> il y a quelques mois, ou la catastrophe qui a <a href="https://www.theregister.co.uk/2017/07/13/watercooling_leak_killed_vnx_array/">mis OVH sur les rotules</a> pendant plusieurs heures.</p>
<p>Personne ne peut réellement faire face à une perte de données. Ceci dit, je peux dire que je me sens <em>confiant</em> avec cette stratégie, et j'ai essayé de penser à tous les scénarios possibles (même les vents solaires, mais c'est une tout autre histoire). Nous verront bien où se trouvent mes données dans quelques années.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Adieu LastPass, bonjour KeePassXC!</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/adieu-lastpass-bonjour-keepassxc/" />
            <updated>2017-10-06T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/adieu-lastpass-bonjour-keepassxc/</id>
            <content type="html">
                <![CDATA[
                <p>Depuis des années, je suis un fervent défenseur de l'utilisation de gestionnaires de mots de passe. Vous pouvez demander à pratiquement tous les membres de ma famille ou mes amis, il existait une époque où je <em>devais</em> poser la question : &quot;Au fait, tu utilises quoi comme gestionnaire de mots de passe ?&quot;. C'était le plus souvent suivi par un monologue d'une vingtaine de minutes où je décrivais combien la protection de leur vie numérique était désastreuse et qu'ils <strong>devaient</strong> utiliser un gestionnaire. Bien sûr, c'était sans compter les moments où j'apprenais que mon ami ou parent utilisait le même mot de passe de 7 caractères de long pour <strong>absolument tout</strong> et que &quot;c'est <em>hunter2</em>, c'est pas grave si tu le sais, je n'ai rien à cacher&quot;. Oui oui, c'est arrivé.</p>
<p>Durant les quatre dernières années environ, mon gestionnaire de choix était <a href="https://lastpass.com">LastPass</a>. J'étais un membre Premium comblé pendant tout ce temps, et même s'il leur est arrivé quelques tracas (comme en <a href="https://blog.lastpass.com/2015/06/lastpass-security-notice.html/">2015</a> ou en <a href="https://www.theguardian.com/technology/2017/mar/30/lastpass-warns-users-to-exercise-caution-while-it-fixes-major-vulnerability">2017</a>), ils ont toujours été transparents sur la situation à chaque fois et j'ai continué à leur faire confiance. J'avais pour habitude d'utiliser une authentification double facteur : mon mot de passe et une <a href="https://www.yubico.com/">Yubikey</a>, et tout a fonctionné parfaitement pendant des années. Cependant, après une très longue considération, j'ai récemment pris la décision de dire au revoir à LastPass et continuer le voyage avec une autre solution. Voici pourquoi.</p>
<h2>Un vieux char peint en rose reste un vieux char</h2>
<p>Vous voyez, le problème avec la sécurité informatique c'est qu'il faut toujours trouver le bon équilibre entre la sûreté et le confort au jour le jour. Mon setup était quelque peu sans danger, mais le confort n'était plus au rendez-vous. Je dois souvent me connecter à mon compte LastPass plusieurs fois par jour, car j'utilise plusieurs navigateurs web différents, avec parfois même plusieurs profils par navigateur. Ainsi, étant donné que LastPass déconnecte un utilisateur automatiquement lorsque ce dernier ouvre une session ailleurs, je devais aussi utiliser ma Yubikey plusieurs fois par jour, voire même par heure. Sur la fin, cela devenait un véritable calvaire. De plus, LastPass a procédé à plusieurs modifications sur l'UI du système, et se concentre de plus en plus à devenir très facile à utiliser, ce qui rend l'app un tant soit peu &quot;opaque&quot;. D'un côté c'est une bonne chose car cela attire de plus en plus d'utilisateurs à être en sécurité sur le web, d'un autre cela peut engendrer de véritables problèmes sous le capot. Souvent, l'extension navigateur n'arrive pas à attraper les bons champs de connexion ou ne sais pas retrouver les bons combos login/mot de passe. Ou lorsque j'utilisais le générateur de mot de passe, il n'enregistrait pas les informations correctement. L'app a aussi des difficultés avec les sites qui utilisent des requêtes AJAX, et elle sauve souvent des URLs compliquées qui sont utilisées lors de la procédure d'enregistrement, ce qui rend beaucoup d'entrées en base de données sales. Si vous n'êtes pas technophile, c'est toujours un produit superbe, et c'est <strong>bien mieux</strong> que de ne rien utiliser ou d'avoir ce vieux post-it collé à l'écran de votre ordinateur. Mais pour moi, ce qui était un outil très pratique est devenu un poids. Il était temps pour quelque chose de nouveau, ou dans ce cas, quelque chose de vieux.</p>
<h2>Entre en scène KeePassXC</h2>
<p><a href="https://keepass.info">KeePass Password Safe</a> existe depuis 13 ans. Son interface trahit son âge mais sa facilité d'utilisation et les fonctionnalités de sécurité que cette application propose ont été éprouvé plusieurs fois. Au delà du combo habituel utilisateur / mot de passe, il est possible de créer des champs personnalisables, et même de rattacher des fichiers à la base de données. D'ailleurs, KeePass utilise les algorithmes AES, TwoFish ou encore ChaCha20 pour chiffrer cette dernière, et les mots de passe qu'elle contient sont protégés en mémoire. Le programme fait à peu près tout ce que fait LastPass (ou dans l'autre sens en fonction de votre point de vue), sans l'interface moderne et le côté &quot;automatique&quot; des enregistrements.</p>
<p>La version de KeePass que j'utilise s'appelle <a href="https://keepassxc.org">KeePassXC</a>. C'est un <em>fork</em> communautaire du défunt KeePassX, qui était à la base une version multi-plateforme de KeePass. L'app fonctionne donc parfaitement sur Windows, MacOS et Linux. Exporter la base de données depuis LastPass et l'importer dans KeePassXC demande un peu de travail, car il faut faire avec un fichier CSV pas très propre généré par LastPass (pensez à détruire ce fichier une fois l'opération terminée !). Les &quot;notes sécurisées&quot; de LastPass sont stockées dans un format ressemblant à du XML, il vous faudra donc probablement les réécrire à la main dans KeePassXC après l'import. Mais une fois cette tâche fastidieuse effectuée, l'utilisation de KeePass est très facile, et le logiciel fonctionne parfaitement. Dès que vous souhaitez récupérer un mot de passe, il suffit simplement de basculer vers l'application, utiliser Cmd+F, taper les premières lettres de l'entrée que vous cherchez et un simple Cmd+C / Cmd+V fait l'affaire. Cela prend littéralement moins de cinq secondes. Il existe même des extensions navigateur qui peuvent aller chercher automatiquement les informations d'identification correspondant au site visité, ce qui rend le process identique à LastPass, et même plus rapide.</p>
<p>Le setup que j'utilise en ce moment est comme suit. La base de données de mots de passe est stockée dans mon Google Drive. Cela me permet d'avoir une synchronisation silencieuse entre mes machines et me fournit une sauvegarde en ligne. J'utilise à la fois un Mot de Passe Maître (avec une entropie de 100+ bits) et un fichier-clé (qui est stocké localement, <strong>pas</strong> sur Google Drive) pour déverrouiller la base. Le programme peut communiquer avec mes navigateurs internet grâce à deux extensions : chromIPass et PassIFox. Le fichier de base de données et le fichier-clé sont sauvegardés sur un NAS, et deux copies <em>offline</em>.</p>
<h2>Tout va bien ?</h2>
<p>Je suis donc enfin heureux à nouveau. Le &quot;problème&quot; principal que j'ai avec KeePassXC reste son interface utilisateur. En ces jours où tout le monde jure par le Flat UI ou les lignes Material Design, le logiciel montre vraiment son âge. Il lui manque aussi des fonctionnalités basiques comme la sélection des champs affichables dans les colonnes d'entrées, et ma plus grande plainte : les champs personnalisables ne s'affichent pas lorsque l'on cherche une entrée spécifique. Imaginons par exemple que vous créez une entrée pour une carte de crédit. Elle peut contenir plusieurs champs personnalisables comme son numéro, sa date d'expiration et le cryptogramme visuel. KeePassXC ne montrera pas ces champs sauf si vous cherchez à éditer l'entrée pour pouvoir les afficher, ou si vous faîtes un clic droit sur l'entrée et sélectionnez l'une des options &quot;Copie d'attributs&quot;. Cela aurait été bien plus efficace si chaque entrée pouvait s'afficher dans son intégralité sur une seule ligne, en omettant les champs protégés bien évidemment.</p>
<p>Je devrais faire mention de l'excellent <a href="https://keeweb.info/">KeeWeb</a>, une app web (disponible sous la forme d'une app de bureau basée sur Electron aussi), qui essaie de donner un coup de jeune à KeePass à ce niveau. Mais cette app pose encore plus de problème que KeePass d'un point de vue sécurité. Je ne rentrerai pas dans les détails dans ce billet, mais les défauts sont faciles à deviner.</p>
<p>Au final, je préfère utiliser KeePassXC à LastPass. Bien sûr, mon utilisation n'est pas la plus sécurisée, en particulier en stockant le fichier de base de données dans le cloud. Je pourrais arranger ceci en utilisant un système auto-géré comme OwnCloud ou Bitorrent Sync. Mais encore une fois, l'équilibre entre la sécurité et l'usage quotidien aurait été perdu. J'aimerais que KeePassXC puisse supporter le TOTP afin de me permettre d'utiliser à nouveau ma Yubikey et me débarrasser de ce fichier-clé, mais pour l'instant cette solution me satisfait.</p>
<p>Au fait, vous utilisez quoi comme gestionnaire de mots de passe ?</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Notifications push : stop au bruit</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/notifications-push-stop-au-bruit/" />
            <updated>2017-09-26T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/notifications-push-stop-au-bruit/</id>
            <content type="html">
                <![CDATA[
                <p>Depuis l'été dernier, la première chose que je fais lorsque j'installe une nouvelle app sur mon smartphone est d'aller dans ses paramètres et de totalement couper les notifications.</p>
<p>Lorsqu'Apple a présenté les notifications push en 2009, c'était un miracle. Il n'était plus nécessaire de vérifier son téléphone pour voir si un nouvel e-mail était là ! Lorsque quelque chose d'important arrivait, un subtil <em>Ding !</em> ou une petite vibration nous alertait d'un tweet important ou d'un rendez-vous. C'était un excellent moyen pour l'utilisateur de rester connecté avec son monde numérique.</p>
<p>Le problème est que maintenant, la moindre app pense qu'elle est le centre du monde et vous devez écouter ce qu'elle a à dire. Félicitez Hélène pour son nouveau boulot ! Sophie tweet à propos de #balancetonporc ! N'oubliez pas de surveillez vos oeufs sur Joyeux Dragons ! Vincent a posté pour la première fois depuis un moment ! C'est devenu si répétitif qu'il semble que maintenant les notifications ne sont rien plus que du bruit. À tel point que certaines personnes ont commencé à développer de nouvelles maladies comme le <a href="https://en.wikipedia.org/wiki/Phantom_vibration_syndrome">Syndrome de la Vibration Fantôme</a>. Combien de fois vous est-il arrivé de penser avoir reçu une notification pour vous rendre compte que c'est votre cerveau qui vous joue des tours. Il y a même des gens qui ressentent de l'anxiété liées aux notifications. Le flux constant d'informations est devenu tellement écrasant, et il ne s'arrête jamais. Et maintenant, il est même possible de recevoir des notifications push depuis des sites internet. J'ai beau adorer la technologie derrière tout ça, elle est inexorablement mal utilisée par les dévelopeurs à travers le monde. Désormais, tous les sites demandent votre autorisation pour vous envoyer des notifications à propos de la dernière mode concernant leur sujet de prédilection. C'est présenté comme un moyen de fournir un service plus efficace mais ne nous voilons pas la face : elles existent dans le simple but de vous faire revenir et vous montrer quelques publicités de plus.</p>
<p>L'an dernier je me suis offert une Pebble. C'est un gadget très sympa qui se met autour de mon poignet et qui vibre dès que j'ai une notification. Grâce à cette montre, je ne peux plus manquer un seul e-mail, appel ou texto. C'est aussi pratique car c'est discret. Cela fait maintenant un an que la sonnerie de mon téléphone n'a plus sonné, et maintenant qu'il est toujours sur silence, il ne dérange plus mes collègues lorsque je suis dans un open-space, ou ma famille pendant le déjeuner du dimanche. Je me suis mis à moins consulter mon téléphone, car étant donné qu'il est connecté à ma montre via Bluetooth, je ne peux pas louper une seule notification tant que mon poignet est à moins de dix mètres de mon téléphone. Mais je me suis rendu compte que c'était à mon dépend. Le problème est que je me suis mis à consulter ma montre de manière constante. Dès que mon téléphone a besoin de mon attention, je reçois trois petites tapes sur mon poignet pour me rappeler qu'il est là, et qu'il a besoin de moi. C'était marrant au début, mais c'est devenu comme un collier par la suite, et ce lien Bluetooth commençait à ressembler plus à une laisse qu'à un outil.</p>
<p>J'ai donc décidé de tout couper. Les seules notifications que j'ai conservé sont les e-mails, et tout ce qui est <em>directement</em> dirigé vers moi (Telegram, les messages Twitter et les textos). C'est tout. LinkedIn est silencieux, Instagram aussi, et je jetterai un coup d'oeil à mes livraisons Amazon quand je veux. Sur mon PC de bureau, j'ai désactivé toutes les notifications aussi. Maintenant je regarde mon dock de temps en temps, et lorsque je vois une puce sur Slack ou Airmail, je les consulte quand je n'ai rien d'autre à faire. Je ferme complètement mon client mail et je l'ouvre avant le déjeuner ou avant de quitter le bureau. Je ne veux plus interrompre une session de code car Aliexpress m'informe que ma commande est partie ou parce que quelqu'un a répondu à une PR Github. Et c'est merveilleux.</p>
<p>Couper les notifications a été une grande réalisation. J'ai l'impression d'avoir un meilleur contrôle sur ma vie numérique. Je suis de moins en moins interrompu par tout ce bruit, et au final je me sens moins stressé par ce flux constant d'information. Je me sens plus heureux, mais pas moins &quot;connecté&quot;. Je choisis d'ouvrir une application car j'ai envie de le faire et non pas parce qu'un algorithme a décidé qu'il était temps que je le fasse. Un article récent <a href="https://www.wired.com/story/turn-off-your-push-notifications/">paru sur Wired</a> montre qu'en 2013, &quot;<em>Apple a annoncé fièrement [...] que 7,4 mille milliards de notifications push ont été envoyées par leurs serveurs</em>&quot;. Où se trouve la limite entre l'information et le spam ?</p>
<p>J'espère que cette mode va se développer et les gens vont couper les notifications de plus en plus. Malheureusement, maintenant qu'Apple a a jouter le support de la SIM dans sa dernière Apple Watch, la &quot;machine à notifications&quot; est devenue indépendante, et les gens vont donc recevoir encore plus de rappels pour regarder leur téléphone. Je vous invite à essayer pendant une semaine et voir si cela vous manque d'avoir un appareil qui bourdonne constamment dans la poche. Je parie que vous vous sentirez mieux, dès le troisième jour.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>18-55 Productions</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/18-55-productions/" />
            <updated>2017-02-21T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/18-55-productions/</id>
            <content type="html">
                <![CDATA[
                <p>La semaine dernière nous avons lancé le nouveau site web de <strong><a href="http://18-55.fr">18-55 Productions</a></strong> !</p>
<p><strong>18-55</strong> est une société de production audiovisuelle basée à Bordeaux. Ils travaillent avec plusieurs marques comme <strong>DC Shoes</strong>, <strong>Orange</strong> ou encore <strong>Electronic Arts</strong> parmi tant d'autres. Leur but était d'avoir un site qui puisse mettre en avant son contenu (leurs productions) et en même temps servir de plateforme pour les différentes professions qui tournent autour de leur domaine. C'est la raison pour laquelle ils s'entourent de divers artistes, photographes, rédacteurs et réalisateurs ce qui leur permet de mettre rapidement une équipe de divers talents sur pied pour répondre au mieux aux projets sur lesquels ils interviennent.</p>
<p>Leur second désir était d'avoir un back-office qui leur permette d'ajouter / supprimer du contenu rapidement et facilement. Wordpress a été le choix évident pour ce projet, et nous sommes arrivés ensemble à un design basé sur une grille de carrés ce qui leur permet de réorganiser l'intégralité du site comme bon leur semble. Ils furent tout de suite emballés par l'idée, et après quelques échanges avec le gérant et la chargée de production nous nous sommes mis d'accord sur ce design. Je suis très content du résultat et depuis le lancement le feedback est très positif.</p>
<p>Merci beaucoup à <strong>18-55</strong> pour leur temps et leur investissement dans ce projet !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Mon affreux rythme de sommeil</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/mon-affreux-rythme-de-sommeil/" />
            <updated>2017-02-07T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/mon-affreux-rythme-de-sommeil/</id>
            <content type="html">
                <![CDATA[
                <p>Il y a quelques mois je me suis offert une <a href="https://www.pebble.com/">Pebble</a>. C'est un gadget très sympa qui agit comme une extension à mon téléphone, directement attaché à mon poignet. Grâce à elle je peux envoyer des messages pré-enregistrés en cliquant sur quelques boutons, mais je peux surtout recevoir des notifications de nouveaux messages, que ce soit des emails, des SMS ou même des messages Telegram. Ce qui peut être vite fatiguant quand mon ami Édouard me spamme de paroles du Wu-Tang. Mais cela reste un gadget vraiment formidable, et j'en suis très content.</p>
<p>Une autre fonctionnalité très cool est la possibilité de tracker mon rythme de sommeil. Grâce à son gyroscope intégré, elle peut détecter les mouvements de mon poignet et en déduire si je dors ou pas. Bon OK ce n'est pas fiable à 100%, mais cela donne une bonne indication générale sur l'état de mes habitudes de sommeil. Et elles sont désastreuses.</p>
<div class="is-blog-img"><img src="/img/blog/2017-02-07/sleeping-patterns.png" alt="Mon rythme de sommeil la semaine passée"><p class="is-blog-img-title">Mon rythme de sommeil la semaine passée</p></div>
<p>D'après ma Pebble, je dors 7 heures et 3 minutes par nuit sur une moyenne de 30 jours, avec une durée de sommeil profond d'environ 1h 48m. Ce n'est pas <em>si</em> terrible, mais quand on voir que sur une même semaine je peux dormir 3h 55m un jour et 9h 18m un autre, il y a clairement quelque chose qui ne tourne pas rond dans mes horaires de sommeil.</p>
<p>Ma &quot;pire&quot; nuit de la semaine est celle entre le dimanche et le lundi, avec une moyenne de 5 heures de sommeil. Ma &quot;meilleure&quot; nuit est en générale celle du samedi au dimanche avec jusqu'à 11 heures (!) de sommeil.</p>
<p>Un humain normalement constitué a besoin de dormir <a href="https://sleepfoundation.org/sites/default/files/SleepTimeRecommendations012615%5B1%5D-page-001_0.jpg">entre 7 et 8 heures</a> pour des fonctions optimales, ce qui veut dire que j'ai une <a href="http://www.health.harvard.edu/staying-healthy/repaying-your-sleep-debt">dette de sommeil</a> de 30 minutes à une heure <em>par jour</em>. Cela fait une dette cumulée d'environ 4 à 5 heures par semaine. Sur le long terme, une telle privation peut causer du stress, un gain de poids, de la fatigue, et même du diabète. Si vous voulez plus d'information sur les bienfaits du sommeil, je vous conseille de <a href="https://pillowpicker.com/health-wellbeing/benefits-of-sleep/">lire cet article</a> de Pillow Picker.</p>
<blockquote>
<p>&quot;Mais Fabien, tu n'as qu'à aller au lit une heure plus tôt, c'est aussi simple !&quot;</p>
</blockquote>
<p>Oui, mais c'est sans compter la capacité sans limite que le cerveau humain a pour glander.</p>
<div class="is-blog-img"><img src="/img/blog/2017-02-07/chuck-and-beans.jpg" alt="Vive le café"><p class="is-blog-img-title">Vive le café</p></div>
<p>Dormir plus a été un but pour moi en 2017. J'ai déjà pris des mesures pour y arriver, notamment en enlevant ma tablette tactile de la table de nuit et en la remplaçant par <a href="https://www.amazon.com/dp/1421586207/">un bon bouquin</a>. Mais j'ai quand même besoin d'une bonne tape sur la main et il faut que j'arrête de passer mes soirées à me documenter ou me promener dans les rues de <a href="https://en.wikipedia.org/wiki/The_Elder_Scrolls_V:_Skyrim">Tamriel</a> ou de <a href="https://en.wikipedia.org/wiki/Counter-Strike:_Global_Offensive">Dust2</a>.</p>
<p>J'espère pouvoir écrire une suite à ce billet bientôt, décrivant comment mes habitudes de sommeil ont changées, si j'arrive à les changer un jour. En attendant, je vais aller faire une petite sieste.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Gitlab a crashé, à qui la faute ?</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/gitlab-a-crashe-a-qui-la-faute/" />
            <updated>2017-02-02T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/gitlab-a-crashe-a-qui-la-faute/</id>
            <content type="html">
                <![CDATA[
                <p>La nuit dernière, un &quot;admin système fatigué&quot; a supprimé un dossier sur le mauvais serveur de la plateforme d'hébergement de code Gitlab, effaçant plus de 300 GB de données. Suite à cet incident, le site internet a été coupé pendant la manoeuvre de restauration. La blague : &quot;aucune [de leur] technique de sauvegarde / réplication ne fonctionnait correctement ou était active à la base.&quot; Il leur a fallu une journée entière pour réparer les dommages, laissant des milliers de devs échoués, et perdant 6 heures de données.</p>
<p>Sur Twitter, Gitlab a posté le message le plus formidable et rageant que n'importe quel admin système peut poster dans sa carrière :</p>
<blockquote>
<p>We accidentally deleted production data and might have to restore from backup.</p>
</blockquote>
<p>Pour être honnête, il ne peut pas y avoir de catastrophe plus désastreuse pouvant arriver à une société donc le fond de commerce repose sur la donnée. L'équipe avec laquelle je travaille actuellement utilise Gitlab depuis plusieurs mois, et nous avons déjà à souffrir de leurs temps d'arrêt régulier. À un moment, <a href="http://Gitlab.com">Gitlab.com</a> pouvait se retrouver hors ligne trois à cinq fois par jour. Nous étions ravis d'apprendre qu'ils cherchaient à <a href="https://about.gitlab.com/2016/12/11/proposed-server-purchase-for-gitlab-com/">quitter le cloud</a>, pour apprendre seulement qu'au final ils vont étendre leur cloud initial.</p>
<p>Comme l'utilisateur <em>connorshea</em> le précise sur Hacker News :</p>
<blockquote>
<p>Ce n'est jamais la faute d'une personne, toujours d'un système.</p>
</blockquote>
<p>Cela pourrait être appliqué à GitLab. C'est formidable que pour de tâches aussi critiques (on ne supprime pas une base de données entière tous les jours), ils n'utilisent pas de système de gestion aussi simple et efficace que des listes de contrôle. Elles sont utilisées autour du monde dès qu'un système encours un risque. Les pilotes de ligne, les chirurgiens et les infirmières, les militaires, tous ont des listes de contrôle. C'est la raison pour laquelle on voit les pilotes de ligne parler durant une procédure d'urgence, leur co-pilote confirmant tout ce qu'ils font. Cela se passe le plus souvent comme ceci :</p>
<blockquote>
<p>– &quot;Je vais couper la clim.</p>
<p>– OK pour couper la clim.</p>
<p>– La clim est coupée.</p>
<p>– Confirmé. La clim est coupée&quot;.</p>
</blockquote>
<p>C'est grandement exagéré et la dernière fois que j'ai piloté un avion c'était... euh, jamais. Mais vous voyez ce que je veux dire. Chacune des étapes est surveillée et confirmée par un coéquipier. Si vous voulez en savoir plus vous pouvez lire cet article Wikipedia sur la <a href="https://en.wikipedia.org/wiki/Crew_resource_management">gestion des ressources d'équipe</a>.</p>
<p>Gitlab a choisi la politique de la transparence totale, allant jusqu'à diffuser en direct leur procédure de sauvegarde. Beaucoup les félicitent d'avoir pris une telle position, mais ils pouvaient difficilement en prendre une autre, car il y existe une possibilité non  négligeable d'un échec insurmontable.</p>
<p>Au final, la vraie question n'est pas vraiment de savoir &quot;à <em>qui</em> la faute&quot;, mais plutôt &quot;à <em>quoi</em> la faute ?&quot; Une mauvaise pratique, un manque de préparation et la frustration d'un ingénieur fatigué font un mauvais cocktail. Je suis persuadé que Gitlab va grandir de ses erreurs. Je suis moins sûr quant à leur gestion de crise et sur le fait qu'ils perdent des clients chaque minute, mais bon on ne peut pas leur reprocher de vouloir sauver les meubles.</p>
<p>En souvenir de ce jour, <a href="https://twitter.com/CyberShambles">@CyberShambles</a> a dédié le 1 février à la <a href="http://checkyourbackups.work/">journée de la sauvegarde</a>. Pas mal.</p>
<hr>
<p>Source: <a href="https://www.theregister.co.uk/2017/02/01/gitlab_data_loss/">https://www.theregister.co.uk/2017/02/01/gitlab_data_loss/</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Nommer ses tables : singulier vs. pluriel</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/nommer-ses-tables-singulier-vs-pluriel/" />
            <updated>2017-01-25T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/nommer-ses-tables-singulier-vs-pluriel/</id>
            <content type="html">
                <![CDATA[
                <p>L'autre jour, pendant un poker planning, la question du nommage d'une table a été posée. Pendant la conversation, un de nos dévelopeurs a suggéré que la table devait avoir un nom au singulier, pendant que d'autres pensaient que toutes les tables devaient être au pluriel. Cela m'a amené à poser la question : Y'a-t-il un choix meilleur qu'un autre ? Est-ce-que les tables doivent être au singulier ou au pluriel ?</p>
<p>Il est intéressant de voir que malgré le fait qu'il n'y a pas de convention claire sur ce sujet, c'est une question qui est posée assez souvent, et elle est la source de grands débats dans le monde des bases de données.</p>
<h2>Utiliser le pluriel</h2>
<p>Narayana &quot;Vyas&quot; Kondreddi (un ingénieur SQL et Architecte de bases de données réputé) écrivait en 2001 :</p>
<blockquote>
<p>Les tables représentent les instances d'une entité. Par exemple, vous stockez toutes les informations de vos clients dans une table. Ici, un &quot;client&quot; est une entité et toutes les lignes dans la table clients représentent les instances de cette entité &quot;client&quot;. Ainsi, pourquoi ne pas nommer votre table en utilisant l'entité qu'elle représente, le &quot;client&quot; ? Étant donné qu'elle stocke &quot;plusieurs instances&quot; des clients, utilisez le pluriel pour le nom de votre table.</p>
</blockquote>
<p>Cela semble logique, et quelque peu &quot;naturel&quot;. Vous stockez plusieurs clients dans une seule table (ces &quot;instances multiples&quot;), donc la table devrait être naturellement nommée <code>clients</code>. Cela a du sens aussi lorsque l'on écrit une commande SQL. Lorsque vous voulez naviguer à travers tous vos clients, vous utilisez <code>SELECT * FROM clients</code>...</p>
<p>En utilisant un nom au pluriel, on peut considérer la table comme une caisse contenant plusieurs objets. Une caisse de pommes devrait être étiquetée <em>Pommes</em>, qu'elle en contienne une seule ou une centaine.</p>
<h2>Utiliser le singulier</h2>
<p>Les gens qui plébiscitent l'utilisation du singulier prennent souvent l'exemple que lorsqu'on pointe sur un enregistrement en base, cela semble souvent déroutant d'utiliser un mot au pluriel pour décrire un objet unique. Ainsi, pourquoi utiliser <code>clients.name</code> lorsqu'on pointe vers un seul client ?</p>
<p>Cela est d'autant plus mis en avant lors de l'écriture de commandes SQL, qui apparaissent alors plus naturelles pour certains. Par exemple, <code>SELECT activity.name</code> a plus de sens que <code>SELECT activities.name</code>.</p>
<p>Cela permet aussi d'éviter la confusion d'une pluralisation  anglaise qui peut parfois ne pas être évidente pour les personnes ne parlant pas courament la langue de Shakespeare. Par exemple, <em>activity</em> devient <em>activities</em> où <em>person</em> devient <em>people</em> et <em>data</em> reste <em>data</em>...</p>
<p>En utilisant le singulier, on peut aussi considérer la &quot;théorie de l'ensemble&quot;, qui dit que chaque instance de cet ensemble en est représentante, donc une table <code>pomme</code> est l'ensemble Pomme. Elle est agnostique au nombre de pommes qu'il y a dans l'ensemble.</p>
<h2>Que devez-vous utiliser ?</h2>
<p>Je pense personellement qu'une table de base de données est la représentation d'une collection d'objets. À ce titre, elle doit être nommée en utilisant le pluriel, étant donné qu'elle stocke plusieurs copies de cette entité. Ainsi, chaque <code>user</code> est stocké dans la table <code>users</code>.</p>
<p>La seule chose qui aurait pu me faire considérer l'utilisation du singulier est l'argument des commandes SQL, car cela semble moins naturel d'utiliser un nom pluriel pour une requête sur un seul objet. Si cela vous dérange tellement, vous pouvez utiliser cette astuce pour contourner le problème :</p>
<pre class="language-SQL"><code class="language-SQL">SELECT id, name, description FROM products product WHERE product.name = 'foo' AND product.description = 'bar'</code></pre>
<p>C'est même plus pratique lorsqu'on a affaire à une pluralisation complexe :</p>
<pre class="language-SQL"><code class="language-SQL">SELECT id, name, description FROM activities activity WHERE activity.name = 'foo' AND activity.description = 'bar'</code></pre>
<p>Et si vous êtes vraiment préoccupé à l'écriture d'un code propre qui ressemble à de l'anglais, mon conseil est d'utiliser le pluriel pour les noms des tables, mais le singulier pour leurs entités correspondantes. Ainsi pour une déclaration d'entité dans Symfony vous pouvez utiliser :</p>
<pre class="language-php"><code class="language-php"><span class="token comment">/**
 * @Activity
 *
 * @ORM\Table(name="activities")
 */</span>
<span class="token keyword">class</span> <span class="token class-name-definition class-name">Activity</span>
<span class="token punctuation">{</span>
<span class="token operator">...</span>
<span class="token punctuation">}</span></code></pre>
<p>La table <code>activities</code> stocke toutes les instances d'un possible object <code>Activity</code>. Ainsi, lors de l'invocation et de l'interraction avec cet objet, cela a du sens d'utiliser la syntaxe <code>$activity = new Activity()</code> et <code>$activity-&gt;setName(&quot;foo&quot;)</code>.</p>
<h2>En résumé</h2>
<p>Si je devais résumer à un conseil cependant, ce serait ceci : <strong>soyez consistants</strong>. Restez sur le choix que vous avez décidé, et appliquez-le durant tout le projet. Utilisez ce qui vous semble le plus naturel pour vous et votre équipe. Avoir la possibilité de se reposer sur une convention est souvent une bonne chose, mais parfois ce n'est pas suffisant. Ce qui est le plus important est de faire un choix clair et de l'adopter. Bien sûr, il est aussi important de garder une oreille attentive aux arguments que d'autres pourraient vous fournir.</p>
<p>Si vous cherchez encore des conseils, voici quelques astuces qui pourront vous aider lors du choix d'un nom de table :</p>
<ul>
<li>Utilisez des noms courts pour vos tables</li>
<li>Utilisez des underscores pour séparer les mots (ni espaces, ni camelCase). Par ex : <code>product_dimensions</code></li>
<li>Essayez d'être un tant soit peu descriptif : une table doit toujours représenter son contenu. Il est plus facile de lire <code>client_bills</code> que <code>clb</code></li>
<li>Utilisez des noms uniques qui ne font pas de collision avec les mots réservés par SQL/SGBD (évitez <code>name</code>, <code>order</code>, <code>percent</code>...)</li>
<li>N'utilisez pas le nom de la table suivi par &quot;id&quot; (ex : <code>clients_id</code>) comme clé primaire. <code>id</code> est largement suffisant et tout le monde comprendra</li>
<li>Utilisez <code>id</code> et non <code>ID</code></li>
<li>D'ailleurs, n'utilisez jamais de lettre capitale dans le nom de vos tables. Jamais.</li>
</ul>
<hr>
<p>Sources :</p>
<ul>
<li><a href="http://stackoverflow.com/questions/338156/table-naming-dilemma-singular-vs-plural-names">http://stackoverflow.com/questions/338156/table-naming-dilemma-singular-vs-plural-names</a></li>
<li><a href="http://stackoverflow.com/questions/7899200/is-there-a-naming-convention-for-mysql">http://stackoverflow.com/questions/7899200/is-there-a-naming-convention-for-mysql</a></li>
<li><a href="http://vyaskn.tripod.com/object_naming.htm#Tables">http://vyaskn.tripod.com/object_naming.htm#Tables</a></li>
<li><a href="https://justinsomnia.org/2003/04/essential-database-naming-conventions-and-style/">https://justinsomnia.org/2003/04/essential-database-naming-conventions-and-style/</a></li>
</ul>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>HTTPS toutes les choses !</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/https-toutes-les-choses/" />
            <updated>2017-01-24T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/https-toutes-les-choses/</id>
            <content type="html">
                <![CDATA[
                <p>Depuis la sortie de Chrome 56 (qui devrait arriver dans quelques jours), Google va dénoncer les sites internet qui récoltent des mots de passe ou des numéros de carte de crédit comme non sécurisés s'ils sont servis via HTTP.</p>
<p><img src="/img/blog/2017-01-24/chrome56.png" alt="Chrome 56 showing a non-secure page served through HTTP." title="Chrome 56 showing a non-secure page served through HTTP."></p>
<p>C'est une nouvelle énorme, car elle va obliger pratiquement tout le monde à basculer vers HTTPS. Le but de Google a toujours été de fournir un web meilleur et plus sécurisé pour les utilisateurs. Je pense que c'est une bonne chose qu'un tel géant prenne position pour imposer de telles mesures. Mais la chose la plus importante n'est pas forcément d'entraîner les dévelopeurs à servir des pages sécurisées, mais plutôt d'apprendre aux utilisateurs à ne plus faire confiance à des sites non sécurisés. C'est la raison pour laquelle Chrome va renforcer progressivement cette notification pour qu'elle apparaisse au final avec un triangle rouge, un peu comme les sites dont le certificat est cassé.</p>
<p>Le HTTPS devient de moins en moins cher, et avec des services comme Let's Encrypt, il n'y aura bientôt plus aucune raison de ne pas servir des sites en HTTPS. Ce qui m'amène à penser que je devrais ajouter un certificat à mon site.</p>
<hr>
<p>Source : <a href="https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html?m=1">Google Blog</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Regardez ça, j&#39;ai un blog !</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/regardez-ca-j-ai-un-blog/" />
            <updated>2017-01-23T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/regardez-ca-j-ai-un-blog/</id>
            <content type="html">
                <![CDATA[
                <p>Derrière ce titre blagueur se cache un rappel à moi-même que je devrais écrire plus souvent. Écrire à propos du web et ses technologies a toujours été quelque chose que je veux faire mais j'ai tellement été focalisé sur le travail dernièrement que j'ai totalement oublié de le faire. Où tout du moins j'ai totalement oublié de prendre le temps de le faire.</p>
<p>Je me demande toujours si je devrais aussi copier les articles écrits ici sur <a href="https://medium.com/@fbnlsr">Medium</a> ou pas. Tous les gars cool y sont, peut-être devrais-je m'y mettre aussi ? J'ai <a href="https://medium.com/@fbnlsr/the-three-pillars-of-a-developer-s-mind-ab4be1d93d99#.ksxafk9g5">écrit un papier</a> là-bas, qui a eu de bons retours, je devrais peut-être donc continuer à écrire un peu plus sur mes pensées autour de l'esprit du dévelopement web (et des dévelopeurs).</p>
<p>Le premier billet de 2017 pourrait être un bon moyen de souhaiter une bonne année à tout le monde ! Espérons que cette année sera la plus heureuse pour tous. Les choses se passent plutôt bien pour moi jusqu'ici, et j'ai hâte de voir ce que les mois à venir vont apporter.</p>
<p>Depuis le mois dernier je me suis mis à apprendre Symfony. Je dois admettre que j'avais quelques pressentiments à propos de ce framework, sans vraiment savoir pourquoi. On m'avait dit qu'il était dur à apprendre, mal écrit et que les choses étaient confuses parfois. J'ai eu la bonne surprise de voir qu'en réalité il est très bien fait et relativement facile à apprendre (jusqu'à un certain point bien évidement). Il me tarde de mettre en pratique ce que j'ai appris et de travailler concrètement avec.</p>
<p>Je m'intéresse aussi à Vue.js avec un réel intérêt. Cela parait tellement puissant ! J'ai déjà une idée d'une app sympa à concevoir qui pourrait profiter des deux technologies. Mais bon, le <a href="http://www.eleague.com/major/">tournois majeur de E-League</a> a démarré hier, je n'ai donc pas trop de temps pour tout ça. :)</p>
<p>Enfin bref, c'est parti pour 2017 et espérons que j'aurai un peu de temps pour remplir ces pages le plus tôt possible !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Healthy Warriors</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/healthy-warriors/" />
            <updated>2016-08-31T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/healthy-warriors/</id>
            <content type="html">
                <![CDATA[
                <p>J'ai eu l'occasion de travailler à nouveau avec <a href="https://www.ligne13.com/">Ligne 13</a>, cette fois-ci pour Healthy Warriors !</p>
<p>Healthy Warriors est un centre de yoga et un café à Paris, regroupant plusieurs professeurs de yoga venants des quatre coins du monde. Il a été crée par Aria Crescendo et Gus Forristal, et met en avant des professeurs invités pour des séminaires tout au long de l'année.</p>
<p>Le site internet en lui-même est très simple, et est propulsé par Wordpress. Quelques types de contenu et pages personnalisé(e)s ont été créés pour donner au client la possibilité de modifier les articles disponibles dans la boutique ainsi que les plats et boissons servies au bar.</p>
<p>Merci à <a href="https://www.ligne13.com/">Ligne 13</a> pour ce projet !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Agence Saint Germain</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/agence-saint-germain/" />
            <updated>2016-07-07T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/agence-saint-germain/</id>
            <content type="html">
                <![CDATA[
                <p>J'ai développé le nouveau site de l'<a href="http://www.asgparis.com/">Agence Saint Germain</a> en collaboration avec <a href="https://www.ligne13.com/">Ligne 13</a>. L'Agence Saint Germain est une agence de photographes, maquilleurs, stylistes et costumiers. Leur but est de promouvoir et vendre les services des artistes qui sont signés chez eux.</p>
<p>Ce fût un projet très intéressant, car le défi technique était un peu spécial. Pour ce site, j'ai dû utiliser Masonry, tout en permettant au site de gérer des images de tailles et orientations différente, le tout en ayant un effet parallaxe, tout en restant responsive. La tâche n'était pas des plus simples, mais je suis content du résultat.</p>
<p>L'ensemble est propulsé par Wordpress. Le client avait une idée précise du genre d'expérience qu'il voulait offrir à ses utilisateurs, et je trouve le résultat final très intéressant. L'utilisation de l'espace donne toute son importance à ce qui compte : les images. Une lightbox faîte sur mesure a été ajoutée, ce qui permet aux visiteurs de sélectionner des photos afin de les sauvegarder pour plus tard.</p>
<p>Merci à <a href="https://www.ligne13.com/">Ligne 13</a> pour ce projet !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>CFA Le Vigean</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/cfa-le-vigean/" />
            <updated>2016-05-18T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/cfa-le-vigean/</id>
            <content type="html">
                <![CDATA[
                <p>L'agence web et print ComTogether m'a confié le développement du nouveau site internet pour le <a href="http://cfa-levigean.fr">CFA du Vigean</a>, un centre de formation pour étudiants dans divers domaines (dentisterie, menuiserie, ingénierie géomatique…).</p>
<p>La page d'accueil a été dessinée en interne par l'agence et le client m'a fait suffisamment confiance pour me laisser libre du choix du layout des pages internes au site, ainsi que sur l'approche UX et le design responsive.</p>
<p>Le site est basé sur Wordpress, et plusieurs types de post personnalisés ont été créés pour offrir aux étudiants un endroit où ils peuvent avoir un maximum d'informations en ce qui concerne les diplômes que l'école offre. Mais c'est aussi un endroit où les employeurs et les diplômés peuvent échanger.</p>
<p>Merci à ComTogether pour ce projet.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Les trois piliers d&#39;un dévelopeur</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/les-trois-piliers-d-un-developeur/" />
            <updated>2016-03-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/les-trois-piliers-d-un-developeur/</id>
            <content type="html">
                <![CDATA[
                <p>Si je devais décrire la pire façon de faire mon travail, ce serait d'être assis derrière un bureau, à cracher des lignes de code. Pointer à 09:00, partir à 17:00, et attendre que le jour suivant arrive. J'imagine que c'est une certaine façon de faire, et des milliers de dévelopeurs se contentent de vivre comme ceci. Mais ce n'est clairement pas ma vision. En tant que dévelopeur (et en particulier dans un domaine aussi vivant que celui du développement web), j'aime à penser qu'il faut avoir certaines &quot;qualités&quot; ou pré-requis pour faire ce travail. Ou tout du moins le faire bien.</p>
<p>On pourrait penser que ces pré-requis se trouvent dans le côté &quot;fonctionnel&quot; du cerveau humain. Être bon en math pourrait être un bon début. Cela veut dire que l'on peut facilement suivre des règles, et les appliquer. Parler anglais couramment pourrait en être un autre. Cela aide à lire de la documentation ou des commentaires, comprendre des tutoriels... Et bien sûr, être un &quot;technicien&quot; est un impératif, même si la plupart du temps cette caractéristique ne vous sert pas comme vous le voudriez. Combien de fois avez-vous déjà entendu &quot;Ah, t'es dans le web ? Tu veux pas m'aider à réparer mon imprimante ?&quot;</p>
<p>Tout ceci aide, bien sûr. Mais je pense que les choses principales qui font un bon dévelopeur se trouvent dans des concepts bien plus profonds. Et cela pourrait être résumé à trois aspects qui fonctionnent merveilleusement bien ensemble.</p>
<p>L'un de ces pré-requis pour moi est l'amour du travail bien fait. Être un dévelopeur est comme être un menuisier (un autre de mes intérêts). Votre éditeur de code est votre marteau. J'imagine que c'est la raison pour laquelle on voit si souvent certains dévelopeurs se décrire comme des &quot;artisans&quot;. Derrière ce cliché se cache bien évidement une mode, mais je pense qu'il existe un sens plus profond au terme : celui de quelqu'un qui aime son travail, et qui a une passion pour de beaux objets. Ou dans notre cas, du code. On doit constamment savoir comment utiliser ses outils, comment maîtriser des techniques, et comment en acquérir de nouvelles.</p>
<p>Ce besoin d'apprendre et appliquer des techniques ouvre la voie à une autre qualité : savoir être créatif. Vous ne savez peut-être pas comment faire quelque chose, mais votre esprit peut toujours trouver un moyen. Que ce soit explorer des sentiers inconnus, ou en trouver un caché, il existe toujours plusieurs solutions à un problème. Être créatif nous aide à trouver des chemins, et c'est souvent alimenté par une troisième qualité qui est pour moi essentielle à tout dévelopeur : savoir être curieux.</p>
<p>C'est grâce à la curiosité que, en étant enfant, n'importe qui apprend les choses basiques de la vie. Ce qui est bien, ce qui est mauvais. Ce qui fait mal, ce qui rend heureux. La curiosité pousse à rencontrer des gens, confronter des idées, et échanger des opinions. Elle est la pierre angulaire de toute interaction sociale, et le meilleur moyen pour développer son esprit. Il est important de toujours avoir une oreille attentive, non seulement aux critiques mais aussi aux nouvelles choses qui peuvent saisir notre intérêt.</p>
<p>Si c'est votre passion, vous aurez tendance à négliger certains aspects de ces &quot;qualités&quot;, car elles sont souvent intrinsèques à chacun. Si par contre vous êtes du genre timide, mon conseil est d'essayer d'interagir toujours plus. Il n'y a que des bonnes choses qui peuvent découler d'un simple &quot;bonjour&quot;. Vous pourrez rencontrer des esprits obtus, bien sûr, mais il y a toujours quelque chose à garder de chaque être humain que vous rencontrez. Ce qu'il y a de plus intéressant je trouve, c'est que ces trois points sont connectés. Ils dépendent l'un de l'autre, formants comme une boucle sans fin. Ils apparaissent dans un certain ordre, mais vous pouvez définitivement sentir leur influence et leur interconnexion.</p>
<p>Au final, ces caractéristiques combinées créent un flux intéressant. La Curiosité est l'étincelle qui déclenche la Créativité, qui elle-même se sublime grâce à l'Artisanat. C'est avec ces trois piliers de l'esprit humain que vous pourrez grandir et aiguiser vos compétences, pour à la fin produire un code plus beau et efficace.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Nouveau site web !</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/nouveau-site-web/" />
            <updated>2016-01-20T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/nouveau-site-web/</id>
            <content type="html">
                <![CDATA[
                <p>J'aurai mis le temps, mais j'ai enfin mis à jour mon site internet. Bienvenue !</p>
<p>Avec ce nouveau site, je dis au revoir à Jekyll, et bonjour à Wordpress. J'ai toujours été fan de Jekyll, et je l'utilise toujours pour certains de mes projets. Mais dernièrement je me suis mis à utiliser Wordpress et ça a énormément évolué ces dernières années. C'est maintenant un écosystème solide, et cela me permettra de faire évoluer ce blog plus rapidement qu'avant. Heureusement cela veut aussi dire que je vais avoir un peu plus de temps pour écrire sur les projets sur lesquels je travaille.</p>
<p>Mais malheureusement, la plupart de mon travail est couvert d'une NDA qui m'empêche de parler des projets sur lesquels je travaille. Cependant, grâce aux gens de <a href="http://www.quasardb.net/">quasardb</a>, j'ai pu concocter une petite &quot;étude de cas&quot; qui décrit comment j'ai conçu une application qui est utilisée tous les jours dans la société. C'est toujours passionnant de travailler avec des gens intéressants sur ce genre de projet, et même si ce n'était pas le projet le plus gros sur lequel j'ai pu travailler, c'était une expérience enrichissante.</p>
<p>Et si vous vous posez la question, je suis disponible pour travailler sur votre projet, alors n'hésitez pas à m'appeler pour qu'on en discute !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Blog</title>
            <link href="https://www.fabienlasserre.dev/fr/blog/" />
            <updated>1970-01-01T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/fr/blog/</id>
            <content type="html">
                <![CDATA[
                
                ]]>
            </content>
        </entry>
    
</feed>
