From 02ef7c55e9b7b0a746f6bb8b7d991d2f95600359 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 15:39:50 +0200
Subject: [PATCH 1/7] :book: doc readme

---
 README.md    |   7 ++-
 index.js     | 160 ++++++++++++++++++++++++++-------------------------
 package.json |   1 +
 3 files changed, 89 insertions(+), 79 deletions(-)

diff --git a/README.md b/README.md
index f83adc5..d247a6b 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,10 @@ Duniter graphical interface. This is a developement package which is embedded in
 > Requires [Yarn](https://classic.yarnpkg.com/en/docs/install/)
 
 ```bash
+npm i -g nvm yarn
 nvm install 9
 nvm use 9
-yarn
+yarn --pure-lockfile
 ```
 
 ## Run
@@ -18,3 +19,7 @@ yarn
 ```bash
 node run.js direct_webstart
 ```
+or
+```bash
+node_modules/brunch/bin/brunch watch --server
+```
diff --git a/index.js b/index.js
index faed8ca..d9a9e8b 100644
--- a/index.js
+++ b/index.js
@@ -1,12 +1,12 @@
-"use strict";
+'use strict';
 
-const _ = require('underscore')
+const _ = require('underscore');
 const co = require('co');
 const fs = require('fs');
 const bodyParser = require('body-parser');
-const http    = require('http');
+const http = require('http');
 const express = require('express');
-const path    = require('path');
+const path = require('path');
 const cors = require('cors');
 const fileUpload = require('express-fileupload');
 const webminController = require('./server/controller/webmin.js');
@@ -23,34 +23,34 @@ module.exports = {
 
       // Webmin options
       { value: '--webmhost <host>', desc: 'Local network interface to connect to (IP)' },
-      { value: '--webmport <port>', desc: 'Local network port to connect', parser: parseInt }
+      { value: '--webmport <port>', desc: 'Local network port to connect', parser: parseInt },
     ],
 
     cli: [{
 
-      name: 'webstart',
-      desc: 'Starts Duniter as a daemon (background task).',
-      logs: false,
-      onConfiguredExecute: (server, conf, program, params) => co(function*() {
-        yield server.checkConfig()
-        const daemon = server.getDaemon('direct_webstart', 'webstart')
-        yield startDaemon(program, daemon)
-      })
+      name               : 'webstart',
+      desc               : 'Starts Duniter as a daemon (background task).',
+      logs               : false,
+      onConfiguredExecute: (server, conf, program, params) => co(function* () {
+        yield server.checkConfig();
+        const daemon = server.getDaemon('direct_webstart', 'webstart');
+        yield startDaemon(program, daemon);
+      }),
     }, {
 
-      name: 'webrestart',
-      desc: 'Stops Duniter daemon and restart it with its web interface.',
-      logs: false,
-      onConfiguredExecute: (server, conf, program, params) => co(function*() {
-        yield server.checkConfig()
-        const daemon = server.getDaemon('direct_webstart', 'webrestart')
-        yield stopDaemon(daemon)
-        yield startDaemon(program, daemon)
-      })
+      name               : 'webrestart',
+      desc               : 'Stops Duniter daemon and restart it with its web interface.',
+      logs               : false,
+      onConfiguredExecute: (server, conf, program, params) => co(function* () {
+        yield server.checkConfig();
+        const daemon = server.getDaemon('direct_webstart', 'webrestart');
+        yield stopDaemon(daemon);
+        yield startDaemon(program, daemon);
+      }),
     }, {
-      name: 'direct_webstart',
-      desc: 'Do a webstart',
-      onDatabaseExecute: (server, conf, program, params, startServices, stopServices, stack) => co(function*(){
+      name             : 'direct_webstart',
+      desc             : 'Do a webstart',
+      onDatabaseExecute: (server, conf, program, params, startServices, stopServices, stack) => co(function* () {
 
         try {
 
@@ -73,35 +73,36 @@ module.exports = {
           app.use(fileUpload());
 
           app.use(bodyParser.urlencoded({
-            extended: true
+            extended: true,
           }));
           app.use(bodyParser.json());
 
           const wbmin = webminController(server, startServices, stopServices, listDuniterPlugins, stack);
           const httpServer = http.createServer(app);
           httpServer.listen(PORT, HOTE);
-          server.logger.info("Web administration accessible at following address: http://%s:%s", HOTE, PORT);
+          server.logger.info('Web administration accessible at following address: http://%s:%s', HOTE, PORT);
 
           require('./server/lib/routes').webmin(wbmin, app);
           require('./server/lib/routes').webminWS(wbmin)(httpServer);
 
-          const uiDeps = listDuniterUIPlugins()
+          const uiDeps = listDuniterUIPlugins();
           for (const dep of uiDeps) {
             // Eventual HTTP routing
             if (dep.required.duniterUI.route) {
-              const subApp = express()
-              dep.required.duniterUI.route(subApp, server, conf, program, params)
-              app.use('/modules/', subApp)
+              const subApp = express();
+              dep.required.duniterUI.route(subApp, server, conf, program, params);
+              app.use('/modules/', subApp);
             }
           }
 
-          const currentBlock = yield server.dal.getCurrentBlockOrNull()
+          const currentBlock = yield server.dal.getCurrentBlockOrNull();
           if (currentBlock) {
-            yield wbmin.startAllServices()
+            yield wbmin.startAllServices();
           }
 
           // Never ending promise
-          return new Promise((resolve) => {});
+          return new Promise((resolve) => {
+          });
 
           /****************************************/
 
@@ -109,30 +110,30 @@ module.exports = {
           console.error(e);
           process.exit(1);
         }
-      })
-    }]
-  }
+      }),
+    }],
+  },
 };
 
 function startDaemon(program, daemon) {
-  return co(function*() {
+  return co(function* () {
 
-    const PORT = program.webmport || 9220
+    const PORT = program.webmport || 9221;
 
     const isPortAlreadyTaken = yield new Promise((resolve) => {
-      isPortTaken(PORT, (err, taken) => err ? reject(err) : resolve(taken))
-    })
+      isPortTaken(PORT, (err, taken) => err ? reject(err) : resolve(taken));
+    });
 
     if (isPortAlreadyTaken) {
-      console.error('Port ' + PORT + ' already used.')
-      process.exit(3)
+      console.error('Port ' + PORT + ' already used.');
+      process.exit(3);
     }
 
     return new Promise((resolve, reject) => daemon.start((err) => {
-      if (err) return reject(err)
-      resolve()
-    }))
-  })
+      if (err) return reject(err);
+      resolve();
+    }));
+  });
 }
 
 /**
@@ -143,67 +144,70 @@ function startDaemon(program, daemon) {
  * @param fn
  */
 function isPortTaken(port, fn) {
-  const net = require('net')
+  const net = require('net');
   const tester = net.createServer()
     .once('error', function (err) {
-      if (err.code != 'EADDRINUSE') return fn(err)
-      fn(null, true)
+      if (err.code != 'EADDRINUSE') return fn(err);
+      fn(null, true);
     })
-    .once('listening', function() {
-      tester.once('close', function() { fn(null, false) })
-        .close()
+    .once('listening', function () {
+      tester.once('close', function () {
+        fn(null, false);
+      })
+        .close();
     })
-    .listen(port)
+    .listen(port);
 }
 
 function stopDaemon(daemon) {
   return new Promise((resolve, reject) => daemon.stop((err) => {
     err && console.error(err);
-    if (err) return reject(err)
-    resolve()
-  }))
+    if (err) return reject(err);
+    resolve();
+  }));
 }
 
 function listDuniterPlugins() {
-  return listPlugins(r => !!r.duniter || !!r.duniterUI)
+  return listPlugins(r => !!r.duniter || !!r.duniterUI);
 }
 
 function listDuniterUIPlugins() {
-  return listPlugins(r => !!r.duniterUI)
+  return listPlugins(r => !!r.duniterUI);
 }
 
 function listPlugins(conditionTest) {
-  const uiDependencies = []
-  const pathToPackageJSON = path.resolve('./package.json')
-  const pkgJSON = JSON.parse(fs.readFileSync(pathToPackageJSON, 'utf8'))
-  const peerDeps = pkgJSON.peerDependencies || {}
-  const allDeps = _.extend(pkgJSON.dependencies || {}, pkgJSON.devDependencies || {})
-  const deps = Object.keys(allDeps)
+  const uiDependencies = [];
+  const pathToPackageJSON = path.resolve('./package.json');
+  const pkgJSON = JSON.parse(fs.readFileSync(pathToPackageJSON, 'utf8'));
+  const peerDeps = pkgJSON.peerDependencies || {};
+  const allDeps = _.extend(pkgJSON.dependencies || {}, pkgJSON.devDependencies || {});
+  const deps = Object.keys(allDeps);
   for (const dep of deps) {
     try {
-      const required = require(dep)
+      const required = require(dep);
       if (required && conditionTest(required)) {
         uiDependencies.push({
-          name: dep,
+          name   : dep,
           version: allDeps[dep],
-          locked: !!peerDeps[dep],
-          required
-        })
+          locked : !!peerDeps[dep],
+          required,
+        });
       }
-    } catch (e) {}
+    } catch (e) {
+    }
   }
   // Special: self dependency (if local package is also a module)
   if (pkgJSON.main && pkgJSON.main.match(/\.js/)) { // With NW.js, the main is an index.html file, which causes a bug
-    const dep = pkgJSON.name
-    const required = require(path.resolve('./' + pkgJSON.main))
+    const dep = pkgJSON.name;
+    const required = require(path.resolve('./' + pkgJSON.main));
     if (required && conditionTest(required)) {
       uiDependencies.push({
-        name: dep,
+        name   : dep,
         version: 'local',
-        locked: true,
-        required
-      })
+        locked : true,
+        required,
+      });
     }
   }
