Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
  • prez
2 results

Target

Select target project
  • vimazeno/blog.limos.fr
  • matrossevi/blog.limos.fr
  • borlonjo/blog.limos.fr
3 results
Select Git revision
Show changes
Showing
with 1125 additions and 1201 deletions
......@@ -7,11 +7,11 @@
<title>Souveraineté</title>
<link rel="stylesheet" href="../../node_modules/reveal.js/css/reveal.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/white.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/black.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="../../node_modules/reveal.js/lib/css/zenburn.css">
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../../node_modules/@fortawesome/fontawesome-free/css/all.min.css">
<link rel="stylesheet" href="../main.css">
<!-- Printing and PDF exports -->
......@@ -35,7 +35,7 @@
</div>
</div>
<script src="../../node_modules/reveal.js/lib/js/head.min.js"></script>
<!-- script src="../../node_modules/reveal.js/lib/js/head.min.js"></script -->
<script src="../../node_modules/reveal.js/js/reveal.js"></script>
<script>
......@@ -49,11 +49,23 @@
center: false,
dependencies: [
{ src: '../../node_modules/reveal.js/plugin/markdown/marked.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js',
condition: function() { return !!document.querySelector( '[data-markdown]' ); },
callback: function() {
Array.prototype.forEach.call(document.querySelectorAll('section > li'), function(ele){
var fragIndex = ele.innerHTML.indexOf("--")
if (fragIndex != -1){
ele.innerHTML = ele.innerHTML.replace("--", "");
ele.className = 'fragment';
}
});
}
},
{ src: '../../node_modules/reveal.js/plugin/notes/notes.js', async: true },
{ src: '../../node_modules/reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
<script src="../main.js"></script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Tor</title>
<link rel="stylesheet" href="../../node_modules/reveal.js/css/reveal.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/black.css">
<style>
.trsp {background-color: rgba(0, 0, 0, 0.8);}
.green {color: green;}
.red {color: red;}
li {list-style-type: none}
li.c {list-style-type: circle}
ul.c {padding-left: 70px;padding-right: 70px;}
</style>
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="../../node_modules/reveal.js/lib/css/zenburn.css">
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? '../../node_modules/reveal.js/css/print/pdf.css' : '../../node_modules/reveal.js/css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-background-image="images/tor/hacker.jpg">
<h1 class="trsp" style="margin-top: 30%">Comment surfer couvert ...</h1>
<h4 class="trsp" style="margin-top: 2%; text-transform: lowercase;">
<img src="https://fc.isima.fr/~mazenod/images/mazenovi.png"
style="margin: 0; margin-left: 10px; margin-right: 10px;padding: 0;float: right;border: 0; border-radius: 50%;width: 5%" />
<a href="http://m4z3.me/pintofscience">http://m4z3.me/pintofscience</a>
<span style="float: right">par
<a href="http://twitter.com/mazenovi">@mazenovi</a>
</span>
</h4>
<aside class="notes">
<ul>
<li>Vincent Mazenod aka mazenovi
<ul>
<li>20 ans de développement pour le web</li>
<li>dont 17 au CNRS</li>
<li>expert SSI à la délégation régionale Auvergne-Rhône-Alpes du CNRS (DR7) depuis 10 ans</li>
<li>concerné par la vie privée depuis le début</li>
<li>je parle histoire de l'informatique, éthique de l'informatique et futurologie après quelques pintes</li>
</ul>
</li>
<li>Surfez couvert ...
<ul>
<li>sujet banal pour image de couverture banale
<ul>
<li>marronier digital</li>
<li>saucisson numérique</li>
</ul>
</li>
<li>
ce soir réponse radicale et argumentée,
accrochez vous à vos claviers
</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/n00b.jpg">
<h2 class="trsp">... alors qu'on ne comprend rien à l'informatique?</h2>
<aside class="notes">
<ul>
<li>ce n'est pas une présentation pour les nuls</li>
<li>pas de conseils sur les mot de passes, JP Pernaud fait ca très bien</li>
<li>pour ne pas avoir de problèmes:
<ul>
<li>vérifier votre barre d'url souvent</li>
<li>Pas de streaming / téléchargement</li>
<li>Pas de jeu en ligne</li>
<li>Pas de porn</li>
</ul>
</li>
<li>si vous êtes persuadés d'être nul ...</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/minitel.gif">
<h1 class="trsp">ne surfez pas!!</h1>
<aside class="notes">
<ul>
<li>Si on y comprend vraiment rien
<ul>
<li>Mieux vaut ne pas</li>
</ul>
</li>
<li>Si vous voulez comprendre
<ul>
<li>Poser des questions : c'est le moment</li>
<li>Aucune question n'est idiote: peu de gens savent</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/go.gif">
<h2 class="trsp">ou comprenez les enjeux!</h2>
<aside class="notes">
<ul>
<li>On ne parle pas ici des enjeux de madame michu</li>
<li>On parle de la protection des sources sur Internet</li>
<li>hastag: investigation, journalisme, liberté d'expression, leak etc ... </li>
</ul>
</aside>
</section>
<section>
<h2>menaces</h2>
<ul>
<li>états / renseignements</li>
<li>plateformes (GAFAM)</li>
<li>attaques ciblées</li>
</ul>
<aside class="notes">
<ul>
<li>vous connaissez Edward Snowden?
<ul>
<li>la NSA</li>
<li>les plateformes qui collaborent avec la NSA (révélations PRISM)</li>
<li>éventuellement des pirates souhaitant mettre la main
sur les outils de la NSA avant qu'ils soient connus</li>
</ul>
</li>
<li>TOUS LES COUPS SONT PERMIS</li>
</ul>
</aside>
</section>
<section>
<h2>se protéger</h2>
<ul>
<li>confidentialité</li>
<li>authenticité</li>
<li>anonymat</li>
</ul>
<aside class="notes">
<ul>
<li>confidentialité: le message n'est lu QUE par le destinataire</li>
<li>authenticité:
<ul>
<li>le message est lu par le bon destinataire</li>
<li>l'identité de l'émetteur est éventuellement vérifiable</li>
</ul>
</li>
<li>
anonymat:
<ul>
<li>l'IP (de votre box, ou mobile) notamment est géolocalisable et tracable</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/en-clair.png"
data-background-color="white"
data-background-size="70%">
<h4>confidentialité / en clair par défaut</h4>
<aside class="notes">
<ul>
<li>le FAI
<ul>
<li>attribue l'IP</li>
<li>
fournit les DNS (correspondance nom de domaine
adresse IP du serveur à consulter). Requête transparente
mais cruciale comme on le verra plus tard
</li>
<li>voit tout le traffic. C'est le centre de tri des cartes postales</li>
<li>plusieurs FAI sont en général impliqués dans une communication</li>
</ul>
</li>
<li>
les plateformes
<ul>
<li>s'il partage un fichier via dropbox</li>
<li>une pièce jointe via gmail</li>
</ul>
</li>
<li>la NSA collabore avec les FAI et les plateformes</li>
<li>Snowden balance gros: il doit se protéger de tout le monde</li>
<li>comment s'en sortir?</li>
</ul>
</aside>
</section>
<section data-background-image="images/crypto/scytale.png"
data-background-size="50%">
<h4>confidentialité / chiffrement</h4>
<aside class="notes">
<ul>
<li>vieux comme le monde</li>
<li>le scytale</li>
<li>on fait plus robuste maintenant (attaque par analyse)</li>
<li>la sécurité doit reposer sur le secret de la clé,
pas sur le secret de l'algorithme
<ul>
<li>le fonctionnement des algo de chiffrement est typiquement connu</li>
</ul>
</li>
<li> chiffrer, déchiffrer, décrypter, crypter, chiffrage</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/chiffrement-symetrique.png"
data-background-color="white"
data-background-size="70%">
<h4>confidentialité / chiffrement symétrique</h4>
<aside class="notes">
<ul>
<li>on a un problème pour partager la clé
<ul>
<li>peut marcher si on se cache des FAI (IRL?)</li>
</ul>
</li>
<li>pas de scellé (authenticité)
<ul>
<li>on sait juste que c'est quelqu'un qui partage notre sercret</li>
</ul>
</li>
<li>la NSA doit décrypter : ca nécessitebeaucoup de calcul</li>
<li>google aussi dans le contexte Gmail, mais pas forcément dans le contexte pub ou ananlyse de traffic (techniquement parlant)</li>
<li>Souvenez vous on parle d'Edward Snowden!</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/chiffrement-asymetrique-chiffrement.png"
data-background-color="white"
data-background-size="70%">
<h4>confidentialité / chiffrement asymétrique</h4>
<aside class="notes">
<ul>
<li>depuis les 70s</li>
<li>tout s'appelle clé ... désolé</li>
<li>1 clé publique présentée dans un certificat publique
<ul>
<li>sur le site du guardian, carte de visite, IRL, etc ...</li>
<li>permet d'obtenir un cadenas pour fermer sans la clé</li>
<li>seul celui qui a la clé peut ouvrir</li>
<li>pas d'échange de clé</li>
</ul>
</li>
<li>c'est des maths
<ul>
<li>tout ce qui est chiffré avec la clé publique est déchiffrable avec la clé privée</li>
<li>tout ce qui est chiffré avec la clé privée est déchiffrable avec la clé publique
<ul>
<li>c'est la signature</li>
</ul>
</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/chiffrement-asymetrique-signature.png"
data-background-color="white"
data-background-size="70%">
<h4>authenticité / signature</h4>
<aside class="notes">
<ul>
<li>la clé privée agit ici comme un sceau</li>
<li>elle permet de sceller l'enveloppe avec la clé privée</li>
<li>le sceau est vérifiable par tous</li>
<li>toute la confiance repose sur la légitimité des certificats
<ul>
<li>approche PGP : on échange les clés d'humain à humain
<ul>
<li>système décentralisé, chacun se créée sa toile de confiance</li>
</ul>
</li>
<li>approche X509 : des autorités délivrent les certificats
<ul>
<li>repose essentiellement sur le nom de domaine pour TLS</li>
</ul>
</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/pki.png"
data-background-color="white"
data-background-size="70%">
<h4>authenticité / PKI</h4>
<aside class="notes">
<ul>
<li>Autorité de certification (CA)</li>
<li>système de gestion de clé publique / privée (ou de chiffrement asymétrique)
<ul>
<li>pour humains, serveurs, organisation</li>
</ul>
</li>
<li>vérifie la légitimité lors de l'attribution</li>
<li>gère les révocations, la distribution, le renouvellement etc ...</li>
<li>tout repose sur ces autorités mais elles sont loin d'être irréprochable</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/known-good-signers.png"
data-background-size="50%">
<h4>authenticité / CA</h4>
<aside class="notes">
<ul>
<li>le navigateur est configuré avec les autorités de confiance</li>
<li>en pratique les autorités qui paient</li>
<li>on ne les connaît pas toutes</li>
</ul>
</aside>
</section>
<section>
<h2><strike>SSL</strike> TLS</h2>
<ul>
<li class="c">
échange d'une clé de chiffrement symétirque
<ul>
<li class="c">via un système de chiffrement asymétrique</li>
</ul>
</li>
<li class="c">autorités de confiance dans le navigateur</li>
cadenas vert
</ul>
<img src="images/tor/green-bar-web-browser.jpg" />
<div class="trsp" style="margin-top: 2%">le FAI voit toujours quelle IP consulte quelle IP :/</div>
<aside class="notes">
<ul>
<li>mais il y a pire que le leake de l'IP: même le nom de domaine n'est pas préservé</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/dns.jpg"
data-background-color="white"
data-background-size="55%">
<h2>anonymat / leak DNS</h2>
<aside class="notes">
<ul>
<li>trahit la consultation de tel nom de domaine pour telle IP</li>
<li>typiquement les données recueilli par les boites noires de la loi renseignement</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/tracking.png"
data-background-color="#F8F2E4"
data-background-size="72%">
<h2>anonymat / tracking</h2>
<div style="margin-top: 45%;font-size: 12px">source: <a href="https://c-marketing.eu/zoom-sur-les-cookies/">https://c-marketing.eu/zoom-sur-les-cookies/</a></div>
<div style="margin-top: 5%"><a href="https://panopticlick.eff.org/">panopticlick.eff.org</a></div>
<aside class="notes">
<ul>
<li>des régies publicitaires (Google adSens)</li>
<li>des analyseur de traffic (Goole Analytics)</li>
<li>agit au sein de l'échange protégé
<ul>
<li>recoupement d'utilisateur par cookie</li>
<li>recoupement d'utilisateur par signature de navigateur
<ul>
<li>prévient la suppression des cookies</li>
<li>adblocker peut être payé pour laisser passer telle ou telle régies</li>
</ul>
</li>
<li>même en navigation privé google peut lier le traffic à un utilisateur
<ul>
<li>ne pas être authentifié sur gmail ou facebook n'est pas un obstacle</li>
<li>navigation mode privée permet juste de préserver l'historique</li>
</ul>
</li>
<li>javascript est la faille, le désactiver est une bonne idée pour la privacy
<ul>
<li>une mauvaise idée pour le confort</li>
</ul>
</li>
</ul>
</li>
</ul>
</aside>
</section>
<section>
<h2>se protéger avec <strike>SSL</strike> TLS</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authenticité <strong class="green"></strong></li>
<li>anonymat <strong class="red">𐄂</strong></li>
</ul>
<div class="trsp" style="margin-top: 25%">idéal pour le e-commerce ;)</div>
<aside class="notes">
<ul>
<li>
BTW les gesticulations sécuritaires à chaque attentat sur la remise en cause du chiffrement n'ont pas de sens
<ul>
<li>pas de chiffrement = pas de confiance = pas de e-commerce</li>
<li>la première chose qu'on a chiffré c'est les numéro de CB (LCEN 2004)</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/tor.png" data-background-size="55%">
<h2>Tor</h2>
<ul class="trsp c">
<li class="c">"un" darknet parmi d'autres</li>
<li class="c">"le" <strong>darkweb</strong>: un des services de <strong>Tor</strong></li>
<li class="c">surcouche réseau
<ul>
<li class="c">s'appuie sur le web <em>"classique"</em></li>
</ul>
</li>
<li class="c">projet de l'armée américaine (mi-1990)</li>
<li class="c">code en open source depuis 2004</li>
<li class="c">sponsorisée par l'<a href="https://www.eff.org/fr">EFF</a></li>
<li class="c">organisation à but non lucratif</li>
<li class="c">contournement de la censure</li>
<li class="c">~2 375 000 utilisateurs par jour</li>
</ul>
<aside class="notes">
<ul>
<li>faire une recherche sur TOR c'est déjà être fiché par la NSA
<ul>
<li>peut être même en venant à cette talk ;)</li>
<li>réellement efficace</li>
</ul>
</li>
<li>utilisé par Snowden, Wikileaks, panama papers etc ...
<ul>
<li>
mais pas n'importe comment
</li>
</ul>
</li>
<li>utilisé par les dissidents chinois etc ... où le web est surveillé / censuré
<ul>
<li>c'est pourquoi facebook par exemple est sur Tor</li>
</ul>
</li>
</ul>
</aside>
</section>
<section>
<ul class="trsp c">
<li class="c">
<a href="https://www.torproject.org/projects/torbrowser.html.en">cross platform</a>
<i class="fa fa-linux" aria-hidden="true"></i>
<i class="fa fa-windows" aria-hidden="true"></i>
<i class="fa fa-apple" aria-hidden="true"></i>
<i class="fa fa-android" aria-hidden="true"></i>
</li>
<li class="c"><i class="fa fa-firefox" aria-hidden="true"></i> Firefox + proxy tor intégré</li>
<li class="c">
<i class="fa fa-firefox" aria-hidden="true"></i> add ons</a>
<ul>
<li><a href="https://addons.mozilla.org/fr/firefox/addon/noscript/">NoScript</li>
<li><a href="https://www.eff.org/fr/https-everywhere"> HTTPS everywhere</li>
</ul>
</li>
<li class="c">
<a href="https://tails.boum.org/">Tails (OS live)</a>
</li>
</ul>
<img src="images/tor/tor-browser.jpg" />
<aside class="notes">
<ul>
<li>Tails ajoute de la sécurité au niveau de l'OS
<ul>
<li>linux opensource VS windows ou mac os privateur</li>
<li>système live = système amnésique
<ul>
<li>ne possède aucune donnée</li>
</ul>
</li>
<li>MEME SI personnalisation d'une partition de données chiffrées possible</li>
</ul>
</li>
<li>portable
<ul>
<li>se démarre avec une clé usb à partir de n'importe quelle machine</li>
<li>environnement utilisable sur n'importe quelle machine sans laisser de trace</lI>
<li>kit de survie pour journaliste d'investigation</li>
</ul>
</li>
<li>toutes les applications passent par Tor
<ul>
<li>IM, mail, etc ... ce qui n'est pas le cas avec Tor Browser</li>
</ul>
</li>
<li>utiliser Tor sur son mobile ou sur windows est ce une bonne idée?</li>
</ul>
</aside>
</section>
<section
data-background-image="images/tor/tor-circuit.png"
data-background-size="contain"
data-background-position="bottom">
<h3 class="trsp">Tor circuit</h3>
<aside class="notes">
<ul>
<li>repose sur des noeuds internet spéciaux appelés neouds Tor
<ul>
<li>on chiffre de proche en proche
<ul>
<li>chaque noeud ne connait que son prédécesseur et son successeur</li>
<li>on utilise Trois nouds Tor</li>
<li>on cache donc le client au serveur</li>
</ul>
</li>
<li>
c'est le noeud de sortie qui effectue la requête DNS
<ul>
<li>peut voir le contenu de la requête</li>
</ul>
</li>
<li>le noeud d'entrée l'IP du client</li>
</ul>
</li>
<li>anonymat préservé
<ul>
<li>sauf sur le dernier mètre
<ul>
<li>la NSA peut avoir des infos</li>
</ul>
</li>
<li>sauf si on maîtrise tout le circuit Tor
<ul>
<li>le projet émane de l'armée américaine</li>
<li>opensourcing du renseignement</li>
</ul>
</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/Tor-Encryption.jpg" data-background-size="contain" data-background-color="#eeeeee">
<aside class="notes">
<ul>
<li>le schéma est faux mais beau</li>
<li>les trois noeuds par les quels passent la communication
<ul>
<li>sont choisis aléatoirement</li>
<li>renouvelés régulièrement</li>
</ul>
</li>
<li>réduit la probabilité qu'un attaquant maîtrise tous les cricuits</li>
<li>ce n'est pas 100% efficace
<ul>
<li>ca maximise la probabilité de l'être</li>
</ul>
</li>
</ul>
</aside>
</section>
<section>
<h2>se protéger avec Tor sur le web</h2>
<ul>
<li>confidentialité <strong class="red">𐄂</strong></li>
<li>authenticité <strong class="red">𐄂</strong></li>
<li>anonymat <strong class="green"></strong></li>
</ul>
</section>
<section>
<h2>se protéger avec Tor + TLS (https) sur le web</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authenticité <strong class="green"></strong></li>
<li>anonymat <strong class="green"></strong></li>
</ul>
<aside class="notes">
<ul>
<li>sécuristation du dernier mètre</li>
<li>authenticité du serveur via TLS et la PKI associée</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/hs.png" data-background-size="contain" data-background-color="#e1e1e1">
<aside class="notes">
<ul>
<li>service caché = hidden service</li>
<li>l'idée est de mettre 2 circuits Tor bout à bout
<ul>
<li>un annuaire de service cachés joue le rôle d'entremetteur</li>
</ul>
</li>
<li>un point de rendez vous est fixé
<ul>
<li>le client ne connait pas l'IP du serveur</li>
<li>le serveur ne connait pas l'IP du client</li>
<li>le HSDir ne connait ni l'IP du client ni l'IP du serveur</li>
<li>le Point de rendez vous ne connait ni l'IP du client ni l'IP du serveur</li>
</ul>
</li>
</ul>
</aside>
</section>
<section>
<h2>se protéger avec des services cachés Tor (.onion)</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authenticité <strong class="green"></strong></li>
<li>anonymat <strong class="green"></strong></li>
<li><strong>anonymat du serveur</strong> <strong class="green"></strong></li>
</ul>
<div class="trsp" style="margin-top: 15%">idéal pour la liberté d'expression ;)</div>
</section>
<section data-background-image="images/tor/whistleblowers.jpg">
<aside class="notes">
<ul>
<li>Edward Snowden</li>
<li>Julian Assange - wikileaks: panama papers, cable diplomatique - personnalité discutable</li>
<li>Bradley / Chelsea Manning</li>
</ul>
</aside>
</section>
<section>
<h2>se protéger avec Tor (.onion)</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authenticité <strong class="green"></strong></li>
<li>anonymat <strong class="green"></strong></li>
<li><strong>anonymat du serveur</strong> <strong class="green"></strong></li>
</ul>
<div class="trsp" style="margin-top: 15%">idéal pour la cybercriminalité ;)</div>
<aside class="notes">
<ul>
<li>Tout ce qui est usuellement interdit est faisable
<ul>
<li>drogues: beaucoup</li>
<li> armes: peu, beaucoup de scam</li>
<li>pédopornographie: bien cachée, car ne fait pas l'unanimité sur Tor comme ailleurs</li>
</ul>
</li>
<li>ces sujets existent avant Tor et existeront après</li>
</aside>
</section>
<section data-background-image="images/tor/cybercrime.gif">
<aside class="notes">
<ul>
<li>acheter de la drogue, avec des crypto monnaie
<ul>
<li>BTC ou mieux monero pour l'anonymat</li>
<li>une autre présentation pour Pot Of Science peut etre;)</li>
</ul>
</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/phil-zimmermann.jpg">
<aside class="notes">
<ul>
<li>Problème fondamental qui doit amener à la réflexion et non à l'action</li>
<li>Posé dès 1991 par Phil Zimmermann auteur de PGP et de la première crypto war</li>
</ul>
</aside>
</section>
<section data-background-image="images/tor/applause.gif">
<div class="trsp">
<h1>Merci</h1>
<h2>Questions?</h2>
</div>
</section>
</section>
</div>
</div>
<script src="../../node_modules/reveal.js/lib/js/head.min.js"></script>
<script src="../../node_modules/reveal.js/js/reveal.js"></script>
<script>
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: false,
dependencies: [
{ src: '../../node_modules/reveal.js/plugin/markdown/marked.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js' },
{ src: '../../node_modules/reveal.js/plugin/notes/notes.js', async: true },
{ src: '../../node_modules/reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Tor</title>
<link rel="stylesheet" href="../../node_modules/reveal.js/css/reveal.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/black.css">
<style>
.trsp {background-color: rgba(0, 0, 0, 0.8);}
.green {color: green;}
.red {color: red;}
li {list-style-type: none}
li.c {list-style-type: circle}
ul.c {padding-left: 70px;padding-right: 70px;}
</style>
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="../../node_modules/reveal.js/lib/css/zenburn.css">
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? '../../node_modules/reveal.js/css/print/pdf.css' : '../../node_modules/reveal.js/css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-background-image="images/tor/hacker.jpg">
<h1 class="trsp" style="margin-top: 30%">Comment surfer couvert ...</h1>
</section>
<section data-background-image="images/tor/n00b.jpg">
<h2 class="trsp">... alors qu'on ne comprend rien à l'informatique?</h2>
</section>
<section data-background-image="images/tor/minitel.gif">
<h1 class="trsp">ne surfez pas!!</h1>
</section>
<section data-background-image="images/tor/go.gif">
<h2 class="trsp">ou comprenez les enjeux!</h2>
</section>
<section>
<h2>se protéger</h2>
<ul>
<li>confidentialité</li>
<li>authentification</li>
<li>anonymat</li>
</ul>
</section>
<section data-background-image="images/tor/chiffrement.png"
data-background-color="white"
data-background-size="70%">
<h4>confidentialité / chiffrement symétrique</h4>
</section>
<section data-background-image="images/crypto/scytale.png"
data-background-size="50%">
<h4>confidentialité / Vieux comme le monde</h4>
</section>
<section data-background-image="images/tor/chiffrer.png"
data-background-color="white"
data-background-size="70%">
<h4>confidentialité / chiffrement asymétrique</h4>
</section>
<section data-background-image="images/tor/signer.png"
data-background-color="white"
data-background-size="70%">
<h4>authentification / signature</h4>
</section>
<section data-background-image="images/tor/pki.png"
data-background-color="white"
data-background-size="70%">
<h4>authentification / PKI</h4>
</section>
<section data-background-image="images/tor/known-good-signers.png"
data-background-size="50%">
<h4>authentification / CA</h4>
</section>
<section data-background-image="images/tor/dns.jpg"
data-background-color="white"
data-background-size="55%">
<h2>anonymat / IP / DNS</h2>
</section>
<section data-background-image="images/tor/tracking.jpg"
data-background-color="#F8F2E4"
data-background-size="72%">
<h2>anonymat / tracking</h2>
<div style="margin-top: 55%"><a href="https://panopticlick.eff.org/">panopticlick.eff.org</a></div>
</section>
<section>
<h2>se protéger avec <strike>SSL</strike> TLS</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authentification <strong class="green"></strong></li>
<li>anonymat <strong class="red">𐄂</strong></li>
</ul>
<div class="trsp" style="margin-top: 25%">idéal pour le e-commerce ;)</div>
</section>
<section data-background-image="images/tor/tor.png" data-background-size="55%">
<h2>Tor</h2>
<ul class="trsp c" style="margin-top: 40px">
<li class="c">"un" darknet parmi d'autres</li>
<li class="c">"le" <strong>darkweb</strong>: un des services de <strong>Tor</strong></li>
<li class="c">projet de l'armée américaine (mi-1990)</li>
<li class="c">code en open source depuis 2004</li>
<li class="c">sponsorisée par l'<a href="https://www.eff.org/fr">EFF</a></li>
<li class="c">organisation à but non lucratif</li>
<li class="c">contournement de la censure</li>
<li class="c">~2 375 000 utilisateurs par jour</li>
</ul>
</section>
<section data-background-image="images/tor/tor-browser.jpg" data-background-size="55%" data-background-position="bottom">
<ul class="trsp c">
<li class="c">
<a href="https://www.torproject.org/projects/torbrowser.html.en">cross platform</a>
<i class="fa fa-linux" aria-hidden="true"></i>
<i class="fa fa-windows" aria-hidden="true"></i>
<i class="fa fa-apple" aria-hidden="true"></i>
<i class="fa fa-android" aria-hidden="true"></i>
</li>
<li class="c"><i class="fa fa-firefox" aria-hidden="true"></i> Firefox + proxy tor intégré</li>
<li class="c">
<a href="https://addons.mozilla.org/fr/firefox/addon/noscript/">NoScript
<i class="fa fa-firefox" aria-hidden="true"></i> add ons</a>
<ul class="c"><li>javascript désactivé</li></ul>
</li>
<li class="c">
<a href="https://tails.boum.org/">Tails (OS live)</a>
</li>
</ul>
</section>
<section data-background-image="images/tor/Tor-Encryption.jpg" data-background-size="contain" data-background-color="#eeeeee">
</section>
<section
data-background-image="images/tor/tor-circuit.png"
data-background-size="contain"
data-background-position="bottom">
<h3 class="trsp">Tor circuit</h3>
</section>
<section>
<h2>se protéger avec Tor sur le web</h2>
<ul>
<li>confidentialité <strong class="red">𐄂</strong></li>
<li>authentification <strong class="red">𐄂</strong></li>
<li>anonymat <strong class="green"></strong></li>
</ul>
</section>
<section>
<h2>se protéger avec Tor + TLS (https) sur le web</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authentification <strong class="green"></strong></li>
<li>anonymat <strong class="green"></strong></li>
</ul>
</section>
<section data-background-image="images/tor/hs.png" data-background-size="contain" data-background-color="#e1e1e1">
</section>
<section>
<h2>se protéger avec Tor (.onion)</h2>
<ul>
<li>confidentialité <strong class="green"></strong></li>
<li>authentification <strong class="green"></strong></li>
<li>anonymat <strong class="green"></strong></li>
<li><strong>anonymat du serveur</strong> <strong class="green"></strong></li>
</ul>
<div class="trsp" style="margin-top: 15%">idéal pour la cybercriminalité ;)</div>
</section>
<section data-background-image="images/tor/cybercrime.gif">
</section>
<section data-background-image="images/tor/applause.gif">
<div class="trsp">
<h1>Merci</h1>
<h2>Questions?</h2>
</div>
</section>
</section>
</div>
</div>
<script src="../../node_modules/reveal.js/lib/js/head.min.js"></script>
<script src="../../node_modules/reveal.js/js/reveal.js"></script>
<script>
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: false,
dependencies: [
{ src: '../../node_modules/reveal.js/plugin/markdown/marked.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js' },
{ src: '../../node_modules/reveal.js/plugin/notes/notes.js', async: true },
{ src: '../../node_modules/reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Vault</title>
<link rel="stylesheet" href="../../node_modules/reveal.js/css/reveal.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/white.css">
<link rel="stylesheet" href="../main.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="../../node_modules/reveal.js/lib/css/zenburn.css">
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? '../../node_modules/reveal.js/css/print/pdf.css' : '../../node_modules/reveal.js/css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-markdown="md/vault.md"
data-separator="^\n\n\n"
data-separator-vertical="^\n\n"
data-separator-notes="^Note:"
data-charset="utf-8">
</section>
</div>
</div>
<script src="../../node_modules/reveal.js/lib/js/head.min.js"></script>
<script src="../../node_modules/reveal.js/js/reveal.js"></script>
<script>
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: false,
dependencies: [
{ src: '../../node_modules/reveal.js/plugin/markdown/marked.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js' },
{ src: '../../node_modules/reveal.js/plugin/notes/notes.js', async: true },
{ src: '../../node_modules/reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
</body>
</html>
# Profan
## RGPD
## RGPD
* Règlement général sur la protection des données
* en vigueur le 25 mai 2018
* Dans tous les états membres de l'UE
* En France la CNIL devient autorité de contrôle
* <strike>Déclaration CNIL</strike>
* tous responsable
* démonstration permanente du maintien de la conformité
## Périmètre
* Tout service ou sous traitant (y compris hors UE) traitant des données de résidents de l'UE
* Entreprises
* Associations
* Organismes publics
* Sous traitants
## Objectifs
### Crédibiliser la régulation
* Réclamation auprès de l'autorité de contrôle
* Droit de recours contre le responsable du traitement ou un sous traitant
* Actions collectives ou personnelles
* Sanctions
* 4% du chiffre d'affaire annuel mondial
* 20 000 000 €
## Objectifs
### Renforcer la transparence
* Quelles données sont collectées?
* Dans quels buts?
* Pour combien de temps?
## Objectifs
### Faciliter l'exercice des droits
* droit à la rectification
* droit à la portabilité
* droit à l'oubli
* suppression des données personnelles
* dès qu'elles ne sont plus nécessaires au traitement
* dès que le consentement de l'utilisateur a été retiré
* dès que la personne s'y oppose
## Objectifs
### Responsabiliser les acteurs traitant des données
* Obligation de moyens pour la sécurité des données
* Obligation d'information en cas de violation de données à caractère personnel
* Vis à vis de l'autorité de contrôle
* Vis à vis des personnes concernées
## Comment?
* DPO (Data Protection Officer) / DPD (Délégué à la protection des données)
* conformité RGPD
* Point de contact avec les autorités
* Analyse d'impact (PIA)
* [un logiciel pour réaliser son analyse d’impact sur la protection des données (PIA)](https://www.cnil.fr/fr/rgpd-un-logiciel-pour-realiser-son-analyse-dimpact-sur-la-protection-des-donnees-pia)
## Profan
* qui est le responsable du traitement
* L'UCA?
* Le CNRS?
* Le ministère de l'éducation nationale?
Réponse le 20 mars!
## Profan
* Quels sont les finalités du traitement?
* Le LAPSCO doit préciser
* cette information doit faire partier de la communication Profan dès la rentrée prochaine
## Profan
* exercice des droits
* les fonctionnalités sont implémentées
* Mesure de sécurité
* elles ont été listées
Plus généralement l'analyse d'impact a été largement entamé pour eP3c et peut être réutilisé pour Profan.
## à faire
* Déterminer le responsable des traitements
* Détailler (toutes) les finalités du projet
* Les communiquer clairement aux famille
* Adapter l'analyse d'impact à partir du travail réalisé pour eP3C
\ No newline at end of file
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Profan RGPD</title>
<link rel="stylesheet" href="../../node_modules/reveal.js/css/reveal.css">
<link rel="stylesheet" href="../../node_modules/reveal.js/css/theme/white.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="../../node_modules/reveal.js/lib/css/zenburn.css">
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../main.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? '../../node_modules/reveal.js/css/print/pdf.css' : '../../node_modules/reveal.js/css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-markdown="md/profan-rgpd.md"
data-separator="^\n\n\n"
data-separator-vertical="^\n\n"
data-separator-notes="^Note:"
data-charset="utf-8">
</section>
</div>
</div>
<script src="../../node_modules/reveal.js/lib/js/head.min.js"></script>
<script src="../../node_modules/reveal.js/js/reveal.js"></script>
<script>
// More info about config & dependencies:
// - https://github.com/hakimel/reveal.js#configuration
// - https://github.com/hakimel/reveal.js#dependencies
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: false,
dependencies: [
{ src: '../../node_modules/reveal.js/plugin/markdown/marked.js' },
{ src: '../../node_modules/reveal.js/plugin/markdown/markdown.js' },
{ src: '../../node_modules/reveal.js/plugin/notes/notes.js', async: true },
{ src: '../../node_modules/reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
</body>
</html>
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@fortawesome/fontawesome-free@^6.1.1":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.2.tgz#d18880eddeadd42b1c64cb559f2f3d13d47a4a64"
integrity sha512-XwWADtfdSN73/udaFm+1mnGIj/ShDZNFMe/PRoqv3FhQ4GNI2PUN70yFTPsjq65Lw2C9i4TG5/hTbxXIXVCiqQ==
reveal.js@^3.5.0:
version "3.9.2"
resolved "https://registry.yarnpkg.com/reveal.js/-/reveal.js-3.9.2.tgz#7f63d3dfec338b6c313dcabdf006e8cf80e0b358"
integrity sha512-Dvv2oA9FrtOHE2DWj5js8pMRfwq++Wmvsn1EyAdYLC80lBjTphns+tPsB652Bnvep9AVviuVS/b4XoVY9rXHLA==
......@@ -67,7 +67,7 @@ function start_up(){
pelican_pid=$!
echo $pelican_pid > $PELICAN_PID
mkdir -p $OUTPUTDIR && cd $OUTPUTDIR
$PY -m pelican.server $port &
$PY -m pelican.server $port
srv_pid=$!
echo $srv_pid > $SRV_PID
cd $BASEDIR
......
version: '3.5'
services:
node:
image: node:latest
container_name: ${APP_NAME}_node
environment:
TZ: ${TIMEZONE:-Europe/Paris}
working_dir: /srv/blog
volumes:
- .:/srv/blog
command: bash /srv/blog/.docker/scripts/node/command.dev.sh
python:
image: ${NAMESPACE}/${APP_NAME}_python:latest
build:
context: .
dockerfile: .docker/build/python/Dockerfile
container_name: ${APP_NAME}_python
environment:
TZ: ${TIMEZONE:-Europe/Paris}
working_dir: /srv/blog
volumes:
- .:/srv/blog
ports:
- "${PORT-8000}:8000"
command: bash /srv/blog/.docker/scripts/python/command.dev.sh
kerberos:
image: ${NAMESPACE}/${APP_NAME}_kerberos:latest
build:
context: .
dockerfile: .docker/build/kerberos/Dockerfile
container_name: ${APP_NAME}_kerberos
environment:
TZ: ${TIMEZONE:-Europe/Paris}
volumes:
- .:/srv/blog
command: bash /srv/blog/.docker/scripts/kerberos/command.dev.sh
dockerize:
image: jwilder/dockerize
volumes:
- .:/tmp
\ No newline at end of file
## TODOS
* faire le challenge javascript au moment de javascript
* faire le challenge brute force + weak session dans auth
* faire le challenge DVWA CSP après XSS ajouter SOP et CORS => virer ou vider Browser
* faire le challenge captcha après CSRF
* reprendre le code du gist dans http et dans CSP pour faire des fetch dignes
* faire relire js à @tibertra & @thlecoub
##
sudo vi /etc/sudoers -> %sudo ALL=(ALL:ALL) ALL -> %sudo ALL=(ALL) NOPASSWD:ALL
sudo vi /etc/default/keyboard -> replace "us" by "fr"
default language in kali : https://www.youtube.com/watch?v=zCzxXc2SJ6Y
firefox / burp suite / kali / https://www.youtube.com/watch?v=Uzy28osev5g
simple api project / https://developer.okta.com/blog/2019/03/08/simple-rest-api-php
https://addons.mozilla.org/fr/firefox/addon/proxy-switcher-and-manager/
https://github.com/onhexgroup/Vulnerable-WordPress
https://sharpforce.gitbook.io/cybersecurity/walkthroughs/damn-vulnerable-web-application/damn-vulnerable-web-application-dvwa
https://medium.com/@dannybeton/dvwa-brute-force-tutorial-low-security-463880d53e50
\ No newline at end of file
......@@ -4,57 +4,104 @@ from __future__ import unicode_literals
import os
AUTHOR = 'Vincent Mazenod'
SITENAME = 'Vincent Mazenod / ingénieur d\'études'
SITENAME = 'Vincent Mazenod / ingénieur de recherche'
SITELOGO = 'images/mazenovi.png'
OIMPHOTO = 'images/oim.jpg'
FAVICON = 'images/favicon.ico'
SITEURL = 'https://fc.isima.fr/~mazenod'
DISQUS_SITENAME = 'limos-1'
DISQUS_SHORTNAME = 'mazenovi'
SITEURL = 'https://perso.limos.fr/~mazenovi/'
# DISQUS_SITENAME = 'limos-1'
# DISQUS_SHORTNAME = 'mazenovi'
LINKS = (
(
'user-circle',
'Farouk Toumani',
'http://www.isima.fr/~ftoumani/'
'http://isima.fr/~ftoumani/'
),
(
'user-circle',
'Pascal Lafourcade',
'http://sancy.univ-bpclermont.fr/~lafourcade/'
'https://sancy.iut.uca.fr/~lafourcade/index-en.html'
),
(
'user-circle',
'Christophe Rey',
'http://chrey.blogspot.com/'
),
(
'user-circle',
'Marie Pailloux',
'http://isima.fr/~pailloux/'
),
(
'user-circle',
'Engelbert Mephu Nguifo',
'http://ws.isima.fr/~mephu/'
)
'http://isima.fr/~mephu/'
),
(
'user-circle',
'Jean-Marie Favreau',
'https://jmfavreau.info/'
),
(
'user-circle',
'Pascal Huguet',
'https://lapsco.fr/la-direction.html'
),
)
RESEARCH = (
(
'https://limos.fr/static/limos/limos.png',
'width: 40px',
'LIMOS',
'https://limos.fr/detailperson/109',
),
(
'https://hal.science/assets/img/hal-logo-header.png',
'width: 20px',
'HAL',
'https://cv.hal.science/vincent-mazenod',
),
(
'https://static-00.iconduck.com/assets.00/google-scholar-icon-2048x2048-sjbhklt7.png',
'width: 20px',
'Google Scholar',
'https://scholar.google.com/citations?user=CFWYNi8AAAAJ',
),
)
SOCIAL = (
(
'building-o',
'users-cog',
'<span style="font-size:13px">CRI ISIMA/LIMOS</span>',
'https://hedgedoc.isima.fr/p/gD_i47uIi#/'
),
(
'building',
'<span style="font-size:13px">Bureau A115 - 1<sup>ère</sup> étage</span>',
'#'
),
# (
# 'phone',
# '04 73 40 50 27',
# '#'
# ),
(
'phone',
'04 73 40 50 27',
'#'
'envelope',
'<span style="font-size:13px">vincent.mazenod@limos.fr</span>',
'mailto:vincent.mazenod@limos.fr'
),
(
'envelope-o',
'<span style="font-size:13px">vincent.mazenod@isima.fr</span>',
'mailto:vincent.mazenod@isima.fr'
'linkedin',
'LinkedIn',
'pages/pgp.html'
),
(
'key',
'clé publique PGP',
'pages/pgp.html'
'https://www.linkedin.com/in/mazenovi'
),
(
'gitlab',
'gitlab.isima.fr',
'https://gitlab.isima.fr/mazenovi'
)
)
#INDEX_SAVE_AS = 'index.html'
......@@ -142,12 +189,13 @@ SHOW_ARTICLE_AUTHOR = False
SHOW_ARTICLE_CATEGORY = True
SHOW_DATE_MODIFIED = True
CUSTOM_CSS = 'static/custom.css'
STATIC_PATHS = ['images', 'extra/custom.css', 'node_modules', 'slides']
STATIC_PATHS = ['images', 'extra/custom.css', 'extra/custom.js', 'node_modules', 'slides']
ARTICLE_EXCLUDES = ['node_modules', 'slides']
PAGE_EXCLUDES = ['node_modules', 'slides']
STATIC_EXCLUDES = ['node_modules', 'slides']
EXTRA_PATH_METADATA = {
'extra/custom.css': {'path': 'static/custom.css'}
'extra/custom.css': {'path': 'static/custom.css'},
'extra/custom.js': {'path': 'static/custom.js'}
}
PYGMENTS_STYLE = "monokai"
USE_PAGER = True
......
=======================
I18N Sub-sites Plugin
=======================
This plugin extends the translations functionality by creating
internationalized sub-sites for the default site.
This plugin is designed for Pelican 3.4 and later.
What it does
============
1. When the content of the main site is being generated, the settings
are saved and the generation stops when content is ready to be
written. While reading source files and generating content objects,
the output queue is modified in certain ways:
- translations that will appear as native in a different (sub-)site
will be removed
- untranslated articles will be transformed to drafts if
``I18N_UNTRANSLATED_ARTICLES`` is ``'hide'`` (default), removed if
``'remove'`` or kept as they are if ``'keep'``.
- untranslated pages will be transformed into hidden pages if
``I18N_UNTRANSLATED_PAGES`` is ``'hide'`` (default), removed if
``'remove'`` or kept as they are if ``'keep'``.''
- additional content manipulation similar to articles and pages can
be specified for custom generators in the ``I18N_GENERATOR_INFO``
setting.
2. For each language specified in the ``I18N_SUBSITES`` dictionary the
settings overrides are applied to the settings from the main site
and a new sub-site is generated in the same way as with the main
site until content is ready to be written.
3. When all (sub-)sites are waiting for content writing, all removed
contents, translations and static files are interlinked across the
(sub-)sites.
4. Finally, all the output is written.
Setting it up
=============
For each extra used language code, a language-specific settings overrides
dictionary must be given (but can be empty) in the ``I18N_SUBSITES`` dictionary
.. code-block:: python
PLUGINS = ['i18n_subsites', ...]
# mapping: language_code -> settings_overrides_dict
I18N_SUBSITES = {
'cz': {
'SITENAME': 'Hezkej blog',
}
}
Default and special overrides
-----------------------------
The settings overrides may contain arbitrary settings, however, there
are some that are handled in a special way:
``SITEURL``
Any overrides to this setting should ensure that there is some level
of hierarchy between all (sub-)sites, because Pelican makes all URLs
relative to ``SITEURL`` and the plugin can only cross-link between
the sites using this hierarchy. For instance, with the main site
``http://example.com`` a sub-site ``http://example.com/de`` will
work, but ``http://de.example.com`` will not. If not overridden, the
language code (the language identifier used in the ``lang``
metadata) is appended to the main ``SITEURL`` for each sub-site.
``OUTPUT_PATH``, ``CACHE_PATH``
If not overridden, the language code is appended as with ``SITEURL``.
Separate cache paths are required as parser results depend on the locale.
``STATIC_PATHS``, ``THEME_STATIC_PATHS``
If not overridden, they are set to ``[]`` and all links to static
files are cross-linked to the main site.
``THEME``, ``THEME_STATIC_DIR``
If overridden, the logic with ``THEME_STATIC_PATHS`` does not apply.
``DEFAULT_LANG``
This should not be overridden as the plugin changes it to the
language code of each sub-site to change what is perceived as translations.
Localizing templates
--------------------
Most importantly, this plugin can use localized templates for each
sub-site. There are two approaches to having the templates localized:
- You can set a different ``THEME`` override for each language in
``I18N_SUBSITES``, e.g. by making a copy of a theme ``my_theme`` to
``my_theme_lang`` and then editing the templates in the new
localized theme. This approach means you don't have to deal with
gettext ``*.po`` files, but it is harder to maintain over time.
- You use only one theme and localize the templates using the
`jinja2.ext.i18n Jinja2 extension
<http://jinja.pocoo.org/docs/templates/#i18n>`_. For a kickstart
read this `guide <./localizing_using_jinja2.rst>`_.
Additional context variables
............................
It may be convenient to add language buttons to your theme in addition
to the translation links of articles and pages. These buttons could,
for example, point to the ``SITEURL`` of each (sub-)site. For this
reason the plugin adds these variables to the template context:
``main_lang``
The language of the main site — the original ``DEFAULT_LANG``
``main_siteurl``
The ``SITEURL`` of the main site — the original ``SITEURL``
``lang_siteurls``
An ordered dictionary, mapping all used languages to their
``SITEURL``. The ``main_lang`` is the first key with ``main_siteurl``
as the value. This dictionary is useful for implementing global
language buttons that show the language of the currently viewed
(sub-)site too.
``extra_siteurls``
An ordered dictionary, subset of ``lang_siteurls``, the current
``DEFAULT_LANG`` of the rendered (sub-)site is not included, so for
each (sub-)site ``set(extra_siteurls) == set(lang_siteurls) -
set([DEFAULT_LANG])``. This dictionary is useful for implementing
global language buttons that do not show the current language.
``relpath_to_site``
A function that returns a relative path from the first (sub-)site to
the second (sub-)site where the (sub-)sites are identified by the
language codes given as two arguments.
If you don't like the default ordering of the ordered dictionaries,
use a Jinja2 filter to alter the ordering.
All the siteurls above are always absolute even in the case of
``RELATIVE_URLS == True`` (it would be to complicated to replicate the
Pelican internals for local siteurls), so you may rather use something
like ``{{ SITEURL }}/{{ relpath_to_site(DEFAULT_LANG, main_lang }}``
to link to the main site.
This short `howto <./implementing_language_buttons.rst>`_ shows two
example implementations of language buttons.
Usage notes
===========
- It is **mandatory** to specify ``lang`` metadata for each article
and page as ``DEFAULT_LANG`` is later changed for each sub-site, so
content without ``lang`` metadata would be rendered in every
(sub-)site.
- As with the original translations functionality, ``slug`` metadata
is used to group translations. It is therefore often convenient to
compensate for this by overriding the content URL (which defaults to
slug) using the ``url`` and ``save_as`` metadata. You could also
give articles e.g. ``name`` metadata and use it in ``ARTICLE_URL =
'{name}.html'``.
Development
===========
- A demo and a test site is in the ``gh-pages`` branch and can be seen
at http://smartass101.github.io/pelican-plugins/
from .i18n_subsites import *
File deleted
"""i18n_subsites plugin creates i18n-ized subsites of the default site
This plugin is designed for Pelican 3.4 and later
"""
import os
import six
import logging
import posixpath
from copy import copy
from itertools import chain
from operator import attrgetter
from collections import OrderedDict
from contextlib import contextmanager
from six.moves.urllib.parse import urlparse
import gettext
import locale
from pelican import signals
from pelican.generators import ArticlesGenerator, PagesGenerator
from pelican.settings import configure_settings
from pelican.contents import Draft
# Global vars
_MAIN_SETTINGS = None # settings dict of the main Pelican instance
_MAIN_LANG = None # lang of the main Pelican instance
_MAIN_SITEURL = None # siteurl of the main Pelican instance
_MAIN_STATIC_FILES = None # list of Static instances the main Pelican instance
_SUBSITE_QUEUE = {} # map: lang -> settings overrides
_SITE_DB = OrderedDict() # OrderedDict: lang -> siteurl
_SITES_RELPATH_DB = {} # map: (lang, base_lang) -> relpath
# map: generator -> list of removed contents that need interlinking
_GENERATOR_DB = {}
_NATIVE_CONTENT_URL_DB = {} # map: source_path -> content in its native lang
_LOGGER = logging.getLogger(__name__)
@contextmanager
def temporary_locale(temp_locale=None):
'''Enable code to run in a context with a temporary locale
Resets the locale back when exiting context.
Can set a temporary locale if provided
'''
orig_locale = locale.setlocale(locale.LC_ALL)
if temp_locale is not None:
locale.setlocale(locale.LC_ALL, temp_locale)
yield
locale.setlocale(locale.LC_ALL, orig_locale)
def initialize_dbs(settings):
'''Initialize internal DBs using the Pelican settings dict
This clears the DBs for e.g. autoreload mode to work
'''
global _MAIN_SETTINGS, _MAIN_SITEURL, _MAIN_LANG, _SUBSITE_QUEUE
_MAIN_SETTINGS = settings
_MAIN_LANG = settings['DEFAULT_LANG']
_MAIN_SITEURL = settings['SITEURL']
_SUBSITE_QUEUE = settings.get('I18N_SUBSITES', {}).copy()
prepare_site_db_and_overrides()
# clear databases in case of autoreload mode
_SITES_RELPATH_DB.clear()
_NATIVE_CONTENT_URL_DB.clear()
_GENERATOR_DB.clear()
def prepare_site_db_and_overrides():
'''Prepare overrides and create _SITE_DB
_SITE_DB.keys() need to be ready for filter_translations
'''
_SITE_DB.clear()
_SITE_DB[_MAIN_LANG] = _MAIN_SITEURL
# make sure it works for both root-relative and absolute
main_siteurl = '/' if _MAIN_SITEURL == '' else _MAIN_SITEURL
for lang, overrides in _SUBSITE_QUEUE.items():
if 'SITEURL' not in overrides:
overrides['SITEURL'] = posixpath.join(main_siteurl, lang)
_SITE_DB[lang] = overrides['SITEURL']
# default subsite hierarchy
if 'OUTPUT_PATH' not in overrides:
overrides['OUTPUT_PATH'] = os.path.join(
_MAIN_SETTINGS['OUTPUT_PATH'], lang)
if 'CACHE_PATH' not in overrides:
overrides['CACHE_PATH'] = os.path.join(
_MAIN_SETTINGS['CACHE_PATH'], lang)
if 'STATIC_PATHS' not in overrides:
overrides['STATIC_PATHS'] = []
if ('THEME' not in overrides and 'THEME_STATIC_DIR' not in overrides and
'THEME_STATIC_PATHS' not in overrides):
relpath = relpath_to_site(lang, _MAIN_LANG)
overrides['THEME_STATIC_DIR'] = posixpath.join(
relpath, _MAIN_SETTINGS['THEME_STATIC_DIR'])
overrides['THEME_STATIC_PATHS'] = []
# to change what is perceived as translations
overrides['DEFAULT_LANG'] = lang
def subscribe_filter_to_signals(settings):
'''Subscribe content filter to requested signals'''
for sig in settings.get('I18N_FILTER_SIGNALS', []):
sig.connect(filter_contents_translations)
def initialize_plugin(pelican_obj):
'''Initialize plugin variables and Pelican settings'''
if _MAIN_SETTINGS is None:
initialize_dbs(pelican_obj.settings)
subscribe_filter_to_signals(pelican_obj.settings)
def get_site_path(url):
'''Get the path component of an url, excludes siteurl
also normalizes '' to '/' for relpath to work,
otherwise it could be interpreted as a relative filesystem path
'''
path = urlparse(url).path
if path == '':
path = '/'
return path
def relpath_to_site(lang, target_lang):
'''Get relative path from siteurl of lang to siteurl of base_lang
the output is cached in _SITES_RELPATH_DB
'''
path = _SITES_RELPATH_DB.get((lang, target_lang), None)
if path is None:
siteurl = _SITE_DB.get(lang, _MAIN_SITEURL)
target_siteurl = _SITE_DB.get(target_lang, _MAIN_SITEURL)
path = posixpath.relpath(get_site_path(target_siteurl),
get_site_path(siteurl))
_SITES_RELPATH_DB[(lang, target_lang)] = path
return path
def save_generator(generator):
'''Save the generator for later use
initialize the removed content list
'''
_GENERATOR_DB[generator] = []
def article2draft(article):
'''Transform an Article to Draft'''
draft = Draft(article._content, article.metadata, article.settings,
article.source_path, article._context)
draft.status = 'draft'
return draft
def page2hidden_page(page):
'''Transform a Page to a hidden Page'''
page.status = 'hidden'
return page
class GeneratorInspector(object):
'''Inspector of generator instances'''
generators_info = {
ArticlesGenerator: {
'translations_lists': ['translations', 'drafts_translations'],
'contents_lists': [('articles', 'drafts')],
'hiding_func': article2draft,
'policy': 'I18N_UNTRANSLATED_ARTICLES',
},
PagesGenerator: {
'translations_lists': ['translations', 'hidden_translations'],
'contents_lists': [('pages', 'hidden_pages')],
'hiding_func': page2hidden_page,
'policy': 'I18N_UNTRANSLATED_PAGES',
},
}
def __init__(self, generator):
'''Identify the best known class of the generator instance
The class '''
self.generator = generator
self.generators_info.update(generator.settings.get(
'I18N_GENERATORS_INFO', {}))
for cls in generator.__class__.__mro__:
if cls in self.generators_info:
self.info = self.generators_info[cls]
break
else:
self.info = {}
def translations_lists(self):
'''Iterator over lists of content translations'''
return (getattr(self.generator, name) for name in
self.info.get('translations_lists', []))
def contents_list_pairs(self):
'''Iterator over pairs of normal and hidden contents'''
return (tuple(getattr(self.generator, name) for name in names)
for names in self.info.get('contents_lists', []))
def hiding_function(self):
'''Function for transforming content to a hidden version'''
hiding_func = self.info.get('hiding_func', lambda x: x)
return hiding_func
def untranslated_policy(self, default):
'''Get the policy for untranslated content'''
return self.generator.settings.get(self.info.get('policy', None),
default)
def all_contents(self):
'''Iterator over all contents'''
translations_iterator = chain(*self.translations_lists())
return chain(translations_iterator,
*(pair[i] for pair in self.contents_list_pairs()
for i in (0, 1)))
def filter_contents_translations(generator):
'''Filter the content and translations lists of a generator
Filters out
1) translations which will be generated in a different site
2) content that is not in the language of the currently
generated site but in that of a different site, content in a
language which has no site is generated always. The filtering
method bay be modified by the respective untranslated policy
'''
inspector = GeneratorInspector(generator)
current_lang = generator.settings['DEFAULT_LANG']
langs_with_sites = _SITE_DB.keys()
removed_contents = _GENERATOR_DB[generator]
for translations in inspector.translations_lists():
for translation in translations[:]: # copy to be able to remove
if translation.lang in langs_with_sites:
translations.remove(translation)
removed_contents.append(translation)
hiding_func = inspector.hiding_function()
untrans_policy = inspector.untranslated_policy(default='hide')
for (contents, other_contents) in inspector.contents_list_pairs():
for content in other_contents: # save any hidden native content first
if content.lang == current_lang: # in native lang
# save the native URL attr formatted in the current locale
_NATIVE_CONTENT_URL_DB[content.source_path] = content.url
for content in contents[:]: # copy for removing in loop
if content.lang == current_lang: # in native lang
# save the native URL attr formatted in the current locale
_NATIVE_CONTENT_URL_DB[content.source_path] = content.url
elif content.lang in langs_with_sites and untrans_policy != 'keep':
contents.remove(content)
if untrans_policy == 'hide':
other_contents.append(hiding_func(content))
elif untrans_policy == 'remove':
removed_contents.append(content)
def install_templates_translations(generator):
'''Install gettext translations in the jinja2.Environment
Only if the 'jinja2.ext.i18n' jinja2 extension is enabled
the translations for the current DEFAULT_LANG are installed.
'''
if 'JINJA_ENVIRONMENT' in generator.settings: # pelican 3.7+
jinja_extensions = generator.settings['JINJA_ENVIRONMENT'].get(
'extensions', [])
else:
jinja_extensions = generator.settings['JINJA_EXTENSIONS']
if 'jinja2.ext.i18n' in jinja_extensions:
domain = generator.settings.get('I18N_GETTEXT_DOMAIN', 'messages')
localedir = generator.settings.get('I18N_GETTEXT_LOCALEDIR')
if localedir is None:
localedir = os.path.join(generator.theme, 'translations')
current_lang = generator.settings['DEFAULT_LANG']
if current_lang == generator.settings.get('I18N_TEMPLATES_LANG',
_MAIN_LANG):
translations = gettext.NullTranslations()
else:
langs = [current_lang]
try:
translations = gettext.translation(domain, localedir, langs)
except (IOError, OSError):
_LOGGER.error((
"Cannot find translations for language '{}' in '{}' with "
"domain '{}'. Installing NullTranslations.").format(
langs[0], localedir, domain))
translations = gettext.NullTranslations()
newstyle = generator.settings.get('I18N_GETTEXT_NEWSTYLE', True)
generator.env.install_gettext_translations(translations, newstyle)
def add_variables_to_context(generator):
'''Adds useful iterable variables to template context'''
context = generator.context # minimize attr lookup
context['relpath_to_site'] = relpath_to_site
context['main_siteurl'] = _MAIN_SITEURL
context['main_lang'] = _MAIN_LANG
context['lang_siteurls'] = _SITE_DB
current_lang = generator.settings['DEFAULT_LANG']
extra_siteurls = _SITE_DB.copy()
extra_siteurls.pop(current_lang)
context['extra_siteurls'] = extra_siteurls
def interlink_translations(content):
'''Link content to translations in their main language
so the URL (including localized month names) of the different subsites
will be honored
'''
lang = content.lang
# sort translations by lang
content.translations.sort(key=attrgetter('lang'))
for translation in content.translations:
relpath = relpath_to_site(lang, translation.lang)
url = _NATIVE_CONTENT_URL_DB[translation.source_path]
translation.override_url = posixpath.join(relpath, url)
def interlink_translated_content(generator):
'''Make translations link to the native locations
for generators that may contain translated content
'''
inspector = GeneratorInspector(generator)
for content in inspector.all_contents():
interlink_translations(content)
def interlink_removed_content(generator):
'''For all contents removed from generation queue update interlinks
link to the native location
'''
current_lang = generator.settings['DEFAULT_LANG']
for content in _GENERATOR_DB[generator]:
url = _NATIVE_CONTENT_URL_DB[content.source_path]
relpath = relpath_to_site(current_lang, content.lang)
content.override_url = posixpath.join(relpath, url)
def interlink_static_files(generator):
'''Add links to static files in the main site if necessary'''
if generator.settings['STATIC_PATHS'] != []:
return # customized STATIC_PATHS
filenames = generator.context['filenames'] # minimize attr lookup
relpath = relpath_to_site(generator.settings['DEFAULT_LANG'], _MAIN_LANG)
for staticfile in _MAIN_STATIC_FILES:
if staticfile.get_relative_source_path() not in filenames:
staticfile = copy(staticfile) # prevent override in main site
staticfile.override_url = posixpath.join(relpath, staticfile.url)
generator.add_source_path(staticfile)
def save_main_static_files(static_generator):
'''Save the static files generated for the main site'''
global _MAIN_STATIC_FILES
# test just for current lang as settings change in autoreload mode
if static_generator.settings['DEFAULT_LANG'] == _MAIN_LANG:
_MAIN_STATIC_FILES = static_generator.staticfiles
def update_generators():
'''Update the context of all generators
Ads useful variables and translations into the template context
and interlink translations
'''
for generator in _GENERATOR_DB.keys():
install_templates_translations(generator)
add_variables_to_context(generator)
interlink_static_files(generator)
interlink_removed_content(generator)
interlink_translated_content(generator)
def get_pelican_cls(settings):
'''Get the Pelican class requested in settings'''
cls = settings['PELICAN_CLASS']
if isinstance(cls, six.string_types):
module, cls_name = cls.rsplit('.', 1)
module = __import__(module)
cls = getattr(module, cls_name)
return cls
def create_next_subsite(pelican_obj):
'''Create the next subsite using the lang-specific config
If there are no more subsites in the generation queue, update all
the generators (interlink translations and removed content, add
variables and translations to template context). Otherwise get the
language and overrides for next the subsite in the queue and apply
overrides. Then generate the subsite using a PELICAN_CLASS
instance and its run method. Finally, restore the previous locale.
'''
global _MAIN_SETTINGS
if len(_SUBSITE_QUEUE) == 0:
_LOGGER.debug(
'i18n: Updating cross-site links and context of all generators.')
update_generators()
_MAIN_SETTINGS = None # to initialize next time
else:
with temporary_locale():
settings = _MAIN_SETTINGS.copy()
lang, overrides = _SUBSITE_QUEUE.popitem()
settings.update(overrides)
settings = configure_settings(settings) # to set LOCALE, etc.
cls = get_pelican_cls(settings)
new_pelican_obj = cls(settings)
_LOGGER.debug(("Generating i18n subsite for language '{}' "
"using class {}").format(lang, cls))
new_pelican_obj.run()
# map: signal name -> function name
_SIGNAL_HANDLERS_DB = {
'get_generators': initialize_plugin,
'article_generator_pretaxonomy': filter_contents_translations,
'page_generator_finalized': filter_contents_translations,
'get_writer': create_next_subsite,
'static_generator_finalized': save_main_static_files,
'generator_init': save_generator,
}
def register():
'''Register the plugin only if required signals are available'''
for sig_name in _SIGNAL_HANDLERS_DB.keys():
if not hasattr(signals, sig_name):
_LOGGER.error((
'The i18n_subsites plugin requires the {} '
'signal available for sure in Pelican 3.4.0 and later, '
'plugin will not be used.').format(sig_name))
return
for sig_name, handler in _SIGNAL_HANDLERS_DB.items():
sig = getattr(signals, sig_name)
sig.connect(handler)
File deleted
-----------------------------
Implementing language buttons
-----------------------------
Each article with translations has translations links, but that's the
only way to switch between language subsites.
For this reason it is convenient to add language buttons to the top
menu bar to make it simple to switch between the language subsites on
all pages.
Example designs
---------------
Language buttons showing other available languages
..................................................
The ``extra_siteurls`` dictionary is a mapping of all other (not the
``DEFAULT_LANG`` of the current (sub-)site) languages to the
``SITEURL`` of the respective (sub-)sites
.. code-block:: jinja
<!-- SNIP -->
<nav><ul>
{% if extra_siteurls %}
{% for lang, url in extra_siteurls.items() %}
<li><a href="{{ url }}/">{{ lang }}</a></li>
{% endfor %}
<!-- separator -->
<li style="background-color: white; padding: 5px;">&nbsp</li>
{% endif %}
{% for title, link in MENUITEMS %}
<!-- SNIP -->
Notice the slash ``/`` after ``{{ url }}``, this makes sure it works
with local development when ``SITEURL == ''``.
Language buttons showing all available languages, current is active
...................................................................
The ``extra_siteurls`` dictionary is a mapping of all languages to the
``SITEURL`` of the respective (sub-)sites. This template sets the
language of the current (sub-)site as active.
.. code-block:: jinja
<!-- SNIP -->
<nav><ul>
{% if lang_siteurls %}
{% for lang, url in lang_siteurls.items() %}
<li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ url }}/">{{ lang }}</a></li>
{% endfor %}
<!-- separator -->
<li style="background-color: white; padding: 5px;">&nbsp</li>
{% endif %}
{% for title, link in MENUITEMS %}
<!-- SNIP -->
Tips and tricks
---------------
Showing more than language codes
................................
If you want the language buttons to show e.g. the names of the
languages or flags [#flags]_, not just the language codes, you can use
a jinja filter to translate the language codes
.. code-block:: python
languages_lookup = {
'en': 'English',
'cz': 'Čeština',
}
def lookup_lang_name(lang_code):
return languages_lookup[lang_code]
JINJA_FILTERS = {
...
'lookup_lang_name': lookup_lang_name,
}
And then the link content becomes
.. code-block:: jinja
<!-- SNIP -->
{% for lang, siteurl in lang_siteurls.items() %}
<li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ siteurl }}/">{{ lang | lookup_lang_name }}</a></li>
{% endfor %}
<!-- SNIP -->
Changing the order of language buttons
......................................
Because ``lang_siteurls`` and ``extra_siteurls`` are instances of
``OrderedDict`` with ``main_lang`` being always the first key, you can
change the order through a jinja filter.
.. code-block:: python
def my_ordered_items(ordered_dict):
items = list(ordered_dict.items())
# swap first and last using tuple unpacking
items[0], items[-1] = items[-1], items[0]
return items
JINJA_FILTERS = {
...
'my_ordered_items': my_ordered_items,
}
And then the ``for`` loop line in the template becomes
.. code-block:: jinja
<!-- SNIP -->
{% for lang, siteurl in lang_siteurls | my_ordered_items %}
<!-- SNIP -->
.. [#flags] Although it may look nice, `w3 discourages it
<http://www.w3.org/TR/i18n-html-tech-lang/#ri20040808.173208643>`_.
-----------------------------
Localizing themes with Jinja2
-----------------------------
1. Localize templates
---------------------
To enable the |ext| extension in your templates, you must add it to
``JINJA_EXTENSIONS`` in your Pelican configuration
.. code-block:: python
JINJA_EXTENSIONS = ['jinja2.ext.i18n', ...]
Then follow the `Jinja2 templating documentation for the I18N plugin
<http://jinja.pocoo.org/docs/templates/#i18n>`_ to make your templates
localizable. This usually means surrounding strings with the ``{%
trans %}`` directive or using ``gettext()`` in expressions
.. code-block:: jinja
{% trans %}translatable content{% endtrans %}
{{ gettext('a translatable string') }}
For pluralization support, etc. consult the documentation.
To enable `newstyle gettext calls
<http://jinja.pocoo.org/docs/extensions/#newstyle-gettext>`_ the
``I18N_GETTEXT_NEWSTYLE`` config variable must be set to ``True``
(default).
.. |ext| replace:: ``jinja2.ext.i18n``
2. Specify translations location
--------------------------------
The |ext| extension uses the `Python gettext library
<http://docs.python.org/library/gettext.html>`_ for translating
strings.
In your Pelican config you can give the path in which to look for
translations in the ``I18N_GETTEXT_LOCALEDIR`` variable. If not given,
it is assumed to be the ``translations`` subfolder in the top folder
of the theme specified by ``THEME``.
The domain of the translations (the name of each translation file is
``domain.mo``) is controlled by the ``I18N_GETTEXT_DOMAIN`` config
variable (defaults to ``messages``).
Example
.......
With the following in your Pelican settings file
.. code-block:: python
I18N_GETTEXT_LOCALEDIR = 'some/path/'
I18N_GETTEXT_DOMAIN = 'my_domain'
the translation for language 'cz' will be expected to be in
``some/path/cz/LC_MESSAGES/my_domain.mo``
3. Extract translatable strings and translate them
--------------------------------------------------
There are many ways to extract translatable strings and create
``gettext`` compatible translations. You can create the ``*.po`` and
``*.mo`` message catalog files yourself, or you can use some helper
tool as described in `the Python gettext library tutorial
<http://docs.python.org/library/gettext.html#internationalizing-your-programs-and-modules>`_.
You of course don't need to provide a translation for the language in
which the templates are written which is assumed to be the original
``DEFAULT_LANG``. This can be overridden in the
``I18N_TEMPLATES_LANG`` variable.
Recommended tool: babel
.......................
`Babel <http://babel.pocoo.org/>`_ makes it easy to extract
translatable strings from the localized Jinja2 templates and assists
with creating translations as documented in this `Jinja2-Babel
tutorial
<http://pythonhosted.org/Flask-Babel/#translating-applications>`_
[#flask]_ on which the following is based.
1. Add babel mapping
~~~~~~~~~~~~~~~~~~~~
Let's assume that you are localizing a theme in ``themes/my_theme/``
and that you use the default settings, i.e. the default domain
``messages`` and will put the translations in the ``translations``
subdirectory of the theme directory as
``themes/my_theme/translations/``.
It is up to you where to store babel mappings and translation files
templates (``*.pot``), but a convenient place is to put them in
``themes/my_theme/`` and work in that directory. From now on let's
assume that it will be our current working directory (CWD).
To tell babel to extract translatable strings from the templates
create a mapping file ``babel.cfg`` with the following line
.. code-block:: cfg
[jinja2: templates/**.html]
2. Extract translatable strings from templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run the following command to create a ``messages.pot`` message catalog
template file from extracted translatable strings
.. code-block:: bash
pybabel extract --mapping babel.cfg --output messages.pot ./
3. Initialize message catalogs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to translate the template to language ``lang``, run the
following command to create a message catalog
``translations/lang/LC_MESSAGES/messages.po`` using the template
``messages.pot``
.. code-block:: bash
pybabel init --input-file messages.pot --output-dir translations/ --locale lang --domain messages
babel expects ``lang`` to be a valid locale identifier, so if e.g. you
are translating for language ``cz`` but the corresponding locale is
``cs``, you have to use the locale identifier. Nevertheless, the
gettext infrastructure should later correctly find the locale for a
given language.
4. Fill the message catalogs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The message catalog files format is quite intuitive, it is fully
documented in the `GNU gettext manual
<http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files>`_. Essentially,
you fill in the ``msgstr`` strings
.. code-block:: po
msgid "just a simple string"
msgstr "jenom jednoduchý řetězec"
msgid ""
"some multiline string"
"looks like this"
msgstr ""
"nějaký více řádkový řetězec"
"vypadá takto"
You might also want to remove ``#,fuzzy`` flags once the translation
is complete and reviewed to show that it can be compiled.
5. Compile the message catalogs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The message catalogs must be compiled into binary format using this
command
.. code-block:: bash
pybabel compile --directory translations/ --domain messages
This command might complain about "fuzzy" translations, which means
you should review the translations and once done, remove the fuzzy
flag line.
(6.) Update the catalogs when templates change
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you add any translatable patterns into your templates, you have to
update your message catalogs too. First you extract a new message
catalog template as described in the 2. step. Then you run the
following command [#pybabel_error]_
.. code-block:: bash
pybabel update --input-file messages.pot --output-dir translations/ --domain messages
This will merge the new patterns with the old ones. Once you review
and fill them, you have to recompile them as described in the 5. step.
.. [#flask] Although the tutorial is focused on Flask-based web
applications, the linked translation tutorial is not
Flask-specific.
.. [#pybabel_error] If you get an error ``TypeError: must be str, not
bytes`` with Python 3.3, it is likely you are
suffering from this `bug
<https://github.com/mitsuhiko/flask-babel/issues/43>`_.
Until the fix is released, you can use babel with
Python 2.7.
404 stránka
===========
:slug: 404
:lang: cz
:status: hidden
Jednoduchá 404 stránka.