From 437770538c988bede423b66ce9138ee9407be6de Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 May 2016 10:32:28 -0400 Subject: [PATCH] work-in-progress commit for #4655 --- src/languages.js | 42 ++++++++++++++++++++++++++- src/meta.js | 2 ++ src/middleware/middleware.js | 14 +++++++++ src/plugins.js | 55 ++++++++++++++++++------------------ src/plugins/load.js | 15 ++++------ src/routes/index.js | 3 +- src/webserver.js | 2 ++ 7 files changed, 95 insertions(+), 38 deletions(-) diff --git a/src/languages.js b/src/languages.js index de1d82bcff..2ad64fd588 100644 --- a/src/languages.js +++ b/src/languages.js @@ -3,8 +3,48 @@ var fs = require('fs'), path = require('path'), async = require('async'), + LRU = require('lru-cache'), + _ = require('underscore'); - Languages = {}; +var plugins = require('./plugins'); + +var Languages = {}; + +Languages.init = function(next) { + if (Languages.hasOwnProperty('_cache')) { + Languages._cache.reset(); + } else { + Languages._cache = LRU(100); + } + + next(); +}; + +Languages.get = function(code, key, callback) { + var combined = [code, key].join('/'); + + if (Languages._cache.has(combined)) { + return callback(null, Languages._cache.get(combined)); + } + + var languageData; + + fs.readFile(path.join(__dirname, '../public/language/', code, key), { encoding: 'utf-8' }, function(err, data) { + // If language file in core cannot be read, then no language file present + try { + languageData = JSON.parse(data) || {}; + } catch (e) { + languageData = {}; + } + + if (plugins.customLanguages.hasOwnProperty(combined)) { + _.extendOwn(languageData, plugins.customLanguages[combined]); + } + + Languages._cache.set(combined, languageData); + callback(null, languageData); + }); +}; Languages.list = function(callback) { var languagesPath = path.join(__dirname, '../public/language'), diff --git a/src/meta.js b/src/meta.js index ffc85c98bf..eb5816fdaf 100644 --- a/src/meta.js +++ b/src/meta.js @@ -8,6 +8,7 @@ var async = require('async'), user = require('./user'), groups = require('./groups'), + languages = require('./languages'), emitter = require('./emitter'), pubsub = require('./pubsub'), auth = require('./routes/authentication'), @@ -65,6 +66,7 @@ var async = require('async'), async.apply(Meta.js.minify, 'nodebb.min.js'), async.apply(Meta.js.minify, 'acp.min.js'), async.apply(Meta.sounds.init), + async.apply(languages.init), async.apply(Meta.templates.compile), async.apply(auth.reloadRoutes), function(next) { diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 9068fdb9a9..342ecdc38f 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -16,6 +16,7 @@ var app, toobusy = require('toobusy-js'), plugins = require('../plugins'), + languages = require('../languages'), meta = require('../meta'), user = require('../user'), groups = require('../groups'), @@ -314,6 +315,19 @@ middleware.applyBlacklist = function(req, res, next) { }; middleware.processLanguages = function(req, res, next) { + var code = req.params.code; + var key = req.path.match(/[\w]+\.json/); + + if (code && key) { + languages.get(code, key[0], function(err, language) { + res.status(200).json(language); + }) + } else { + res.status(404).json('{}'); + } +}; + +middleware.processTimeagoLocales = function(req, res, next) { var fallback = req.path.indexOf('-short') === -1 ? 'jquery.timeago.en.js' : 'jquery.timeago.en-short.js', localPath = path.join(__dirname, '../../public/vendor/jquery/timeago/locales', req.path), exists; diff --git a/src/plugins.js b/src/plugins.js index 853c6f44f2..1045bab8c6 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -31,7 +31,7 @@ var middleware; Plugins.lessFiles = []; Plugins.clientScripts = []; Plugins.acpScripts = []; - Plugins.customLanguages = []; + Plugins.customLanguages = {}; Plugins.customLanguageFallbacks = {}; Plugins.libraryPaths = []; Plugins.versionWarning = []; @@ -85,10 +85,10 @@ var middleware; Plugins.acpScripts.length = 0; Plugins.libraryPaths.length = 0; - Plugins.registerHook('core', { - hook: 'static:app.load', - method: addLanguages - }); + // Plugins.registerHook('core', { + // hook: 'static:app.load', + // method: addLanguages + // }); async.waterfall([ function(next) { @@ -415,27 +415,28 @@ var middleware; ], next); }; - function addLanguages(params, callback) { - Plugins.customLanguages.forEach(function(lang) { - params.router.get('/language' + lang.route, function(req, res, next) { - res.json(lang.file); - }); - - var components = lang.route.split('/'), - language = components[1], - filename = components[2].replace('.json', ''); - - translator.addTranslation(language, filename, lang.file); - }); - - for(var resource in Plugins.customLanguageFallbacks) { - params.router.get('/language/:lang/' + resource + '.json', function(req, res, next) { - winston.verbose('[translator] No resource file found for ' + req.params.lang + '/' + path.basename(req.path, '.json') + ', using provided fallback language file'); - res.sendFile(Plugins.customLanguageFallbacks[path.basename(req.path, '.json')]); - }); - } - - callback(null); - } + // function addLanguages(params, callback) { + // Plugins.customLanguages.forEach(function(lang) { + // console.log('route for', '/language/' + lang.route); + // params.router.get('/language' + lang.route, function(req, res, next) { + // res.json(lang.file); + // }); + + // var components = lang.route.split('/'), + // language = components[1], + // filename = components[2].replace('.json', ''); + + // translator.addTranslation(language, filename, lang.file); + // }); + + // for(var resource in Plugins.customLanguageFallbacks) { + // params.router.get('/language/:lang/' + resource + '.json', function(req, res, next) { + // winston.verbose('[translator] No resource file found for ' + req.params.lang + '/' + path.basename(req.path, '.json') + ', using provided fallback language file'); + // res.sendFile(Plugins.customLanguageFallbacks[path.basename(req.path, '.json')]); + // }); + // } + + // callback(null); + // } }(exports)); diff --git a/src/plugins/load.js b/src/plugins/load.js index 48bbdb0390..58dc244fbd 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -219,26 +219,23 @@ module.exports = function(Plugins) { fallbackMap = {}; utils.walk(pathToFolder, function(err, languages) { - var arr = []; - async.each(languages, function(pathToLang, next) { fs.readFile(pathToLang, function(err, file) { if (err) { return next(err); } - var json; + var data; + var route = pathToLang.replace(pathToFolder + '/', ''); try { - json = JSON.parse(file.toString()); + data = JSON.parse(file.toString()); } catch (err) { winston.error('[plugins] Unable to parse custom language file: ' + pathToLang + '\r\n' + err.stack); return next(err); } - arr.push({ - file: json, - route: pathToLang.replace(pathToFolder, '') - }); + Plugins.customLanguages[route] = Plugins.customLanguages[route] || {}; + _.extendOwn(Plugins.customLanguages[route], data); if (pluginData.defaultLang && pathToLang.endsWith(pluginData.defaultLang + '/' + path.basename(pathToLang))) { fallbackMap[path.basename(pathToLang, '.json')] = path.join(pathToFolder, pluginData.defaultLang, path.basename(pathToLang)); @@ -251,7 +248,7 @@ module.exports = function(Plugins) { return callback(err); } - Plugins.customLanguages = Plugins.customLanguages.concat(arr); + // do I need this either? _.extendOwn(Plugins.customLanguageFallbacks, fallbackMap); callback(); diff --git a/src/routes/index.js b/src/routes/index.js index c5d2d25178..626b480db1 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -144,10 +144,11 @@ module.exports = function(app, middleware, hotswapIds) { } app.use(middleware.privateUploads); + app.use('/language/:code', middleware.processLanguages); app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), { maxAge: app.enabled('cache') ? 5184000000 : 0 })); - app.use('/vendor/jquery/timeago/locales', middleware.processLanguages); + app.use('/vendor/jquery/timeago/locales', middleware.processTimeagoLocales); app.use(controllers.handle404); app.use(controllers.handleErrors); diff --git a/src/webserver.js b/src/webserver.js index 78808d5371..48d8167d3b 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -12,6 +12,7 @@ var path = require('path'), emailer = require('./emailer'), meta = require('./meta'), + languages = require('./languages'), logger = require('./logger'), plugins = require('./plugins'), middleware = require('./middleware'), @@ -92,6 +93,7 @@ function initializeNodeBB(callback) { async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, 'acp.min.js'), async.apply(meta.css.minify), async.apply(meta.sounds.init), + async.apply(languages.init), async.apply(meta.blacklist.load) ], next); },