diff --git a/Gruntfile.js b/Gruntfile.js
index 161b0464b596cb7c7af490a7a28b25344efeda39..1a48eda53a5e4374681807c8706c53ad4bc6633e 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -3,7 +3,7 @@ module.exports = function exports(grunt) {
 
   grunt.loadTasks('tasks');
 
-  grunt.registerTask('default', ['lint', 'copy', 'sass:dist', 'postcss', 'requirejs:default', 'inlinedata', 'cachebreaker', 'inline', 'htmlmin', 'clean:release']);
+  grunt.registerTask('default', ['lint', 'copy', 'sass:dist', 'postcss', 'requirejs:default', 'inlinedata', 'cachebreaker', 'inline', 'htmlmin', 'json-minify', 'clean:release']);
   grunt.registerTask('lint', ['sasslint', 'eslint']);
-  grunt.registerTask('serve', ['lint', 'copy', 'sass:dev', 'postcss', 'requirejs:dev', 'inlinedata', 'htmlmin', 'browserSync', 'watch']);
+  grunt.registerTask('serve', ['lint', 'copy', 'sass:dev', 'postcss', 'requirejs:dev', 'inlinedata', 'htmlmin', 'json-minify', 'browserSync', 'watch']);
 };
diff --git a/README.md b/README.md
index 362722f174f3e00c0cd260d0f07eca1b6810a300..fd5e755ff7054d9b0775d236c5d340683bed1d5c 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
 - Updates selected node or list (incl. image stats cache-breaker) - not only overview tables
 - Zoom level if you click a node (`nodeZoom`) - Zoom level 22 available, but it is to close for a click
 - Formatted Code
+- Translation support - https://crowdin.com/project/meshviewer
 - Grunt inline for some css and js - less requests
 - Icon font with only needed icons
 - Upgrade to grunt v1.x (Tested with Node.js 4 LTS,6 LTS,7 Linux,OSX,W**)
@@ -154,7 +155,7 @@ This option allows to show node statistics depending on following case-sensitive
 - `caption` is shown, if `thumbnail` is not present (no thumbnail in infobox)
 
 To insert current node-id in either `href`, `thumbnail` or `caption`
-you can use the case-sensitive template string `{NODE_ID}`, `{NODE_NAME}` and `{TIME}` as cache-breaker.
+you can use the case-sensitive template string `{NODE_ID}`, `{NODE_NAME}`, `{LOCALE}` and `{TIME}` as cache-breaker.
 
 Examples for `nodeInfos`:
 
@@ -207,7 +208,7 @@ This option allows to show link statistics depending on the following case-sensi
 - `caption` is shown, if `thumbnail` is not present (no thumbnail in infobox)
 
 To insert the source or target node-id in either `href`, `thumbnail` or `caption`
