diff --git a/gulp/tasks/i18next.js b/gulp/tasks/i18next.js
index f7eb9bd44..396165920 100644
--- a/gulp/tasks/i18next.js
+++ b/gulp/tasks/i18next.js
@@ -1,10 +1,10 @@
-import _ from 'lodash';
 import fs from 'fs';
 import gulp from 'gulp';
 import gutil from 'gulp-util';
 import sort from 'gulp-sort';
 import i18nextScanner from 'i18next-scanner';
 import table from 'text-table';
+import { languages } from '../../i18n.config';
 
 const appConfig = {
     src: [
@@ -57,20 +57,7 @@ const webConfig = {
         debug: false,
         sort: false,
         removeUnusedKeys: true,
-        lngs: [
-            'en', // English (default)
-            'cs', // Czech
-            'de', // German
-            'es', // Spanish
-            'fr', // French
-            'hu', // Hungarian
-            'it', // Italian
-            'ja', // Japanese
-            'pt-br', // Portuguese (Brazil)
-            'ru', // Russian
-            'zh-cn', // Simplified Chinese
-            'zh-tw' // Traditional Chinese
-        ],
+        lngs: languages,
         defaultValue: (lng, ns, key) => {
             if (lng === 'en') {
                 return key; // Use key as value for base language
@@ -100,7 +87,7 @@ const webConfig = {
 function customTransform(file, enc, done) {
     const parser = this.parser;
     const content = fs.readFileSync(file.path, enc);
-    let tableData = [
+    const tableData = [
         ['Key', 'Value']
     ];
 
@@ -115,7 +102,7 @@ function customTransform(file, enc, done) {
         });
     }
 
-    if (_.size(tableData) > 1) {
+    if (tableData.length > 1) {
         const text = table(tableData, { 'hsep': ' | ' });
         gutil.log('i18next-scanner:', file.relative + '\n' + text);
     } else {
diff --git a/webpack.webconfig.i18n.js b/i18n.config.js
similarity index 100%
rename from webpack.webconfig.i18n.js
rename to i18n.config.js
diff --git a/src/app/api/api.i18n.js b/src/app/api/api.i18n.js
deleted file mode 100644
index 3d10444c1..000000000
--- a/src/app/api/api.i18n.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import _ from 'lodash';
-import fs from 'fs';
-import path from 'path';
-import settings from '../config/settings';
-import logger from '../lib/logger';
-
-const log = logger('api:i18n');
-
-export const getAcceptedLanguage = (req, res) => {
-    let headers = req.headers || {};
-    let httpAccept = headers['accept-language'] || '';
-
-    // Tags for the Identification of Languages (http://www.ietf.org/rfc/rfc1766.txt)
-    //
-    // The syntax of this tag in RFC-822 EBNF is:
-    //
-    // Language-Tag = Primary-tag *( "-" Subtag )
-    // Primary-tag = 1*8ALPHA
-    // Subtag = 1*8ALPHA
-
-    let values = httpAccept.split(',') || [];
-    let acceptedList = [];
-    _.each(values, (val) => {
-        let matches = val.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i);
-        if (!matches) {
-            return;
-        }
-        let lang = matches[1];
-        let qval = Number(matches[4]) || Number(1.0);
-        acceptedList.push({
-            lang: lang.toLowerCase(),
-            qval: qval
-        });
-    });
-
-    // In decreasing order of preference
-    let sortedLngs = _.chain(acceptedList)
-                       .sortBy((o) => o.qval)
-                       .reverse()
-                       .map('lang')
-                       .value();
-
-    let preferred, match;
-
-    // 1. Look through sorted list and use first one that exactly matches our languages
-    match = 'exact';
-    preferred = _.find(sortedLngs, (lang) => {
-        return _.contains(settings.supportedLngs, lang);
-    });
-
-    // 2. Look through sorted list again and use first one that partially matches our languages
-    if (!preferred) {
-        match = 'partial';
-        _.some(sortedLngs, (lang) => {
-            preferred = _.find(settings.supportedLngs, (supportedLng) => {
-                return supportedLng.indexOf(lang) === 0;
-            });
-
-            return !!preferred;
-        });
-    }
-
-    // 3. Fallback to default language that matches nothing
-    if (!preferred) {
-        match = 'none';
-        preferred = settings.supportedLngs[0];
-    }
-
-    const result = {
-        acceptedList: acceptedList,
-        sortedLngs: sortedLngs,
-        supportedLngs: settings.supportedLngs,
-        preferred: preferred,
-        match: match
-    };
-    log.debug(`getAcceptedLanguage: ${JSON.stringify(result)}`);
-
-    res.send(preferred);
-};
-
-export const saveMissing = (req, res) => {
-    let lng = req.params.__lng__;
-    let ns = req.params.__ns__;
-
-    let mergedFile = path.join(settings.assets.web.path, 'i18n', lng, ns + '.json');
-    let mergedObject = JSON.parse(fs.readFileSync(mergedFile, 'utf8'));
-
-    let savedMissingFile = path.join(settings.assets.web.path, 'i18n', lng, ns + '.savedMissing.json');
-    let savedMissingObject = req.body;
-
-    // Copy all of the properties in the sendMissing object over to the merged object
-    _.extend(mergedObject, savedMissingObject);
-
-    // Sort object by key
-    let sortedObject = {};
-    let sortedKeys = _.keys(mergedObject).sort();
-    _.each(sortedKeys, (key) => {
-        sortedObject[key] = mergedObject[key];
-    });
-    let prettyJSON = JSON.stringify(sortedObject, null, 4); // space=4
-
-    fs.writeFile(savedMissingFile, prettyJSON, (err) => {
-        if (err) {
-            log.error(`err=${err}`);
-        } else {
-            log.debug(`Saved missing ${JSON.stringify(savedMissingObject)} to "${savedMissingFile}"`);
-        }
-    });
-
-    res.send({ 'reply': 'ok' });
-};
diff --git a/src/app/api/index.js b/src/app/api/index.js
index e88954695..c411fcd28 100644
--- a/src/app/api/index.js
+++ b/src/app/api/index.js
@@ -1,7 +1,6 @@
 export * as version from './api.version';
 export * as state from './api.state';
 export * as gcode from './api.gcode';
-export * as i18n from './api.i18n';
 export * as controllers from './api.controllers';
 export * as commands from './api.commands';
 export * as events from './api.events';
diff --git a/src/app/app.js b/src/app/app.js
index 32c3a1358..43057262d 100644
--- a/src/app/app.js
+++ b/src/app/app.js
@@ -301,10 +301,6 @@ const appMain = () => {
         app.post(urljoin(settings.route, 'api/watch/files'), api.watch.getFiles);
         app.get(urljoin(settings.route, 'api/watch/file'), api.watch.readFile);
         app.post(urljoin(settings.route, 'api/watch/file'), api.watch.readFile);
-
-        // I18n
-        app.get(urljoin(settings.route, 'api/i18n/acceptedLng'), api.i18n.getAcceptedLanguage);
-        app.post(urljoin(settings.route, 'api/i18n/sendMissing/:__lng__/:__ns__'), api.i18n.saveMissing);
     }
 
     // page
diff --git a/src/app/config/settings.base.js b/src/app/config/settings.base.js
index 801a38b38..96bad44c5 100644
--- a/src/app/config/settings.base.js
+++ b/src/app/config/settings.base.js
@@ -1,5 +1,6 @@
 import path from 'path';
 import pkg from '../../package.json';
+import { languages } from '../../../i18n.config';
 
 // RCFile
 const RCFILE = '.cncrc';
@@ -95,21 +96,6 @@ export default {
             saveUninitialized: true
         }
     },
-    // Supported languages
-    supportedLngs: [
-        'en', // English (default)
-        'cs', // Czech
-        'de', // German
-        'es', // Spanish
-        'fr', // French
-        'hu', // Hungarian
-        'it', // Italian
-        'ja', // Japanese
-        'pt-br', // Portuguese (Brazil)
-        'ru', // Russian
-        'zh-cn', // Simplified Chinese
-        'zh-tw' // Traditional Chinese
-    ],
     siofu: { // SocketIOFileUploader
         dir: './tmp/siofu'
     },
@@ -131,20 +117,7 @@ export default {
         // default namespace used if not passed to translation function
         defaultNS: 'resource',
 
-        whitelist: [
-            'en', // English (default)
-            'cs', // Czech
-            'de', // German
-            'es', // Spanish
-            'fr', // French
-            'hu', // Hungarian
-            'it', // Italian
-            'ja', // Japanese
-            'pt-br', // Portuguese (Brazil)
-            'ru', // Russian
-            'zh-cn', // Simplified Chinese
-            'zh-tw' // Traditional Chinese
-        ],
+        whitelist: languages,
 
         // array of languages to preload
         preload: [],
diff --git a/src/app/config/settings.js b/src/app/config/settings.js
index 4df359ab8..06ddbaceb 100644
--- a/src/app/config/settings.js
+++ b/src/app/config/settings.js
@@ -1,4 +1,4 @@
-import _ from 'lodash';
+import merge from 'lodash/merge';
 import base from './settings.base';
 import development from './settings.development';
 import production from './settings.production';
@@ -7,9 +7,9 @@ const env = process.env.NODE_ENV || 'production'; // Ensure production environme
 const settings = {};
 
 if (env === 'development') {
-    _.merge(settings, base, development, { env: env });
+    merge(settings, base, development, { env: env });
 } else {
-    _.merge(settings, base, production, { env: env });
+    merge(settings, base, production, { env: env });
 }
 
 export default settings;
diff --git a/src/web/config/settings.js b/src/web/config/settings.js
index f01493a40..db0433fa2 100644
--- a/src/web/config/settings.js
+++ b/src/web/config/settings.js
@@ -36,20 +36,8 @@ const settings = {
         // default namespace used if not passed to translation function
         defaultNS: 'resource',
 
-        whitelist: [
-            'en', // English (default)
-            'cs', // Czech
-            'de', // German
-            'es', // Spanish
-            'fr', // French
-            'hu', // Hungarian
-            'it', // Italian
-            'ja', // Japanese
-            'pt-br', // Portuguese (Brazil)
-            'ru', // Russian
-            'zh-cn', // Simplified Chinese
-            'zh-tw' // Traditional Chinese
-        ],
+        // @see webpack.config.i18n.js
+        whitelist: process.env.I18N.languages,
 
         // array of languages to preload
         preload: [],
diff --git a/webpack.webconfig.development.js b/webpack.webconfig.development.js
index 4ef29ed03..dfe8bdf6f 100644
--- a/webpack.webconfig.development.js
+++ b/webpack.webconfig.development.js
@@ -14,7 +14,7 @@ const HtmlWebpackPluginAddons = require('html-webpack-plugin-addons');
 const nib = require('nib');
 const stylusLoader = require('stylus-loader');
 const baseConfig = require('./webpack.webconfig.base');
-const languages = require('./webpack.webconfig.i18n').languages;
+const languages = require('./i18n.config').languages;
 const pkg = require('./package.json');
 
 const timestamp = new Date().getTime();
@@ -48,7 +48,10 @@ const webpackConfig = Object.assign({}, baseConfig, {
         new webpack.DefinePlugin({
             'process.env': {
                 // This has effect on the react lib size
-                NODE_ENV: JSON.stringify('development')
+                NODE_ENV: JSON.stringify('development'),
+                I18N: JSON.stringify({
+                    languages: languages
+                })
             }
         }),
         new webpack.HotModuleReplacementPlugin(),
diff --git a/webpack.webconfig.production.js b/webpack.webconfig.production.js
index d915ffa20..b93e0a4f6 100644
--- a/webpack.webconfig.production.js
+++ b/webpack.webconfig.production.js
@@ -15,7 +15,7 @@ const HtmlWebpackPluginAddons = require('html-webpack-plugin-addons');
 const nib = require('nib');
 const stylusLoader = require('stylus-loader');
 const baseConfig = require('./webpack.webconfig.base');
-const languages = require('./webpack.webconfig.i18n').languages;
+const languages = require('./i18n.config').languages;
 const pkg = require('./package.json');
 
 // Use publicPath for production
@@ -55,7 +55,10 @@ const webpackConfig = Object.assign({}, baseConfig, {
         new webpack.DefinePlugin({
             'process.env': {
                 // This has effect on the react lib size
-                NODE_ENV: JSON.stringify('production')
+                NODE_ENV: JSON.stringify('production'),
+                I18N: JSON.stringify({
+                    languages: languages
+                })
             }
         }),
         new webpack.NoEmitOnErrorsPlugin(),