-  return uiDependencies
+  return uiDependencies;
 }
diff --git a/package.json b/package.json
index 9407d26..bec9b2f 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "b": "brunch build",
     "watch": "brunch watch",
     "start": "cd cesium && npm start",
+    "webstart": "node run.js direct_webstart",
     "test": "mocha --growl tests/"
   },
   "repository": {
-- 
GitLab


From 65acef7051e43b1c53e411717df57d6a6fde2822 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 16:28:56 +0200
Subject: [PATCH 2/7] :zap: add translation file in French

---
 app/js/lib/conf/i18n/fr.json | 258 +++++++++++++++++++++++++++++++++++
 app/js/lib/conf/translate.js |   1 +
 2 files changed, 259 insertions(+)
 create mode 100644 app/js/lib/conf/i18n/fr.json

diff --git a/app/js/lib/conf/i18n/fr.json b/app/js/lib/conf/i18n/fr.json
new file mode 100644
index 0000000..547270f
--- /dev/null
+++ b/app/js/lib/conf/i18n/fr.json
@@ -0,0 +1,258 @@
+{
+  "top.menu.overview": "Home",
+  "top.menu.data": "Explore",
+  "top.menu.settings": "Settings",
+  "top.menu.wallet": "Wallet",
+  "general.server.started": "Server started",
+  "general.server.stopped": "Server stopped",
+  "general.choose_option": "Choose your option",
+  "general.réseau.reconf_ok": "Reconfiguration success",
+  "global.button.validate": "Validate",
+  "global.button.start": "Start",
+  "err.unknown": "Unknown error",
+  "err.connection": "Could not connect to noeud",
+  "err.back_index": "Get back to previous screen",
+  "err.sync.interrupted": "Sync interrupted because the following error occured:",
+  "index.message.loading": "Loading...",
+  "crypto.secret_key": "Secret key",
+  "index.message.current_block": "Current block {{ number }}",
+  "configuration.init.choose.title": "Initialization",
+  "configuration.init.choose.message": "Your software has to be initialized. You may either connect to an existing one or restore a backup file.",
+  "configuration.init.choose.create": "Create a new currency",
+  "configuration.init.choose.connect": "Connect to an existing currency",
+  "configuration.init.choose.import": "Import from a backup file",
+  "configuration.create_currency.cancel": "Cancel & go home",
+  "configuration.create_uid.title": "Your identity",
+  "configuration.create_uid.message": "As a first step, you need to define your personal, unique identity.<br/>The following informations will be <strong>definitive</strong> for this currency: please choose them carefully.",
+  "configuration.create_uid.uid.tooltip": "The name you will be known as.",
+  "configuration.create_uid.entropy.tooltip": "An entropy source to make your key unique: an e-mail, a phone n°, ...",
+  "configuration.create_uid.password.tooltip": "A secret password to protect your key.",
+  "configuration.create_uid.create_button": "Continue",
+  "configuration.create_uid.preview_button": "Preview pubkey",
+  "configuration.create_uid.nrp_algo_choose": "NRP algorithm",
+  "configuration.create_uid.nrp_algo_choose_1": "N = 4096 ; r = 16 ; p = 1",
+  "configuration.create_uid.modal_title": "Identity confirmation",
+  "configuration.create_uid.modal_message": "This identity will be definitive for this currency: you will be known by your User ID and will be able to access your account using your Secret Key and Password values.",
+  "configuration.create_uid.modal_agree": "Agree",
+  "configuration.create_uid.modal_disagree": "Disagree",
+  "configuration.create_uid.modal_preview_title": "Preview of pubkey",
+  "configuration.create_uid.modal_preview_ok": "OK",
+  "configuration.ws2p.private": "Enable WS2P Private access",
+  "configuration.ws2p.private.desc": "<b>Strongly recommanded</b>. <i>No configuration required</i>. Private WS2P access allows your noeud to connect through P2P to other noeuds without exposing itself to the Internet. This is the most secure and efficient way to connect your noeud to the réseau.",
+  "configuration.ws2p.private.connections.title": "Connections",
+  "configuration.ws2p.private.connections.message": "You can limit the number of active private connections.",
+  "configuration.ws2p.private.max": "Maximum",
+  "configuration.proxiesConf.title": "Setting up a Proxy",
+  "configuration.proxiesConf.proxySocksAddress": "Classical socks proxy",
+  "configuration.proxiesConf.torMessage": "<b>Aversissement :</b> If you want to use a socks proxy to redirect all outgoing traffic via <b>Tor</b>, you must use the <i>\"Tor socks proxy\"</i> field :",
+  "configuration.proxiesConf.proxyTorAddress": "Tor socks proxy",
+  "configuration.proxiesConf.clear": "clear",
+  "configuration.proxiesConf.tor": "tor",
+  "configuration.proxiesConf.none": "none",
+  "configuration.proxiesConf.reachingClearEp": "How do you want to reach the classic noeuds ? (clear endpoints)",
+  "configuration.ws2p.public": "Enable WS2P Public access",
+  "configuration.ws2p.public.desc": "<b>Recommanded</b>. Public WS2P access allows your noeud to be publicly visible to communicate through WS2P. Considering that most of the noeuds will communicate through WS2P access, it is important to have a maximum number of noeuds with Public WS2P access enabled to have a decentralized réseau.",
+  "configuration.ws2p.public.upnp.title": "UPnP",
+  "configuration.ws2p.public.upnp.message": "Automated configuration. You need to have a box (router) for this to work. Typically true if you use Duniter at home.",
+  "configuration.ws2p.public.upnp.value": "Enable Public WS2P accss through UPnP",
+  "configuration.ws2p.public.manual.title": "Manual configuration",
+  "configuration.ws2p.public.manual.message": "You can also manually configure your noeud for Public WS2P access.",
+  "configuration.ws2p.public.manual.local_ipv4": "Private (computer)",
+  "configuration.ws2p.public.manual.host": "Public (remote host)",
+  "configuration.ws2p.public.manual.lport": "Private port",
+  "configuration.ws2p.public.manual.rport": "Public port",
+  "configuration.ws2p.public.manual.rpath": "WebSocket web path",
+  "configuration.ws2p.public.connections.title": "Connections",
+  "configuration.ws2p.public.connections.message": "You can limit the number of active public connections.",
+  "configuration.ws2p.public.max": "Maximum",
+  "configuration.create_réseau.desc": "<b>Deprecated</b>. BMA is the legacy communication interface for Duniter noeuds. It is being deprecated. Yet, you can activate it if you know what you are doing.",
+  "configuration.create_réseau.title": "Réseau",
+  "configuration.create_réseau.message": "Duniter is a P2P software and needs bidirectionnal access to the réseau. Please chose carefully the following parameters.",
+  "configuration.create_réseau.none": "None",
+  "configuration.create_réseau.ipv4.title": "IPv4",
+  "configuration.create_réseau.ipv6.title": "IPv6",
+  "configuration.create_réseau.ipv4.message": "For compatibilty reasons, you may prefer to use classic IPv4 interfaces. The configuration is more complicated.",
+  "configuration.create_réseau.local_ipv4": "Private (computer)",
+  "configuration.create_réseau.remote_ipv4": "Public (box/router)",
+  "configuration.create_réseau.local_ipv6": "IPv6",
+  "configuration.create_réseau.lport": "Local port",
+  "configuration.create_réseau.rport": "Remote port",
+  "configuration.create_réseau.port.title": "Ports",
+  "configuration.create_réseau.ipv6.message": "IPv6 gives your computer a unique, direct address to your noeud over the Internet. This is the <b>recommended way</b> to connect your noeud to the réseau.",
+  "configuration.create_réseau.port.message": "Wether you use IPv6 or IPv4, Duniter noeud will use this port number for connection to the réseau. If you use IPv6, local and remote port should equal each other.",
+  "configuration.create_réseau.dns.title": "Domain name",
+  "configuration.create_réseau.dns": "Domain name",
+  "configuration.create_réseau.dns.message": "IPv6 (AAAA) and IPv4 (A) DNS records will be used.",
+  "configuration.create_réseau.upnp": "Use UPnP",
+  "configuration.create_réseau.bma": "Enable BMA access",
+  "configuration.create_réseau.button.validate": "Continue",
+  "configuration.create_réseau.button.autoconf": "Automatic configuration",
+  "configuration.create_parameters.title": "Currency",
+  "configuration.create_parameters.message": "Initial parameters of the currency. It should be carefully chosen, as these parameters never change once the currency is started.",
+  "configuration.create_parameters.currency.title": "Money units",
+  "configuration.create_parameters.currency.message": "Give a name to your currency. The 3 following parameters configure the way new money units are created.",
+  "configuration.create_parameters.currency": "Currency name",
+  "configuration.create_parameters.c": "c",
+  "configuration.create_parameters.dt": "UD period",
+  "configuration.create_parameters.ud0": "UD(0)",
+  "configuration.create_parameters.button.validate": "Continue",
+  "configuration.create_parameters.wot.title": "Web of Trust",
+  "configuration.create_parameters.wot.message": "The following parameters deal with identities and their links gathered in the Web of Trust concept.",
+  "configuration.create_parameters.sigStock": "Max cert stock",
+  "configuration.create_parameters.sigPeriod": "Delay between 2 certs",
+  "configuration.create_parameters.sigValidity": "Cert expiry delay",
+  "configuration.create_parameters.msValidity": "Membership expiry delay",
+  "configuration.create_parameters.sigQty": "Min required certs",
+  "configuration.create_parameters.sigWindow": "Cert time window",
+  "configuration.create_parameters.stepMax": "Max distance",
+  "configuration.create_parameters.xpercent": "Percent of distance",
+  "configuration.create_parameters.blockchain.title": "Blockchain",
+  "configuration.create_parameters.blockchain.message": "The technical support of money and identities is the blockchain. It also has some parameters driving its behavior.",
+  "configuration.create_parameters.medianTimeBlocks": "Blocks count",
+  "configuration.create_parameters.avgGenTime": "Block gen. duration",
+  "configuration.create_parameters.dtDiffEval": "Blocks count for diff.",
+  "configuration.create_parameters.blocksRot": "Personal diff. blocks",
+  "configuration.create_parameters.percentRot": "Personal diff. rotation",
+  "configuration.create_root.title": "Root block creation",
+  "configuration.create_root.message": "This is the final step to create the new currency! The root block or <i>genesis</i> will include the first members and define the currency parameters. Once generated and submitted, the blockchain will be started.",
+  "configuration.create_root.button.start": "Start HTTP",
+  "configuration.create_root.button.stop": "Stop HTTP",
+  "configuration.create_root.button.generate": "Give a try",
+  "configuration.create_root.need_a_try": "You need to generate a first block with the « Give a try » button. Start HTTP server to do so.",
+  "configuration.create_root.button.accept_and_send": "Accept this block and start currency",
+  "configuration.create_root.button.cancel": "Cancel creation and go to home screen",
+  "configuration.create_root.host_listening": "Host listening at:",
+  "configuration.create_uid.pubkey_preview": "Public key preview",
+  "home.ws2p_pairs": "Connected pairs",
+  "home.current.number": "Current block #",
+  "home.current.membersCount": "Members count",
+  "home.current.medianTime": "Median time",
+  "home.current.powMin": "Common difficulty level",
+  "home.current.mmass": "Masse monétaire",
+  "home.pulling.réseau": "Réseau",
+  "home.pulling.peer": "Paris",
+  "home.pulling.state.unkown": "Prochaine syncro dans quelques minutes",
+  "home.pulling.state.synced": "Syncronisé",
+  "home.pulling.state.syncing": "En syncronisation...",
+  "home.pow.unit": "blocks made by this key (last 2 months)",
+  "home.pow.is_mirror": "Ce noeud est un miroir",
+  "home.pow.is_waiting": "Waiting for better proof conditions",
+  "home.tabs.overview": "Tableau de bord",
+  "home.tabs.overview.should_reconfigure": "Your configuration has changed and your noeud is no more reachable from the réseau. You should reconfigure it to have a functional noeud. If this message appears again, you should manually configure the réseau settings. Often, selecting only IPv6 interface (disabling IPv4) solves the problem.",
+  "home.tabs.réseau": "Pairs",
+  "home.tabs.réseau.button.update": "Check pairs again",
+  "home.tabs.connections": "Réseau",
+  "home.tabs.connections.title.connections": "WS2P Connections",
+  "home.tabs.connections.title.réseau": "Réseau view",
+  "home.tabs.connections.legend.title": "Legend",
+  "home.tabs.connections.legend.prefered": "Prefered: noeuds that you prefer for outcoming connections",
+  "home.tabs.connections.legend.privileged": "Privileged: noeuds that you privilege the incoming connections (= invitation)",
+  "home.tabs.logs": "Logs",
+  "home.tabs.logs.follow.logs": "Suivre les logs",
+  "home.tabs.logs.pause.logs": "Mettre en pause les logs",
+  "home.tabs.logs.level.error": "Error",
+  "home.tabs.logs.level.warn": "Aversissement",
+  "home.tabs.logs.level.info": "Info",
+  "home.tabs.logs.level.debug": "Debug",
+  "home.tabs.logs.level.trace": "Trace",
+  "sync.title": "Synchroniser",
+  "sync.message": "Votre noeud will be synchronized with an existing currency: just enter technical details about a noeud to sync with it.",
+  "sync.host": "Hôte",
+  "sync.port": "Port",
+  "sync.check": "Vérifier le noeud",
+  "sync.start": "Synchroniser avec ce noeud",
+  "sync.failed": "Synchronisation échouée.",
+  "sync.mode.simplified": "Mode simplifié",
+  "sync.mode.manual": "Model manuel",
+  "sync.simplified.choose": "Noeud auquel se connecter",
+  "sync.simplified.default_option": "Sélectionnez un noeud pour continuer",
+  "sync.simplified.currency": "Currency",
+  "sync.simplified.main_mirror": "(main mirror)",
+  "sync.simplified.other mirror": "(other mirror)",
+  "sync.ready.node.part1": "This noeud is available!",
+  "sync.ready.node.part2": "Click on the green button to proceed.",
+  "sync.started.node": "Synchronization started on noeud:",
+  "sync.error.unreachable.try.another.node": "This noeud is not available. Please select another one.",
+  "home.menu.server.stop": "Stop server",
+  "home.menu.server.start": "Start server",
+  "home.menu.server.restart": "Restart server",
+  "home.state": "Server:",
+  "home.state.started": "STARTED",
+  "home.state.stopped": "STOPPED",
+  "settings.tabs.logs": "Logs",
+  "settings.tabs.data": "Data",
+  "settings.tabs.backup": "Backup",
+  "settings.tabs.identity": "Crypto",
+  "settings.tabs.réseau": "Réseau",
+  "settings.tabs.currency": "Currency",
+  "settings.tabs.cpu": "CPU",
+  "settings.tabs.modules": "Modules",
+  "settings.data.reset.title": "Reset this noeud",
+  "settings.data.reset.message": "If you desire to reset this noeud's data and sync it again with the réseau, please select a noeud to sync against and validate.",
+  "settings.data.reset.aversissement": "This process <strong>will not</strong> reset the noeud identity and réseau settings, which will be reused.",
+  "settings.data.reset.peer.none_option": "Select a noeud",
+  "settings.data.reset.peer.label": "Synchronization peer",
+  "settings.data.reset.button": "Full reset of the noeud",
+  "settings.data.reset_sync.button": "Reset data and start sync",
+  "settings.logs.title": "Logs",
+  "settings.logs.consult.message": "Your noeud continually generates information messages in a log file. This information may help you understand what your noeud <i>is doing</i> or what it <i>has done</i> few times ago.",
+  "settings.logs.consult.button": "View real-time logs",
+  "settings.logs.share.message": "You may want to <b>share your logs</b> with other people, sometimes to get help or to add informations in a bug tracker. Clicking on below button will extract the last 2000 lines of your logs and push it on the web, returning you a link to be shared with whoever you want.",
+  "settings.logs.share.button": "Create a web link to your logs",
+  "settings.logs.share.generating": "Generating your link...",
+  "settings.logs.share.error": "An error occurred during the generation of your link:",
+  "settings.data.backup.title": "Backup",
+  "settings.data.backup.message": "You can create backups of your noeud's data and restore them using the buttons below.",
+  "settings.data.backup.aversissement": "<b>Export</b> will only backup your noeud's data, which <i>excludes your secret key and configuration details</i>.<br><b>Import</b> will reset your noeud's data by applying the backup. Your secret keys and configuration remains untouched.",
+  "settings.data.backup.button.export": "Create a data backup",
+  "settings.data.backup.button.import": "Import a data backup",
+  "settings.data.backup.importing": "Importing data...",
+  "settings.data.backup.imported": "Import successfull!",
+  "settings.réseau.button.validate": "Save and apply réseau settings",
+  "settings.réseau.saved": "Configuration saved and applied successfully",
+  "settings.key.title": "Public key of this noeud:",
+  "settings.key.button.validate": "Save and use this key",
+  "settings.key.button.change": "Change keyring",
+  "settings.key.pubkey.description": "This public key is the public part of your keyring, which is composed of a public key and a private key. This public key is shared with all the pairs of the réseau and users of the currency, while your private key is secretely kept and used by this noeud to process operations on the réseau.",
+  "settings.data.modal_title": "Confirm full reset",
+  "settings.data.modal_message": "This action will completely reset the data of your noeud and redirect you to initial configuration screen. Do you confirm?",
+  "settings.data.modal_disagree": "No, cancel this",
+  "settings.data.modal_agree": "Yes, process the reset",
+  "settings.data.reset.experimental": "This functionality is still considered experimental. If you encounters strange behaviors, please stop the software and reset manually your noeud by removing all the files BUT conf.json under ~/.config/duniter/duniter_default, and restart the software.",
+  "settings.cpu.title": "CPU settings",
+  "settings.cpu.message": "You can adjust the CPU power dedicated to proof-of-work computation. The higher the value, the faster is your noeud, the higher the chances you have to compute a block early.",
+  "settings.cpu.aversissement": "<b>Up to 8 cores</b> of your machine are dedicated to proof-of-work computation currently. Also, setting CPU to 100% does not mean Duniter will use 100% of each core, but will use as much as possible each of them, as a core is also shared with other programs.",
+  "settings.cpu.range": "% of CPU power core dedicated to proof-of-work :",
+  "settings.cpu.power": "Core power:",
+  "settings.cpu.saved": "CPU settings saved.",
+  "settings.modules.title": "Modules",
+  "settings.modules.message": "You can install extensions to your Duniter noeud to provide new features. These extensions are called <b>Duniter modules</b>.",
+  "settings.modules.no_access": "This instance does not have enough system rights to install new modules on disk.",
+  "settings.modules.install": "Install this module",
+  "settings.modules.already_install": "Module already installed",
+  "settings.modules.path_does_not_exist": "Path does not lead to a module",
+  "settings.modules.wrong_package_source": "Package URL has wrong format",
+  "settings.modules.aversissement": "Please be <b>VERY CAREFUL</b> when installing a module: you should <b>check that it is not a virus</b>, nor wants to steal your informations. <b>A MODULE HAS A LOT OF POWER</b> and can access/modify any part of your system (including your private key), in the limit of the user's access rights.",
+  "settings.modules.aversissement_light": "AVERSISSEMENT! (click to see more)",
+  "settings.modules.aversissement_close": "Close this message",
+  "settings.modules.on": "On",
+  "settings.modules.off": "Off",
+  "settings.modules.installing": "Installation...",
+  "settings.modules.installing_warn": "Please <b>do not close Duniter</b> during this process!",
+  "settings.modules.uninstalling": "Removal...",
+  "graphs.tabs.blockchain": "Blockchain",
+  "graphs.tabs.currency": "Currency",
+  "graphs.blockchain.range": "Graphs for the last X blocks: (please choose X value)",
+  "graphs.blockchain.with.time": "Time variations graph",
+  "graphs.blockchain.with.speed": "Writing speed graph",
+  "graphs.blockchain.with.difficulty": "Difficulty graph",
+  "help.about_duniter": "A propos de Duniter",
+  "help.about_duniter.title": "About",
+  "help.about_duniter.subtitle": "Duniter Desktop",
+  "help.about_duniter.version": "Version: ",
+  "help.about_duniter.forum": "Forum",
+  "help.about_duniter.chat": "Chat",
+  "help.new_version_available": "Nouvelle version disponible",
+  "help.restart_required": "Restart to apply changes",
+  "help.restart_required.message": "Please close Duniter and restart it."
+}
diff --git a/app/js/lib/conf/translate.js b/app/js/lib/conf/translate.js
index f0c8df5..611499d 100644
--- a/app/js/lib/conf/translate.js
+++ b/app/js/lib/conf/translate.js
@@ -3,6 +3,7 @@ module.exports = (app) => {
   app.config(['$translateProvider', ($translateProvider) => {
 
     $translateProvider.translations('en', require('./i18n/en'));
+    $translateProvider.translations('fr', require('./i18n/fr'));
 
     // Default language
     $translateProvider.preferredLanguage('en');
-- 
GitLab


From 549f89a5ceab36d350cd1610d8f26eacffb949e1 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 16:37:42 +0200
Subject: [PATCH 3/7] :alien: more strings translated

---
 app/js/lib/conf/i18n/fr.json | 64 ++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/app/js/lib/conf/i18n/fr.json b/app/js/lib/conf/i18n/fr.json
index 547270f..5d49fd8 100644
--- a/app/js/lib/conf/i18n/fr.json
+++ b/app/js/lib/conf/i18n/fr.json
@@ -1,28 +1,28 @@
 {
-  "top.menu.overview": "Home",
-  "top.menu.data": "Explore",
-  "top.menu.settings": "Settings",
-  "top.menu.wallet": "Wallet",
-  "general.server.started": "Server started",
-  "general.server.stopped": "Server stopped",
-  "general.choose_option": "Choose your option",
-  "general.réseau.reconf_ok": "Reconfiguration success",
-  "global.button.validate": "Validate",
-  "global.button.start": "Start",
-  "err.unknown": "Unknown error",
-  "err.connection": "Could not connect to noeud",
-  "err.back_index": "Get back to previous screen",
-  "err.sync.interrupted": "Sync interrupted because the following error occured:",
-  "index.message.loading": "Loading...",
-  "crypto.secret_key": "Secret key",
-  "index.message.current_block": "Current block {{ number }}",
-  "configuration.init.choose.title": "Initialization",
+  "top.menu.overview": "Accueil",
+  "top.menu.data": "Explorer",
+  "top.menu.settings": "Paramètres",
+  "top.menu.wallet": "Porte-feuilles",
+  "general.server.started": "Server démarré",
+  "general.server.stopped": "Server arrêté",
+  "general.choose_option": "Choisissez votre option",
+  "general.network.reconf_ok": "Reconfiguration réussie",
+  "global.button.validate": "Valider",
+  "global.button.start": "Démarrer",
+  "err.unknown": "Erreur inconnue",
+  "err.connection": "Impossible de se connecter au noeud",
+  "err.back_index": "Retourner à l'écran précédent",
+  "err.sync.interrupted": "La syncronisation a échoué en raison de l'erreur suivante:",
+  "index.message.loading": "Chargement...",
+  "crypto.secret_key": "Clé secrète",
+  "index.message.current_block": "Block courant {{ number }}",
+  "configuration.init.choose.title": "Initialisation",
   "configuration.init.choose.message": "Your software has to be initialized. You may either connect to an existing one or restore a backup file.",
-  "configuration.init.choose.create": "Create a new currency",
-  "configuration.init.choose.connect": "Connect to an existing currency",
-  "configuration.init.choose.import": "Import from a backup file",
-  "configuration.create_currency.cancel": "Cancel & go home",
-  "configuration.create_uid.title": "Your identity",
+  "configuration.init.choose.create": "Créer une nouvelle monnaie",
+  "configuration.init.choose.connect": "Se connecter à une monnaie existante",
+  "configuration.init.choose.import": "Importer depuis un fichier de sauvegarde",
+  "configuration.create_currency.cancel": "Annuler et retourner à l'accueil",
+  "configuration.create_uid.title": "Votre identité",
   "configuration.create_uid.message": "As a first step, you need to define your personal, unique identity.<br/>The following informations will be <strong>definitive</strong> for this currency: please choose them carefully.",
   "configuration.create_uid.uid.tooltip": "The name you will be known as.",
   "configuration.create_uid.entropy.tooltip": "An entropy source to make your key unique: an e-mail, a phone n°, ...",
@@ -129,7 +129,7 @@
   "home.current.medianTime": "Median time",
   "home.current.powMin": "Common difficulty level",
   "home.current.mmass": "Masse monétaire",
-  "home.pulling.réseau": "Réseau",
+  "home.pulling.network": "Réseau",
   "home.pulling.peer": "Paris",
   "home.pulling.state.unkown": "Prochaine syncro dans quelques minutes",
   "home.pulling.state.synced": "Syncronisé",
@@ -139,11 +139,11 @@
   "home.pow.is_waiting": "Waiting for better proof conditions",
   "home.tabs.overview": "Tableau de bord",
   "home.tabs.overview.should_reconfigure": "Your configuration has changed and your noeud is no more reachable from the réseau. You should reconfigure it to have a functional noeud. If this message appears again, you should manually configure the réseau settings. Often, selecting only IPv6 interface (disabling IPv4) solves the problem.",
-  "home.tabs.réseau": "Pairs",
-  "home.tabs.réseau.button.update": "Check pairs again",
+  "home.tabs.network": "Pairs",
+  "home.tabs.network.button.update": "Check pairs again",
   "home.tabs.connections": "Réseau",
   "home.tabs.connections.title.connections": "WS2P Connections",
-  "home.tabs.connections.title.réseau": "Réseau view",
+  "home.tabs.connections.title.network": "Réseau view",
   "home.tabs.connections.legend.title": "Legend",
   "home.tabs.connections.legend.prefered": "Prefered: noeuds that you prefer for outcoming connections",
   "home.tabs.connections.legend.privileged": "Privileged: noeuds that you privilege the incoming connections (= invitation)",
@@ -183,7 +183,7 @@
   "settings.tabs.data": "Data",
   "settings.tabs.backup": "Backup",
   "settings.tabs.identity": "Crypto",
-  "settings.tabs.réseau": "Réseau",
+  "settings.tabs.network": "Réseau",
   "settings.tabs.currency": "Currency",
   "settings.tabs.cpu": "CPU",
   "settings.tabs.modules": "Modules",
@@ -208,8 +208,8 @@
   "settings.data.backup.button.import": "Import a data backup",
   "settings.data.backup.importing": "Importing data...",
   "settings.data.backup.imported": "Import successfull!",
-  "settings.réseau.button.validate": "Save and apply réseau settings",
-  "settings.réseau.saved": "Configuration saved and applied successfully",
+  "settings.network.button.validate": "Save and apply réseau settings",
+  "settings.network.saved": "Configuration saved and applied successfully",
   "settings.key.title": "Public key of this noeud:",
   "settings.key.button.validate": "Save and use this key",
   "settings.key.button.change": "Change keyring",
@@ -253,6 +253,6 @@
   "help.about_duniter.forum": "Forum",
   "help.about_duniter.chat": "Chat",
   "help.new_version_available": "Nouvelle version disponible",
-  "help.restart_required": "Restart to apply changes",
-  "help.restart_required.message": "Please close Duniter and restart it."
+  "help.restart_required": "Redémarrer to apply changes",
+  "help.restart_required.message": "Please close Duniter and redémarrer it."
 }
-- 
GitLab


From 1e4012401c071b7450eca24b33b713381837b2c1 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 17:04:40 +0200
Subject: [PATCH 4/7] :alien: home screen translated

---
 app/js/lib/conf/i18n/en.json |  1 +
 app/js/lib/conf/i18n/fr.json | 39 ++++++++++++++++++------------------
 app/js/lib/conf/translate.js |  2 +-
 3 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/app/js/lib/conf/i18n/en.json b/app/js/lib/conf/i18n/en.json
index 92a6fe2..eb5e6e6 100644
--- a/app/js/lib/conf/i18n/en.json
+++ b/app/js/lib/conf/i18n/en.json
@@ -129,6 +129,7 @@
   "home.current.medianTime": "Median time",
   "home.current.powMin": "Common difficulty level",
   "home.current.mmass": "Monetary mass",
+  "home.current.mmass_kud": "Monetary mass in thousands universal dividend (kilo UD)",
   "home.pulling.network": "Network",
   "home.pulling.peer": "Peer",
   "home.pulling.state.unkown": "Next sync in few minutes",
diff --git a/app/js/lib/conf/i18n/fr.json b/app/js/lib/conf/i18n/fr.json
index 5d49fd8..a31c25c 100644
--- a/app/js/lib/conf/i18n/fr.json
+++ b/app/js/lib/conf/i18n/fr.json
@@ -25,14 +25,14 @@
   "configuration.create_uid.title": "Votre identité",
   "configuration.create_uid.message": "As a first step, you need to define your personal, unique identity.<br/>The following informations will be <strong>definitive</strong> for this currency: please choose them carefully.",
   "configuration.create_uid.uid.tooltip": "The name you will be known as.",
-  "configuration.create_uid.entropy.tooltip": "An entropy source to make your key unique: an e-mail, a phone n°, ...",
-  "configuration.create_uid.password.tooltip": "A secret password to protect your key.",
+  "configuration.create_uid.entropy.tooltip": "An entropy source to make your clé unique: an e-mail, a phone n°, ...",
+  "configuration.create_uid.password.tooltip": "A secret password to protect your clé.",
   "configuration.create_uid.create_button": "Continue",
   "configuration.create_uid.preview_button": "Preview pubkey",
   "configuration.create_uid.nrp_algo_choose": "NRP algorithm",
   "configuration.create_uid.nrp_algo_choose_1": "N = 4096 ; r = 16 ; p = 1",
   "configuration.create_uid.modal_title": "Identity confirmation",
-  "configuration.create_uid.modal_message": "This identity will be definitive for this currency: you will be known by your User ID and will be able to access your account using your Secret Key and Password values.",
+  "configuration.create_uid.modal_message": "This identity will be definitive for this currency: you will be known by your User ID and will be able to access your account using your Secret Clé and Password values.",
   "configuration.create_uid.modal_agree": "Agree",
   "configuration.create_uid.modal_disagree": "Disagree",
   "configuration.create_uid.modal_preview_title": "Preview of pubkey",
@@ -122,19 +122,20 @@
   "configuration.create_root.button.accept_and_send": "Accept this block and start currency",
   "configuration.create_root.button.cancel": "Cancel creation and go to home screen",
   "configuration.create_root.host_listening": "Host listening at:",
-  "configuration.create_uid.pubkey_preview": "Public key preview",
-  "home.ws2p_pairs": "Connected pairs",
-  "home.current.number": "Current block #",
-  "home.current.membersCount": "Members count",
-  "home.current.medianTime": "Median time",
-  "home.current.powMin": "Common difficulty level",
+  "configuration.create_uid.pubkey_preview": "Public clé preview",
+  "home.ws2p_peers": "Pairs connectés",
+  "home.current.number": "Block courant #",
+  "home.current.membersCount": "Nombre de membres",
+  "home.current.medianTime": "Temps médian",
+  "home.current.powMin": "Niveau de difficulté commune",
   "home.current.mmass": "Masse monétaire",
+  "home.current.mmass_kud": "Masse monétaire en milliers de dividendes universel (kilo DU)",
   "home.pulling.network": "Réseau",
-  "home.pulling.peer": "Paris",
+  "home.pulling.peer": "Pairs",
   "home.pulling.state.unkown": "Prochaine syncro dans quelques minutes",
   "home.pulling.state.synced": "Syncronisé",
   "home.pulling.state.syncing": "En syncronisation...",
-  "home.pow.unit": "blocks made by this key (last 2 months)",
+  "home.pow.unit": "blocks made by this clé (last 2 months)",
   "home.pow.is_mirror": "Ce noeud est un miroir",
   "home.pow.is_waiting": "Waiting for better proof conditions",
   "home.tabs.overview": "Tableau de bord",
@@ -173,9 +174,9 @@
   "sync.ready.node.part2": "Click on the green button to proceed.",
   "sync.started.node": "Synchronization started on noeud:",
   "sync.error.unreachable.try.another.node": "This noeud is not available. Please select another one.",
-  "home.menu.server.stop": "Stop server",
-  "home.menu.server.start": "Start server",
-  "home.menu.server.restart": "Restart server",
+  "home.menu.server.stop": "Arrêter le serveur",
+  "home.menu.server.start": "Démarrer le serveur",
+  "home.menu.server.restart": "Redémarrer le serveur",
   "home.state": "Server:",
   "home.state.started": "STARTED",
   "home.state.stopped": "STOPPED",
@@ -203,17 +204,17 @@
   "settings.logs.share.error": "An error occurred during the generation of your link:",
   "settings.data.backup.title": "Backup",
   "settings.data.backup.message": "You can create backups of your noeud's data and restore them using the buttons below.",
-  "settings.data.backup.aversissement": "<b>Export</b> will only backup your noeud's data, which <i>excludes your secret key and configuration details</i>.<br><b>Import</b> will reset your noeud's data by applying the backup. Your secret keys and configuration remains untouched.",
+  "settings.data.backup.aversissement": "<b>Export</b> will only backup your noeud's data, which <i>excludes your secret clé and configuration details</i>.<br><b>Import</b> will reset your noeud's data by applying the backup. Your secret keys and configuration remains untouched.",
   "settings.data.backup.button.export": "Create a data backup",
   "settings.data.backup.button.import": "Import a data backup",
   "settings.data.backup.importing": "Importing data...",
   "settings.data.backup.imported": "Import successfull!",
   "settings.network.button.validate": "Save and apply réseau settings",
   "settings.network.saved": "Configuration saved and applied successfully",
-  "settings.key.title": "Public key of this noeud:",
-  "settings.key.button.validate": "Save and use this key",
+  "settings.key.title": "Public clé of this noeud:",
+  "settings.key.button.validate": "Save and use this clé",
   "settings.key.button.change": "Change keyring",
-  "settings.key.pubkey.description": "This public key is the public part of your keyring, which is composed of a public key and a private key. This public key is shared with all the pairs of the réseau and users of the currency, while your private key is secretely kept and used by this noeud to process operations on the réseau.",
+  "settings.key.pubkey.description": "This public clé is the public part of your keyring, which is composed of a public clé and a private clé. This public clé is shared with all the pairs of the réseau and users of the currency, while your private clé is secretely kept and used by this noeud to process operations on the réseau.",
   "settings.data.modal_title": "Confirm full reset",
   "settings.data.modal_message": "This action will completely reset the data of your noeud and redirect you to initial configuration screen. Do you confirm?",
   "settings.data.modal_disagree": "No, cancel this",
@@ -232,7 +233,7 @@
   "settings.modules.already_install": "Module already installed",
   "settings.modules.path_does_not_exist": "Path does not lead to a module",
   "settings.modules.wrong_package_source": "Package URL has wrong format",
-  "settings.modules.aversissement": "Please be <b>VERY CAREFUL</b> when installing a module: you should <b>check that it is not a virus</b>, nor wants to steal your informations. <b>A MODULE HAS A LOT OF POWER</b> and can access/modify any part of your system (including your private key), in the limit of the user's access rights.",
+  "settings.modules.aversissement": "Please be <b>VERY CAREFUL</b> when installing a module: you should <b>check that it is not a virus</b>, nor wants to steal your informations. <b>A MODULE HAS A LOT OF POWER</b> and can access/modify any part of your system (including your private clé), in the limit of the user's access rights.",
   "settings.modules.aversissement_light": "AVERSISSEMENT! (click to see more)",
   "settings.modules.aversissement_close": "Close this message",
   "settings.modules.on": "On",
diff --git a/app/js/lib/conf/translate.js b/app/js/lib/conf/translate.js
index 611499d..4b805e2 100644
--- a/app/js/lib/conf/translate.js
+++ b/app/js/lib/conf/translate.js
@@ -6,7 +6,7 @@ module.exports = (app) => {
     $translateProvider.translations('fr', require('./i18n/fr'));
 
     // Default language
-    $translateProvider.preferredLanguage('en');
+    $translateProvider.preferredLanguage('fr');
 
     // Other parameters
     $translateProvider.useSanitizeValueStrategy('');
-- 
GitLab


From 662f7e5b355162097ad333fdd2604aaefa51e995 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 17:05:35 +0200
Subject: [PATCH 5/7] :bug: fix appearance of monetary mass on overview, add
 number separators

---
 app/views/main/home/tabs/overview.jade | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/app/views/main/home/tabs/overview.jade b/app/views/main/home/tabs/overview.jade
index db0c008..f0eeabe 100644
--- a/app/views/main/home/tabs/overview.jade
+++ b/app/views/main/home/tabs/overview.jade
@@ -68,7 +68,7 @@
         .card-content
           i.fa.fa-chain.fa-5x
           .card-title
-            span {{ current_number }}
+            span {{ current_number | number:0}}
         .card-action
           p {{ 'home.current.number' | translate }}
 
@@ -77,7 +77,7 @@
         .card-content
           i.fa.fa-users.fa-5x
           .card-title
-            span {{ current_membersCount }}
+            span {{ current_membersCount | number:0 }}
         .card-action
           p {{ 'home.current.membersCount' | translate }}
 
@@ -96,7 +96,7 @@
         .card-content
           i.fa.fa-graduation-cap.fa-5x
           .card-title
-            span {{ current_powMin }}
+            span {{ current_powMin | number:0}}
         .card-action
           p {{ 'home.current.powMin' | translate }}
 
@@ -105,7 +105,8 @@
         .card-content
           i.fa.fa-money.fa-5x
           .card-title
-            span {{ monetaryMass }}&nbsp;UD
+            span {{ (monetaryMass / 1000 ) | number:0:'fr-FR' }}
+            span(title="'home.current.mmass_kud' | translate ")&nbsp;kUD
         .card-action
           p {{ 'home.current.mmass' | translate }}
 
-- 
GitLab


From 2273b9b475b712f5dacf8c3530a3b0c94eddfe94 Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 17:12:01 +0200
Subject: [PATCH 6/7] :book: add Readme infos about development hot reload

---
 README.md    | 12 ++++++++----
 package.json |  5 +++--
 yarn.lock    |  3 ++-
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index d247a6b..44dcce4 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@ Duniter graphical interface. This is a developement package which is embedded in
 > Requires [Yarn](https://classic.yarnpkg.com/en/docs/install/)
 
 ```bash
-npm i -g nvm yarn
+npm i -g nvm yarn bower
+bower install
 nvm install 9
 nvm use 9
 yarn --pure-lockfile
@@ -17,9 +18,12 @@ yarn --pure-lockfile
 ## Run
 
 ```bash
-node run.js direct_webstart
+firefox http://localhost:9220 &
+yarn run webstart
 ```
-or
+
+## Dev server with hot reload on file save
 ```bash
-node_modules/brunch/bin/brunch watch --server
+firefox http://localhost:9220 &
+yarn run start
 ```
diff --git a/package.json b/package.json
index bec9b2f..1a6e6cd 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
     "build": "bower install && brunch build",
     "b": "brunch build",
     "watch": "brunch watch",
-    "start": "cd cesium && npm start",
+    "start": "brunch watch --server",
+    "cesium": "cd cesium && npm start",
     "webstart": "node run.js direct_webstart",
     "test": "mocha --growl tests/"
   },
@@ -55,7 +56,7 @@
     "brunch": "2.10.9",
     "core-util-is": "1.0.2",
     "css-brunch": "2.0.0",
-    "duniter": "1.7.x",
+    "duniter": "^1.7.21",
     "fb-flo-brunch": "1.7.22",
     "jade-brunch": "2.0.0",
     "javascript-brunch": "2.0.0",
diff --git a/yarn.lock b/yarn.lock
index 8aff82f..9959267 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2115,9 +2115,10 @@ domain-browser@~1.1.7:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
 
-duniter@1.7.x:
+duniter@^1.7.21:
   version "1.7.21"
   resolved "https://registry.yarnpkg.com/duniter/-/duniter-1.7.21.tgz#492f1a9ab6dccf22632d5cd7cd11af9a30cccfe4"
+  integrity sha512-4NyVGbb/ScHU96YN1f35IfT0rI0c7Ixr+fqXdAzEhr1AUJ3At5ZGroGhg5ZAGWYDXc/GRDk8nQtMbibcv8T10g==
   dependencies:
     "@types/leveldown" "^4.0.0"
     "@types/levelup" "^3.1.0"
-- 
GitLab


From 051cfdf16c2720e92f3e741fe18e47e4ea8d297f Mon Sep 17 00:00:00 2001
From: Baptiste Lemoine <contact@cipherbliss.com>
Date: Sun, 5 Apr 2020 17:14:49 +0200
Subject: [PATCH 7/7] :book: add attribute on kDU to show kilo UD

---
 app/js/lib/conf/i18n/fr.json           | 2 +-
 app/views/main/home/tabs/overview.jade | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/js/lib/conf/i18n/fr.json b/app/js/lib/conf/i18n/fr.json
index a31c25c..f91c5dd 100644
--- a/app/js/lib/conf/i18n/fr.json
+++ b/app/js/lib/conf/i18n/fr.json
@@ -135,7 +135,7 @@
   "home.pulling.state.unkown": "Prochaine syncro dans quelques minutes",
   "home.pulling.state.synced": "Syncronisé",
   "home.pulling.state.syncing": "En syncronisation...",
-  "home.pow.unit": "blocks made by this clé (last 2 months)",
+  "home.pow.unit": "blocks produits par cette clé (2 derniers mois)",
   "home.pow.is_mirror": "Ce noeud est un miroir",
   "home.pow.is_waiting": "Waiting for better proof conditions",
   "home.tabs.overview": "Tableau de bord",
diff --git a/app/views/main/home/tabs/overview.jade b/app/views/main/home/tabs/overview.jade
index f0eeabe..1511eb9 100644
--- a/app/views/main/home/tabs/overview.jade
+++ b/app/views/main/home/tabs/overview.jade
@@ -106,7 +106,7 @@
           i.fa.fa-money.fa-5x
           .card-title
             span {{ (monetaryMass / 1000 ) | number:0:'fr-FR' }}
-            span(title="'home.current.mmass_kud' | translate ")&nbsp;kUD
+            span(title="{{'home.current.mmass_kud' | translate }}") &nbsp;kUD
         .card-action
           p {{ 'home.current.mmass' | translate }}
 
-- 
GitLab