-you can use the case-sensitive template strings `{SOURCE}`, `{TARGET}` and `{TIME}` as cache-breaker.
+you can use the case-sensitive template strings `{SOURCE}`, `{LOCALE}`, `{TARGET}` and `{TIME}` as cache-breaker.
 
     "linkInfos": [
       { "href": "stats/dashboard/db/links?var-source={SOURCE}&var-target={TARGET}",
@@ -232,6 +233,18 @@ Example for `siteNames`:
       { "site": "ffgt", "name": "Gothamcity" },
       { "site": "ffal", "name": "Atlantis" }
     ]
+    
+    
+## supportedLocale (array)
+
+Add supported locale (with matching language file in locales/*.json) and it will be matched against the browser language setting. Fallback is the first language in the array.
+
+Example for `supportedLocale`:
+
+    "supportedLocale": [
+      "en",
+      "de"
+    ]
 
 ## Sponsoring / Supporting
 - [BrowserStack](https://www.browserstack.com/) for providing a awesome testing service for hundreds of browsers
diff --git a/app.js b/app.js
index 2d9b1595183c2d1d8f5bcb146dac042772d0ba5c..6e8875f37e06d1660c70bcb7894e1d65454e37a7 100644
--- a/app.js
+++ b/app.js
@@ -3,10 +3,11 @@
 require.config({
   baseUrl: 'lib',
   paths: {
+    'polyglot': '../node_modules/node-polyglot/build/polyglot',
     'leaflet': '../node_modules/leaflet/dist/leaflet',
     'leaflet.label': '../node_modules/leaflet-label/dist/leaflet.label',
     'chroma-js': '../node_modules/chroma-js/chroma.min',
-    'moment': '../node_modules/moment',
+    'moment': '../node_modules/moment/moment',
     'tablesort': '../node_modules/tablesort/src/tablesort',
     'd3': '../node_modules/d3/d3.min',
     'virtual-dom': '../node_modules/virtual-dom/dist/virtual-dom',
diff --git a/config.json b/config.json
index 4d2665cf74e59e7b40e849b40a950e1cc276dc50..a26bd21506d11b4ac3c25331facb261b4b8a7218 100644
--- a/config.json
+++ b/config.json
@@ -110,5 +110,9 @@
       "site": "ffrgb",
       "name": "Regensburg"
     }
+  ],
+  "supportedLocale": [
+    "en",
+    "de"
   ]
 }
diff --git a/crowdin.yml b/crowdin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d043b234951ff24bd532276c16b5dc9f9fc4cddb
--- /dev/null
+++ b/crowdin.yml
@@ -0,0 +1,3 @@
+files:
+  - source: /locale/en.json
+    translation: /locale/%two_letters_code%.json
diff --git a/lib/about.js b/lib/about.js
index 7e2f078e618bbd1257681c1ae9fdc4096b494629..ecad90487c74d947133961ff3ad8092dfea009f5 100644
--- a/lib/about.js
+++ b/lib/about.js
@@ -3,10 +3,7 @@ define(function () {
 
   return function () {
     this.render = function render(d) {
-      d.innerHTML = '<h2>Über Meshviewer</h2>' +
-
-        '<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte ' +
-        'auch zoomen.</p>' +
+      d.innerHTML = _.t('sidebar.aboutInfo') +
 
         '<h3>AGPL 3</h3>' +
 
diff --git a/lib/gui.js b/lib/gui.js
index f38c4226ba7631d9d49e2af9d335e8be63e497ee..815a839a8db449185c0a661ca43a3eccb4efd36e 100644
--- a/lib/gui.js
+++ b/lib/gui.js
@@ -81,8 +81,8 @@ define(['chroma-js', 'map', 'sidebar', 'tabs', 'container', 'legend',
       var tabs = new Tabs();
       var overview = new Container();
       var legend = new Legend(config);
-      var newnodeslist = new SimpleNodelist('new', 'firstseen', router, 'Neue Knoten');
-      var lostnodeslist = new SimpleNodelist('lost', 'lastseen', router, 'Verschwundene Knoten');
+      var newnodeslist = new SimpleNodelist('new', 'firstseen', router, _.t('node.new'));
+      var lostnodeslist = new SimpleNodelist('lost', 'lastseen', router, _.t('node.missing'));
       var nodelist = new Nodelist(router);
       var linklist = new Linklist(linkScale, router);
       var statistics = new Proportions(config, fanout);
@@ -106,11 +106,11 @@ define(['chroma-js', 'map', 'sidebar', 'tabs', 'container', 'legend',
       header.add(filterGUI);
 
       sidebar.add(tabs);
-      tabs.add('Aktuelles', overview);
-      tabs.add('Knoten', nodelist);
-      tabs.add('Verbindungen', linklist);
-      tabs.add('Statistiken', statistics);
-      tabs.add('Über', about);
+      tabs.add('sidebar.actual', overview);
+      tabs.add('node.nodes', nodelist);
+      tabs.add('node.links', linklist);
+      tabs.add('sidebar.stats', statistics);
+      tabs.add('sidebar.about', about);
 
       router.addTarget(title);
       router.addTarget(infobox);
diff --git a/lib/infobox/link.js b/lib/infobox/link.js
index 8a7f2035a6b159ac25ac879382f7508c2a90eca7..1dd5c76f2269c9d6dc766a8873f0e739edd877b2 100644
--- a/lib/infobox/link.js
+++ b/lib/infobox/link.js
@@ -6,6 +6,7 @@ define(['helper'], function (helper) {
     subst['{SOURCE}'] = source;
     subst['{TARGET}'] = target;
     subst['{TIME}'] = time;
+    subst['{LOCALE}'] = _.locale();
     return helper.showStat(o, subst);
   }
 
@@ -37,11 +38,12 @@ define(['helper'], function (helper) {
     var attributes = document.createElement('table');
     attributes.classList.add('attributes');
 
-    helper.attributeEntry(attributes, 'TQ', helper.showTq(d));
-    helper.attributeEntry(attributes, 'Entfernung', helper.showDistance(d));
+    helper.attributeEntry(attributes, 'node.tq', helper.showTq(d));
+    helper.attributeEntry(attributes, 'node.distance', helper.showDistance(d));
     var hw1 = unknown ? null : helper.dictGet(d.source.node.nodeinfo, ['hardware', 'model']);
     var hw2 = helper.dictGet(d.target.node.nodeinfo, ['hardware', 'model']);
-    helper.attributeEntry(attributes, 'Hardware', (hw1 !== null ? hw1 : 'unbekannt') + ' – ' + (hw2 !== null ? hw2 : 'unbekannt'));
+    helper.attributeEntry(attributes, 'node.hardware', (hw1 !== null ? hw1 : _.t('unknown')) + ' – ' + (hw2 !== null ? hw2 : _.t('unknown')));
+
     el.appendChild(attributes);
 
     if (config.linkInfos) {
diff --git a/lib/infobox/location.js b/lib/infobox/location.js
index 909df48b012d9ecf28876a50aef465e81344bc84..802893bb5aa07a0eee599a5c1dcae5a07d3f50e2 100644
--- a/lib/infobox/location.js
+++ b/lib/infobox/location.js
@@ -3,10 +3,10 @@ define(['helper'], function (helper) {
 
   return function (config, el, router, d) {
     var sidebarTitle = document.createElement('h2');
-    sidebarTitle.textContent = 'Location: ' + d.toString();
+    sidebarTitle.textContent = _.t('location.location') + ': ' + d.toString();
     el.appendChild(sidebarTitle);
 
-    helper.getJSON(config.reverseGeocodingApi + '?format=json&lat=' + d.lat + '&lon=' + d.lng + '&zoom=18&addressdetails=0')
+    helper.getJSON(config.reverseGeocodingApi + '?format=json&lat=' + d.lat + '&lon=' + d.lng + '&zoom=18&addressdetails=0&accept-language=' + _.locale())
       .then(function (result) {
         if (result.display_name) {
           sidebarTitle.textContent = result.display_name;
@@ -16,12 +16,12 @@ define(['helper'], function (helper) {
     var editLat = document.createElement('input');
     editLat.type = 'text';
     editLat.value = d.lat.toFixed(9);
-    el.appendChild(createBox('lat', 'Breitengrad', editLat));
+    el.appendChild(createBox('lat', _.t('location.latitude'), editLat));
 
     var editLng = document.createElement('input');
     editLng.type = 'text';
     editLng.value = d.lng.toFixed(9);
-    el.appendChild(createBox('lng', 'Längengrad', editLng));
+    el.appendChild(createBox('lng', _.t('location.longitude'), editLng));
 
     var editUci = document.createElement('textarea');
     editUci.value =
@@ -40,7 +40,7 @@ define(['helper'], function (helper) {
       box.appendChild(heading);
       var btn = document.createElement('button');
       btn.classList.add('ion-ios-copy');
-      btn.title = 'Kopieren';
+      btn.title = _.t('location.copy');
       btn.onclick = function onclick() {
         copy2clip(inputElem.id);
       };
diff --git a/lib/infobox/node.js b/lib/infobox/node.js
index 6ed6f8c56b70de22906cf4c7bd13df9130a92873..c98b204fe931678a9625b96e27885b3479390932 100644
--- a/lib/infobox/node.js
+++ b/lib/infobox/node.js
@@ -1,5 +1,5 @@
-define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de'],
-  function (chroma, moment, tablesort, helper) {
+define(['chroma-js', 'moment', 'tablesort', 'helper'],
+  function (chroma, moment, Tablesort, helper) {
     'use strict';
 
     function showGeoURI(d) {
@@ -19,9 +19,15 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
       return function (el) {
         el.classList.add(d.flags.unseen ? 'unseen' : (d.flags.online ? 'online' : 'offline'));
         if (d.flags.online) {
-          el.textContent = 'online, letzte Nachricht ' + d.lastseen.fromNow() + ' (' + d.lastseen.format('DD.MM.YYYY,  H:mm:ss') + ')';
+          el.textContent = _.t('node.lastOnline', {
+            time: d.lastseen.fromNow(),
+            date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
+          });
         } else {
-          el.textContent = 'offline, letzte Nachricht ' + d.lastseen.fromNow() + ' (' + d.lastseen.format('DD.MM.YYYY,  H:mm:ss') + ')';
+          el.textContent = _.t('node.lastOffline', {
+            time: d.lastseen.fromNow(),
+            date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
+          });
         }
       };
     }
@@ -72,7 +78,7 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
       }
 
       return function (el) {
-        el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : 'keine'));
+        el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : _.t('none')));
         el.appendChild(document.createElement('br'));
 
         var span = document.createElement('span');
@@ -169,14 +175,15 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
         return undefined;
       }
 
-      return au.enabled ? 'aktiviert (' + au.branch + ')' : 'deaktiviert';
+      return au.enabled ? _.t('node.activated', {branch: au.branch}) : _.t('node.deactivated');
     }
 
     function showStatImg(o, d) {
       var subst = {};
-      subst['{NODE_ID}'] = d.nodeinfo.node_id ? d.nodeinfo.node_id : 'unknown';
-      subst['{NODE_NAME}'] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_') : 'unknown';
+      subst['{NODE_ID}'] = d.nodeinfo.node_id ? d.nodeinfo.node_id : _.t('unknown');
+      subst['{NODE_NAME}'] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_') : _.t('unknown');
       subst['{TIME}'] = d.lastseen.format('DDMMYYYYHmmss');
+      subst['{LOCALE}'] = _.locale();
       return helper.showStat(o, subst);
     }
 
@@ -189,29 +196,29 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
       var attributes = document.createElement('table');
       attributes.classList.add('attributes');
 
-      helper.attributeEntry(attributes, 'Status', showStatus(d));
-      helper.attributeEntry(attributes, 'Gateway', d.flags.gateway ? 'ja' : null);
-      helper.attributeEntry(attributes, 'Koordinaten', showGeoURI(d));
+      helper.attributeEntry(attributes, 'node.status', showStatus(d));
+      helper.attributeEntry(attributes, 'node.gateway', d.flags.gateway ? 'ja' : null);
+      helper.attributeEntry(attributes, 'node.coordinates', showGeoURI(d));
 
       if (config.nodeInfobox && config.nodeInfobox.contact) {
-        helper.attributeEntry(attributes, 'Kontakt', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
+        helper.attributeEntry(attributes, 'node.contact', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
       }
 
-      helper.attributeEntry(attributes, 'Hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
-      helper.attributeEntry(attributes, 'Primäre MAC', helper.dictGet(d.nodeinfo, ['network', 'mac']));
-      helper.attributeEntry(attributes, 'Node ID', helper.dictGet(d.nodeinfo, ['node_id']));
-      helper.attributeEntry(attributes, 'Firmware', showFirmware(d));
-      helper.attributeEntry(attributes, 'Site', showSite(d, config));
-      helper.attributeEntry(attributes, 'Uptime', showUptime(d));
-      helper.attributeEntry(attributes, 'Teil des Netzes', showFirstseen(d));
+      helper.attributeEntry(attributes, 'node.hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
+      helper.attributeEntry(attributes, 'node.primaryMac', helper.dictGet(d.nodeinfo, ['network', 'mac']));
+      helper.attributeEntry(attributes, 'node.id', helper.dictGet(d.nodeinfo, ['node_id']));
+      helper.attributeEntry(attributes, 'node.firmware', showFirmware(d));
+      helper.attributeEntry(attributes, 'node.site', showSite(d, config));
+      helper.attributeEntry(attributes, 'node.uptime', showUptime(d));
+      helper.attributeEntry(attributes, 'node.firstSeen', showFirstseen(d));
       if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
-        helper.attributeEntry(attributes, 'Systemlast', showLoad(d));
-        helper.attributeEntry(attributes, 'Arbeitsspeicher', showRAM(d));
+        helper.attributeEntry(attributes, 'node.systemLoad', showLoad(d));
+        helper.attributeEntry(attributes, 'node.ram', showRAM(d));
       }
-      helper.attributeEntry(attributes, 'IP Adressen', showIPs(d));
-      helper.attributeEntry(attributes, 'Gewähltes Gateway', helper.dictGet(d.statistics, ['gateway']));
-      helper.attributeEntry(attributes, 'Autom. Updates', showAutoupdate(d));
-      helper.attributeEntry(attributes, 'Clients', showClients(d));
+      helper.attributeEntry(attributes, 'node.ipAddresses', showIPs(d));
+      helper.attributeEntry(attributes, 'node.selectedGateway', helper.dictGet(d.statistics, ['gateway']));
+      helper.attributeEntry(attributes, 'node.update', showAutoupdate(d));
+      helper.attributeEntry(attributes, 'node.clients', showClients(d));
 
       el.appendChild(attributes);
 
@@ -226,7 +233,7 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
 
       if (d.neighbours.length > 0) {
         var h3 = document.createElement('h3');
-        h3.textContent = 'Links (' + d.neighbours.length + ')';
+        h3.textContent = _.t('node.link', d.neighbours.length) + '(' + d.neighbours.length + ')';
         el.appendChild(h3);
 
         var table = document.createElement('table');
@@ -238,16 +245,16 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
         tr.appendChild(th1);
 
         var th2 = document.createElement('th');
-        th2.textContent = 'Knoten';
+        th2.textContent = _.t('node.node', d.neighbours.length);
         th2.classList.add('sort-default');
         tr.appendChild(th2);
 
         var th3 = document.createElement('th');
-        th3.textContent = 'TQ';
+        th3.textContent = _.t('node.tq');
         tr.appendChild(th3);
 
         var th4 = document.createElement('th');
-        th4.textContent = 'Entfernung';
+        th4.textContent = _.t('node.distance');
         tr.appendChild(th4);
 
         thead.appendChild(tr);
diff --git a/lib/legend.js b/lib/legend.js
index 9cf9878db2b664ee620563ba8951d5aa1fea83a1..22fa65dc9979189803d7b32af4f005d1bf5666d0 100644
--- a/lib/legend.js
+++ b/lib/legend.js
@@ -16,12 +16,11 @@ define(['helper'], function (helper) {
         return n.flags.gateway;
       }).map(helper.one));
 
-      stats.textContent = totalNodes + ' Knoten, ' +
-        'davon ' + totalOnlineNodes + ' Knoten online ' +
-        'mit ' + totalClients + ' Client' + ( totalClients === 1 ? ' ' : 's ' ) +
-        'auf ' + totalGateways + ' Gateway' + ( totalGateways === 1 ? '' : 's' );
+      stats.textContent = _.t('sidebar.nodes', {total: totalNodes, online: totalOnlineNodes}) + ' ' +
+        _.t('sidebar.clients', {smart_count: totalClients}) + ' ' +
+        _.t('sidebar.gateway', {smart_count: totalGateways});
 
-      timestamp.textContent = 'Stand: ' + d.timestamp.format('DD.MM.Y HH:mm');
+      timestamp.textContent = _.t('sidebar.lastUpdate') + ': ' + d.timestamp.format('DD.MM.Y HH:mm');
     };
 
     self.render = function render(el) {
@@ -31,9 +30,9 @@ define(['helper'], function (helper) {
 
       var p = document.createElement('p');
       p.classList.add('legend');
-      p.innerHTML = '<span class="legend-new"><span class="symbol"></span> Neuer Knoten</span>' +
-        '<span class="legend-online"><span class="symbol"></span> Knoten ist online</span>' +
-        '<span class="legend-offline"><span class="symbol"></span> Knoten ist offline</span>';
+      p.innerHTML = '<span class="legend-new"><span class="symbol"></span> ' + _.t('sidebar.nodeNew') + '</span>' +
+        '<span class="legend-online"><span class="symbol"></span> ' + _.t('sidebar.nodeOnline') + '</span>' +
+        '<span class="legend-offline"><span class="symbol"></span> ' + _.t('sidebar.nodeOffline') + '</span>';
       el.appendChild(p);
 
       p.appendChild(document.createElement('br'));
diff --git a/lib/linklist.js b/lib/linklist.js
index 67503abfd3227620b9bc5ae238a339d8b0e2d1a1..4548a4ecd860a067674e3eff7e49d07a0c769666 100644
--- a/lib/linklist.js
+++ b/lib/linklist.js
@@ -6,19 +6,19 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
   }
 
   var headings = [{
-    name: 'Knoten',
+    name: 'node.nodes',
     sort: function (a, b) {
       return linkName(a).localeCompare(linkName(b));
     },
     reverse: false
   }, {
-    name: 'TQ',
+    name: 'node.tq',
     sort: function (a, b) {
       return a.tq - b.tq;
     },
     reverse: true
   }, {
-    name: 'Entfernung',
+    name: 'node.distance',
     sort: function (a, b) {
       return (a.distance === undefined ? -1 : a.distance) -
         (b.distance === undefined ? -1 : b.distance);
@@ -42,7 +42,7 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
 
     this.render = function render(d) {
       var h2 = document.createElement('h2');
-      h2.textContent = 'Verbindungen';
+      h2.textContent = _.t('node.links');
       d.appendChild(h2);
 
       d.appendChild(table.el);
diff --git a/lib/main.js b/lib/main.js
index 7f11ec13090e674056e89854e78ae18914dd05e0..9dcc0394e18468d7f2ebe4961d927e7b0c15ed58 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -1,5 +1,5 @@
-define(['moment/moment', 'router', 'leaflet', 'gui', 'helper', 'moment/locale/de'],
-  function (moment, Router, L, GUI, helper) {
+define(['polyglot', 'moment', 'router', 'leaflet', 'gui', 'helper'],
+  function (Polyglot, moment, Router, L, GUI, helper) {
     'use strict';
 
     return function (config) {
@@ -150,7 +150,35 @@ define(['moment/moment', 'router', 'leaflet', 'gui', 'helper', 'moment/locale/de
         };
       }
 
-      moment.locale('de');
+      function setTranslation(json) {
+        _.extend(json);
+
+        moment.locale(_.locale(), {
+          longDateFormat: {
+            LT: 'HH:mm',
+            LTS: 'HH:mm:ss',
+            L: 'DD.MM.YYYY',
+            LL: 'D. MMMM YYYY',
+            LLL: 'D. MMMM YYYY HH:mm',
+            LLLL: 'dddd, D. MMMM YYYY HH:mm'
+          },
+          calendar: json.momentjs.calendar,
+          relativeTime: json.momentjs.relativeTime
+        });
+      }
+
+      var language = navigator.languages && navigator.languages[0] || navigator.language || navigator.userLanguage;
+      var locale = config.supportedLocale[0];
+      config.supportedLocale.some(function (item) {
+        if (language.indexOf(item) !== -1) {
+          locale = item;
+          return true;
+        }
+        return false;
+      });
+
+      window._ = new Polyglot({locale: locale, allowMissing: true});
+      helper.getJSON('locale/' + _.locale() + '.json').then(setTranslation);
 
       var router = new Router();
 
diff --git a/lib/map.js b/lib/map.js
index 53268c2f4eec09ccaff231444ef792a523d7fde7..992a0b05a47b03940f78452de23612c5663fb57c 100644
--- a/lib/map.js
+++ b/lib/map.js
@@ -1,4 +1,6 @@
-define(['map/clientlayer', 'map/labelslayer', 'leaflet', 'moment/moment', 'locationmarker', 'rbush', 'helper', 'leaflet.label', 'moment/locale/de'],
+define(['map/clientlayer', 'map/labelslayer',
+  'leaflet', 'moment', 'locationmarker', 'rbush', 'helper',
+  'leaflet.label'],
   function (ClientLayer, LabelsLayer, L, moment, LocationMarker, rbush, helper) {
     'use strict';
 
diff --git a/lib/nodelist.js b/lib/nodelist.js
index a99a9b6f9268408e1f35a96a2c73aea74a9d7353..613e1201ae7cb49fb5271e0a52ec3cfd545d0292 100644
--- a/lib/nodelist.js
+++ b/lib/nodelist.js
@@ -28,28 +28,28 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
   var headings = [{
     name: ''
   }, {
-    name: 'Knoten',
+    name: 'node.nodes',
     sort: function (a, b) {
       return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname);
     },
     reverse: false
-  }, {
-    name: 'Uptime',
+  },    {
+    name: 'node.uptime',
     sort: function (a, b) {
       return a.uptime - b.uptime;
     },
     reverse: true
-  }, {
-    name: '#Links',
+  },    {
+    name: 'node.links',
     sort: function (a, b) {
       return a.meshlinks - b.meshlinks;
     },
     reverse: true
-  }, {
-    name: 'Clients',
+  },    {
+    name: 'node.clients',
     sort: function (a, b) {
       return ('clients' in a.statistics ? a.statistics.clients : -1) -
-        ('clients' in b.statistics ? b.statistics.clients : -1);
+          ('clients' in b.statistics ? b.statistics.clients : -1);
     },
     reverse: true
   }];
@@ -84,7 +84,7 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
 
     this.render = function render(d) {
       var h2 = document.createElement('h2');
-      h2.textContent = 'Alle Knoten';
+      h2.textContent = _.t('node.all');
       d.appendChild(h2);
 
       d.appendChild(table.el);
diff --git a/lib/proportions.js b/lib/proportions.js
index f3dfca25d0d004da29f614568b9b86cdac8eb1b0..32c37b0a93e49da2f8ce74c0520fcff5655711f0 100644
--- a/lib/proportions.js
+++ b/lib/proportions.js
@@ -75,7 +75,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
           var c1 = Chroma.contrast(scale(v), 'white');
           var c2 = Chroma.contrast(scale(v), 'black');
 
-          var filter = new Filter(name, d[2], d[0], d[3]);
+          var filter = new Filter(_.t(name), d[2], d[0], d[3]);
 
           var a = V.h('a', { href: '#', onclick: addFilter(filter) }, d[0]);
 
@@ -111,7 +111,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
         var fwDict = count(nodes, ['nodeinfo', 'software', 'firmware', 'release']);
         var hwDict = count(nodes, ['nodeinfo', 'hardware', 'model']);
         var geoDict = count(nodes, ['nodeinfo', 'location'], function (d) {
-          return d && d.longitude && d.latitude ? 'ja' : 'nein';
+          return d && d.longitude && d.latitude ? _.t('yes') : _.t('no');
         });
 
         var autoDict = count(nodes, ['nodeinfo', 'software', 'autoupdater'], function (d) {
@@ -120,7 +120,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
           } else if (d.enabled) {
             return d.branch;
           }
-          return '(deaktiviert)';
+          return _.t('node.deactivated');
         });
 
         var siteDict = count(nodes, ['nodeinfo', 'system', 'site_code'], function (d) {
@@ -135,10 +135,10 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
           return rt;
         });
 
-        fillTable('Status', statusTable, statusDict.sort(function (a, b) {
+        fillTable('node.status', statusTable, statusDict.sort(function (a, b) {
           return b[1] - a[1];
         }));
-        fillTable('Firmware', fwTable, fwDict.sort(function (a, b) {
+        fillTable('node.firmware', fwTable, fwDict.sort(function (a, b) {
           if (b[0] < a[0]) {
             return -1;
           }
@@ -147,28 +147,28 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
           }
           return 0;
         }));
-        fillTable('Hardware', hwTable, hwDict.sort(function (a, b) {
+        fillTable('node.hardware', hwTable, hwDict.sort(function (a, b) {
           return b[1] - a[1];
         }));
-        fillTable('Koordinaten', geoTable, geoDict.sort(function (a, b) {
+        fillTable('node.visible', geoTable, geoDict.sort(function (a, b) {
           return b[1] - a[1];
         }));
-        fillTable('Autom. Updates', autoTable, autoDict.sort(function (a, b) {
+        fillTable('node.update', autoTable, autoDict.sort(function (a, b) {
           return b[1] - a[1];
         }));
-        fillTable('Site', siteTable, siteDict.sort(function (a, b) {
+        fillTable('node.site', siteTable, siteDict.sort(function (a, b) {
           return b[1] - a[1];
         }));
       };
 
       self.render = function render(el) {
         var h2;
-        self.renderSingle(el, 'Status', statusTable);
-        self.renderSingle(el, 'Firmwareversionen', fwTable);
-        self.renderSingle(el, 'Hardwaremodelle', hwTable);
-        self.renderSingle(el, 'Auf der Karte sichtbar', geoTable);
-        self.renderSingle(el, 'Autoupdater', autoTable);
-        self.renderSingle(el, 'Site', siteTable);
+        self.renderSingle(el, 'node.status', statusTable);
+        self.renderSingle(el, 'node.firmware', fwTable);
+        self.renderSingle(el, 'node.hardware', hwTable);
+        self.renderSingle(el, 'node.visible', geoTable);
+        self.renderSingle(el, 'node.update', autoTable);
+        self.renderSingle(el, 'node.site', siteTable);
 
         if (config.globalInfos) {
           config.globalInfos.forEach(function (globalInfo) {
@@ -183,7 +183,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
       self.renderSingle = function renderSingle(el, heading, table) {
         var h2;
         h2 = document.createElement('h2');
-        h2.textContent = heading;
+        h2.textContent = _.t(heading);
         h2.onclick = function onclick() {
           table.classList.toggle('hidden');
         };
diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js
index 26c4f5b33e6a4a47863c71375cfa885b61c792c1..6ac17c80e194d2e260b1598f1e740a75affcf96b 100644
--- a/lib/simplenodelist.js
+++ b/lib/simplenodelist.js
@@ -1,4 +1,4 @@
-define(['moment/moment', 'virtual-dom', 'helper', 'moment/locale/de'], function (moment, V, helper) {
+define(['moment', 'virtual-dom', 'helper'], function (moment, V, helper) {
   'use strict';
 
   return function (nodes, field, router, title) {
diff --git a/lib/sorttable.js b/lib/sorttable.js
index 2fa43d32f525d4c0e46fa603627d2fb84902fade..966559436844ddf7737cc1f050de527056aa57d9 100644
--- a/lib/sorttable.js
+++ b/lib/sorttable.js
@@ -34,7 +34,7 @@ define(['virtual-dom'], function (V) {
             properties.className += sortReverse ? ' sort-up' : ' sort-down';
           }
 
-          return V.h('th', properties, d.name);
+          return V.h('th', properties, _.t(d.name));
         });
 
         var links = data.slice(0).sort(headings[sortIndex].sort);
diff --git a/lib/tabs.js b/lib/tabs.js
index 9d8220ebeb21f3fb07472ff4f59e42622f812faa..f3c13d20de35598044aafe1679f137de2cc47a0b 100644
--- a/lib/tabs.js
+++ b/lib/tabs.js
@@ -34,7 +34,7 @@ define(function () {
 
     self.add = function add(title, d) {
       var li = document.createElement('li');
-      li.textContent = title;
+      li.textContent = _.t(title);
       li.onclick = switchTab;
       li.child = d;
       tabs.appendChild(li);
diff --git a/lib/utils/helper.js b/lib/utils/helper.js
index 003b06bb3cd08175d5743e8b85e6e40dc6fbcea1..ed1aa3480effb5717029bf92c55205b440d04f01 100644
--- a/lib/utils/helper.js
+++ b/lib/utils/helper.js
@@ -132,9 +132,10 @@ define({
       return '';
     }
 
+
     var tr = document.createElement('tr');
     var th = document.createElement('th');
-    th.textContent = label;
+    th.textContent = _.t(label);
     tr.appendChild(th);
 
     var td = document.createElement('td');
diff --git a/locale/de.json b/locale/de.json
new file mode 100644
index 0000000000000000000000000000000000000000..9034e425049f82e5671aa6d941b71294c0bc527c
--- /dev/null
+++ b/locale/de.json
@@ -0,0 +1,83 @@
+{
+  "node":{
+    "all":"Alle Knoten",
+    "nodes":"Knoten",
+    "uptime":"Laufzeit",
+    "links":"Verbindungen",
+    "clients":"Nutzer",
+    "distance":"Entfernung",
+    "tq":"TQ",
+    "lastOnline":"online, letzte Nachricht %{time} (%{date})",
+    "lastOffline":"offline, letzte Nachricht %{time} (%{date})",
+    "activated":"aktiviert (%{branch})",
+    "deactivated":"deaktiviert",
+    "status":"Status",
+    "firmware":"Firmware-Version",
+    "hardware":"Geräte-Modell",
+    "visible":"Auf der Karte sichtbar",
+    "update":"Auto-Update",
+    "site":"Site",
+    "gateway":"Gateway",
+    "coordinates":"Koordinaten",
+    "contact":"Kontakt",
+    "primaryMac":"Primäre MAC",
+    "id":"Knoten ID",
+    "firstSeen":"Erstmals gesehen",
+    "systemLoad":"Load average",
+    "ram":"Speicherauslastung",
+    "ipAddresses":"IP Adressen",
+    "selectedGateway":"Gewähltes Gateway",
+    "link":"Verbindung |||| Verbindungen",
+    "node":"Knoten",
+    "new":"Neuer Knoten",
+    "missing":"Verschwundene Knoten"
+  },
+  "location":{
+    "location":"Standort",
+    "latitude":"Breitengrad",
+    "longitude":"Längengrad",
+    "copy":"Kopieren"
+  },
+  "sidebar":{
+    "nodes":"%{total} Knoten, davon %{online} Knoten online",
+    "clients":"mit %{smart_count} Nutzer |||| mit %{smart_count} Nutzern",
+    "gateway":"auf %{smart_count} Gateway |||| auf %{smart_count} Gateways",
+    "lastUpdate":"Letzte Aktualisierung",
+    "nodeNew":"Knoten ist neu",
+    "nodeOnline":"Knoten ist online",
+    "nodeOffline":"Knoten ist offline",
+    "aboutInfo":"<h2>Über Meshviewer</h2><p>Mit Doppelklick kann man in die Karte hinein zoomen und Shift+Doppelklick heraus zoomen.</p>",
+    "actual":"Aktuell",
+    "stats":"Statistiken",
+    "about":"Über"
+  },
+  "momentjs":{
+    "calendar":{
+      "sameDay":"[heute um] LT [Uhr]",
+      "nextDay":"[morgen um] LT [Uhr]",
+      "nextWeek":"dddd [um] LT [Uhr]",
+      "lastDay":"[gestern um] LT [Uhr]",
+      "lastWeek":"[letzten] dddd [um] LT [Uhr]",
+      "sameElse":"L"
+    },
+    "relativeTime":{
+      "future":"in %s",
+      "past":"vor %s",
+      "s":"ein paar Sekunden",
+      "m":"einer Minute",
+      "mm":"%d Minuten",
+      "h":"einer Stunde",
+      "hh":"%d Stunden",
+      "d":"einem Tag",
+      "dd":"%d Tagen",
+      "M":"einem Monat",
+      "MM":"%d Monate",
+      "y":"einem Jahr",
+      "yy":"%d Jahre"
+    }
+  },
+  "yes":"ja",
+  "no":"nein",
+  "unknown":"unbekannt",
+  "none":"keine"
+}
diff --git a/locale/en.json b/locale/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..1b4ec56d3e794924e15e9105fd1e462fbc408e7b
--- /dev/null
+++ b/locale/en.json
@@ -0,0 +1,83 @@
+{
+  "node": {
+    "all": "All nodes",
+    "nodes": "Nodes",
+    "uptime": "Uptime",
+    "links": "Links",
+    "clients": "Clients",
+    "distance": "Distance",
+    "tq": "TQ",
+    "lastOnline": "online, last message %{time} (%{date})",
+    "lastOffline": "offline, last message %{time} (%{date})",
+    "activated": "activated (%{branch})",
+    "deactivated": "deactivated",
+    "status": "Status",
+    "firmware": "Firmware version",
+    "hardware": "Hardware model",
+    "visible": "Visible on the map",
+    "update": "Auto update",
+    "site": "Site",
+    "gateway": "Gateway",
+    "coordinates": "Coordinates",
+    "contact": "Contact",
+    "primaryMac": "Primary MAC",
+    "id": "Node ID",
+    "firstSeen": "First seen",
+    "systemLoad": "Load average",
+    "ram": "Memory usage",
+    "ipAddresses": "IP addresses",
+    "selectedGateway": "Selected gateway",
+    "link": "Link |||| Links",
+    "node": "Node |||| Nodes",
+    "new": "New nodes",
+    "missing": "Disappeared nodes"
+  },
+  "location": {
+    "location": "Location",
+    "latitude": "Latitude",
+    "longitude": "Longitude",
+    "copy": "Copy"
+  },
+  "sidebar": {
+    "nodes": "%{total} nodes, including %{online} nodes online",
+    "clients": "with %{smart_count} client |||| with %{smart_count} clients",
+    "gateway": "on %{smart_count} gateway |||| on %{smart_count} gateways",
+    "lastUpdate": "Last update",
+    "nodeNew": "Node is new",
+    "nodeOnline": "Node is online",
+    "nodeOffline": "Node is offline",
+    "aboutInfo": "<h2>About Meshviewer</h2><p>You can zoom in with double-click and zoom out with shift+double-click</p>",
+    "actual": "Actual",
+    "stats": "Statistics",
+    "about": "About"
+  },
+  "momentjs": {
+    "calendar": {
+      "sameDay": "[Today at] LT",
+      "nextDay": "[Tomorrow at] LT",
+      "nextWeek": "dddd [at] LT",
+      "lastDay": "[Yesterday at] LT",
+      "lastWeek": "[Last] dddd [at] LT",
+      "sameElse": "L"
+    },
+    "relativeTime": {
+      "future": "in %s",
+      "past": "%s ago",
+      "s": "a few seconds",
+      "m": "a minute",
+      "mm": "%d minutes",
+      "h": "an hour",
+      "hh": "%d hours",
+      "d": "a day",
+      "dd": "%d days",
+      "M": "a month",
+      "MM": "%d months",
+      "y": "a year",
+      "yy": "%d years"
+    }
+  },
+  "yes": "yes",
+  "no": "no",
+  "unknown": "unknown",
+  "none": "none"
+}
diff --git a/package.json b/package.json
index bf680672474d8e1173087e94e9125f2a584a26ab..f72c79d777232e9dfd3d2007a5ff9c3270cf0951 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
     "grunt-eslint": "^19.0.0",
     "grunt-inline": "^0.3.6",
     "grunt-inline-data": "git://github.com/xiaokaike/grunt-inline-data.git#2eeb08f",
+    "grunt-json-minify": "^1.1.0",
     "grunt-postcss": "^0.8.0",
     "grunt-sass": "^2.0.0",
     "grunt-sass-lint": "^0.2.2"
@@ -48,6 +49,7 @@
     "leaflet": "https://github.com/davojta/Leaflet.git#stable_0_7_7_1_release",
     "leaflet-label": "^0.2.1-0",
     "moment": "^2.17.1",
+    "node-polyglot": "airbnb/polyglot.js",
     "promise-polyfill": "^6.0.2",
     "rbush": "1.4.3",
     "requirejs": "^2.3.2",
diff --git a/tasks/build.js b/tasks/build.js
index ec0f4de4764a296c97b874aa7259c539276833c5..97ea3366f95e0f48cc04943309d9fd8d35a5d321 100644
--- a/tasks/build.js
+++ b/tasks/build.js
@@ -33,6 +33,12 @@ module.exports = function exports(grunt) {
         expand: true,
         dest: 'build/',
         cwd: 'assets/'
+      },
+      locale: {
+        src: ['locale/*'],
+        expand: true,
+        dest: 'build/',
+        cwd: '.'
       }
     },
     sass: {
@@ -123,6 +129,11 @@ module.exports = function exports(grunt) {
         }
       }
     },
+    'json-minify': {
+      build: {
+        files: 'build/locale/*.json'
+      }
+    },
     cachebreaker: {
       default: {
         options: {
@@ -142,5 +153,6 @@ module.exports = function exports(grunt) {
   grunt.loadNpmTasks('grunt-inline');
   grunt.loadNpmTasks('grunt-inline-data');
   grunt.loadNpmTasks('grunt-contrib-htmlmin');
+  grunt.loadNpmTasks('grunt-json-minify');
   grunt.loadNpmTasks('grunt-cache-breaker');
 };
diff --git a/tasks/development.js b/tasks/development.js
index 24dc74cd4fa48afe541ca26e377614c7eb9b44a9..e8a312e65fe7faf2cafd44e410fb7814e2b492d3 100644
--- a/tasks/development.js
+++ b/tasks/development.js
@@ -24,12 +24,12 @@ module.exports = function exports(grunt) {
     },
     watch: {
       html: {
-        files: ['html/index.html', 'config.json'],
-        tasks: ['copy', 'inlinedata', 'htmlmin']
+        files: ['html/index.html', 'config.json', 'locale/*.json'],
+        tasks: ['copy', 'inlinedata', 'htmlmin', 'json-minify']
       },
       sass: {
         files: ['scss/**/*.scss'],
-        tasks: ['sasslint', 'sass',  'postcss']
+        tasks: ['sasslint', 'sass', 'postcss']
       },
       js: {
         files: ['app.js', 'lib/**/*.js'],
diff --git a/yarn.lock b/yarn.lock
index 6fc33d012f7a39e807d25094430ff01aee1e3396..e423ab123a8bd19a1475a8b97f75c6021782ef8f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -926,7 +926,7 @@ error@^4.3.0:
     string-template "~0.2.0"
     xtend "~4.0.0"
 
-es-abstract@^1.7.0:
+es-abstract@^1.5.0, es-abstract@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
   dependencies:
@@ -1286,6 +1286,12 @@ flat-cache@^1.2.1:
     graceful-fs "^4.1.2"
     write "^0.2.1"
 
+for-each@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
+  dependencies:
+    is-function "~1.0.0"
+
 for-in@^0.1.5:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
@@ -1362,7 +1368,7 @@ fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10:
     mkdirp ">=0.5 0"
     rimraf "2"
 
-function-bind@^1.1.0:
+function-bind@^1.0.2, function-bind@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
 
@@ -1583,6 +1589,10 @@ grunt-inline@^0.3.6:
     datauri "~0.2.0"
     uglify-js "2.4.1"
 
+grunt-json-minify@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/grunt-json-minify/-/grunt-json-minify-1.1.0.tgz#a20b9a3016d1e8fed0c79560c77b94701f64f6fb"
+
 grunt-known-options@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-1.1.0.tgz#a4274eeb32fa765da5a7a3b1712617ce3b144149"
@@ -1704,6 +1714,12 @@ has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
 
+has@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+  dependencies:
+    function-bind "^1.0.2"
+
 hawk@~3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@@ -1923,6 +1939,10 @@ is-fullwidth-code-point@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
 
+is-function@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
+
 is-glob@^2.0.0, is-glob@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
@@ -2473,6 +2493,15 @@ node-gyp@^3.3.1:
     tar "^2.0.0"
     which "1"
 
+node-polyglot@airbnb/polyglot.js:
+  version "2.2.2"
+  resolved "https://codeload.github.com/airbnb/polyglot.js/tar.gz/eeae4dadf7c0f57bf3266e5370754765ec561037"
+  dependencies:
+    for-each "^0.3.2"
+    has "^1.0.1"
+    string.prototype.trim "^1.1.2"
+    warning "^3.0.0"
+
 node-pre-gyp@^0.6.29:
   version "0.6.32"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5"
@@ -3314,6 +3343,14 @@ string-width@^2.0.0:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^3.0.0"
 
+string.prototype.trim@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.5.0"
+    function-bind "^1.0.2"
+
 string_decoder@~0.10.x:
   version "0.10.31"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@@ -3350,11 +3387,11 @@ strip-indent@^1.0.1:
   dependencies:
     get-stdin "^4.0.1"
 
-strip-json-comments@1.0.2:
+strip-json-comments@1.0.2, strip-json-comments@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.2.tgz#5a48ab96023dbac1b7b8d0ffabf6f63f1677be9f"
 
-strip-json-comments@~1.0.1, strip-json-comments@~1.0.4:
+strip-json-comments@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
 
@@ -3593,6 +3630,12 @@ vlq@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.1.tgz#14439d711891e682535467f8587c5630e4222a6c"
 
+warning@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
+  dependencies:
+    loose-envify "^1.0.0"
+
 websocket-driver@>=0.5.1:
   version "0.6.5"
   resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"