localization nodejs (i18n) - node.js

My code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'en',
directory: __dirname + '/locales',
cookiename: 'locale'
});
app.configure(function () {
app.use(i18n.init);
})
app.get('/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
res.redirect('/');
});
The problems are two:
In template does not work output through
__("Name Key")
When the transfer is not directly through value, do not change text. There are all languages files
res.render('index', {name: res.__('name') });
But do not switch languages​​
/* ----- */
The resulting code:
var i18n = require("i18n");
i18n.configure({
locales: ['en', 'ru'],
defaultLocale: 'ru',
directory: __dirname + '/locales'//,
cookiename: 'locale'
});
app.use(function (req, res, next) {
res.locals.__ = res.__ = function() {
return i18n.__.apply(req, arguments);
};
next();
});
app.get('/i18n/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
i18n.setLocale(req.params.locale);
if (req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
});
This works

In your templates, assuming that you are using JADE you must embrace with #{}, like #{__("Your key")}
I didn't understand your second question, mind rephrasing?
Anyways, if you wanna use i18n from a controller, you must do: res.render('index', {name: res.i18n.__('name') });
When switching the language you must:
1- save user preference anywhere (session or cookies, for example)
app.get("/i18n/:locale", setLocale);
function setLocale(req, res, next){
req.session.locale = req.params.locale;
if(req.headers.referer) res.redirect(req.headers.referer);
else res.redirect("/");
}
2- re-apply this change at every request (simply use a middleware):
var app = require("express")();
app.use(function(req, res, next){
if(req.session.locale) //check if user has changed i18n settings
req.i18n.setLocale(req.session.locale);
})
app.get("/", function(req, res, next){
res.render('index', {name: res.i18n.__('name') });
});
app.listen(8000);

Based on the answer from #renatoargh, I had to make some modifications, but here is the final block that seemed to get things working for me.
// configure i18n
i18n.configure({
locales : [
'en',
'zh'
],
directory : __dirname + '/locales'
});
// configure app
app.configure(function () {
// initialize session support
app.use(express.cookieParser());
app.use(express.cookieSession({secret: uuid.v4()}));
// initialize i18n
app.use(i18n.init);
// set locale (on every request), if session locale exists
// otherwise use default browser setting
app.use(function (req, res, next) {
// check if user has changed i18n settings
if (req.session.locale) {
i18n.setLocale(req, req.session.locale);
}
next();
});
});
// allow MANUAL locale selection
app.get("/i18n/:locale", function (req, res) {
req.session.locale = req.params.locale;
// go back to referrer OR root (/)
res.redirect('back');
});
NOTE in the the configuration, I've set the directory. For some reason this was necessary, even though the docs state that its the default.
Also note that I'm using secret: uuid.v4(), but you can just hard-code this value for persistence across server restarts.

Related

nodejs i18n __ is not a function issue with ejs, express

I want to implement multi-language in my express (nodejs)
However I cannot understand why my ejs do not understand "__" underscore.
app.js
var i18n = require('./i18n');
app.use(i18n);
i18n.js
var i18n = require('i18n');
i18n.configure({
locales:['fr', 'en'],
directory: __dirname + '/locales',
defaultLocale: 'en',
cookie: 'lang'
});
module.exports = function(req, res, next) {
i18n.init(req, res);
res.locals.__ = res.__;
var current_locale = i18n.getLocale();
return next();
};
router.js
console.log(res.__('hello')); // print ok
console.log(res.__('helloWithHTML')); // print ok
req.app.render('index', context, function(err, html) {
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.end(html);
});
/locales/en.json
{
"hello": "Hello.",
"helloWithHTML": "helloWithHTML."
}
index.ejs
<%= __("hello")%>
I got an error message for this:
__ is not defined at eval (eval at compile (/home/nodejs/node_modules/ejs/lib/ejs.js:618:12), :41:7) at returnedFn
However I can see the log message from router:
console.log(res.__('hello')); // print out Hello
console.log(res.__('helloWithHTML')); // print out helloWithHTML
It works fine, I can see both hello and helloWithHTML values.
But ejs do not recognize i18n variable at all.
How can I resolve my issue?
From docs:
In general i18n has to be attached to the response object to let it's public api get accessible in your templates and methods. As of 0.4.0 i18n tries to do so internally via i18n.init, as if you were doing it in app.configure on your own
So, the simplest way you could use is:
// i18nHelper.js file <-- you may want to rename the file so it's different from node_modules file
var i18n = require('i18n');
i18n.configure({
locales:['fr', 'en'],
directory: __dirname + '/locales',
defaultLocale: 'en',
cookie: 'lang'
});
module.export = i18n
// app.js
const i18n = require('./i18nHelper.js');
app.use(i18n.init);
Or if you really want to bind (on your own):
// somei18n.js
module.exports = function(req, res, next) {
res.locals.__ = i18n.__;
return next();
};
// app.js
const i18nHelper = require('./somei18n.js')
app.use(i18nHelper);
app.get('/somepath', (req, res) => {
res.render('index');
})
Here is my solution.
i18n.js
var i18n = require('i18n');
i18n.configure({
locales: ['zh_CN', 'en'],
directory: __dirname+'/locales'
});
module.exports = function(req, res, next) {
let {lang} = req.query;
i18n.init(req, res);
lang = lang ? lang : 'zh_CN';
i18n.setLocale(req, lang);
return next();
};
Other files are the same with you.
Hope to be helpful.

Path in node with parameter passed is not found

I am trying to get a page with express-router when inside of the url :id is supplied as a parameter using passport custom callback and the url is somehow not found. Here is what I have in the main file:
var app = express();
var index = require('./routes/index');
app.post('/login', function(req, res, next) {
passport.authenticate('local-signup', function(err, user, info) {
console.log('passport callback');
console.log(err);
console.log(info);
if (err) { return next(err); }
if (!user) { return res.status(401).json(info); }
req.logIn(user, function(err) {
console.log('logIn function of /login path');
if (err) { return next(err); }
return res.redirect('/auth/' + user.local.username + '/');
});
})(req, res, next);
console.log('end of login function');
});
app.use('/auth/:id/', passport.authenticate(['facebook-token', 'local-signup']), index);
And inside the index I have:
router.get('/auth/:id/', function(req, res) {
console.log("router of index.js is sending app.html");
var appPath = path.join(__dirname, '..', 'public', 'app.html');
res.sendFile(appPath);
});
I see the redirect to /auth/nik1989/ happening, but the url is not found.
Reading from Express 4.x API - Router
// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function(req, res, next) {
// ..
});
// only requests to /calendar/* will be sent to our "router"
app.use('/calendar', router);
Following the example to call events end point you have to do /calendar/events.
Now in your case the way you inject the middleware clearly wrong
router.get('/auth/:id/', function(req, res) {
console.log("router of index.js is sending app.html");
var appPath = path.join(__dirname, '..', 'public', 'app.html');
res.sendFile(appPath);
});
app.use('/auth/:id/', passport.authenticate(['facebook-token', 'local-signup']), index);
cause the url that you have to call is /auth/:id/auth/:id/.
If you breakdown above code what you are doing is somethink like this
app.use('/auth/:id/',passport..., router.get('/auth/:id/'...)
There are many ways to fix it I have put some examples below.
Examples
Working example one
router.get('/:id', function(req, res) {
console.log("router of index.js is sending app.html");
var appPath = path.join(__dirname, '..', 'public', 'app.html');
res.sendFile(appPath);
});
app.use('/auth', passport.authenticate(['facebook-token', 'local-signup']), index);
Working example two
router.get('/auth/:id', function(req, res) {
console.log("router of index.js is sending app.html");
var appPath = path.join(__dirname, '..', 'public', 'app.html');
res.sendFile(appPath);
});
app.use(passport.authenticate(['facebook-token', 'local-signup']), index);
This should the cause
app.use('/auth/:id/', passport.authenticate(['facebook-token', 'local-
signup']), index);
you already have a route in your index.js file thats has the same url structure
/auth/:id/
when you use app.use('/account', accoutRouter);
it means every other route called by his app .use middleware will have
account/ prepend to it e.g account/login, account/register
i didn't see passport initialization in your code
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
but i believe using
router.get('/auth/:id/', function(req, res) {
console.log("router of index.js is sending app.html");
var appPath = path.join(__dirname, '..', 'public', 'app.html');
res.sendFile(appPath);
});
and
app.use('/auth/:id/', passport.authenticate(['facebook-token', 'local-
signup']), index);
will definitely cause errors

Route callbacks not executing but middleware is

So this project was working fine before today, but after a bit of a nooby mistake with git I broke my project, and was unable to recover the commit. After spending some time getting everything fixed, my routes are now broken. My issue is that when calling my API routes now the server hangs for exactly 1 minute, then times out and logs a 404 on the server.
To give some background I'm using this boilerplate. In my debugging process I basically put console.logs everywhere I possibly could, and it looks like all my initialization middleware for express and passport are working fine, and my code is getting to where the routes are defined with no errors.
Middleware with app.use works and all checks out when a request is made, and all console.logs I've put there show fine. The console.logs only stop appearing in the final route definition like in this example:
app.get('/route', function(req, res, next) {
console.log('this never shows');
next();
}, function(req, res, next) {
console.log('this never shows');
})
My actual routes do have a res.send(), this is just an example to show you. All the console.logs I put in the middleware before this route show when the request is made, so it is hanging somewhere in app here.
It's a rather large project, so if you want specific code examples just ask and I'll post it. But I was able to recover all the important files that I had saved somewhere else and I'm pretty sure all my code is back to how it was before now.
edit:
Here is my file with express middleware definitions:
config/express.js
/**
* Module dependencies.
*/
var express = require('express');
var MongoStore = require('connect-mongo')(express);
var flash = require('connect-flash');
var helpers = require('view-helpers');
var swig = require('swig');
var session = require('express-session');
module.exports = function (app, config, passport) {
app.set('showStackError', true);
// should be placed before express.static
app.use(express.compress({
filter: function (req, res) {
return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
},
level: 9
}));
app.use(express.favicon());
app.use(express.static(config.root + '/public'));
app.use('/uploads', express.static(config.root + '/uploads'));
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.status(204).end();
}
else {
next();
}
};
app.use(allowCrossDomain);
// don't use logger for test env
if (process.env.NODE_ENV !== 'test') {
app.use(express.logger('dev'));
}
// set views path, template engine and default layout
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', config.root + '/app/views');
app.set('view cache', process.env.NODE_ENV !== 'development');
app.configure(function () {
// dynamic helpers
// app.use(function(req,res,next){
// req.locals.session = "eeeeeeee";
// next();
// });
// cookieParser should be above session
app.use(express.cookieParser());
// bodyParser should be above methodOverride
app.use(express.bodyParser());
app.use(express.methodOverride());
// express/mongo session storage
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
session({
secret: 'secrettexthere',
saveUninitialized: true,
resave: true,
store: new MongoStore({
url: 'mongodb://localhost/traderdb',
db: 'traderdb',
collection: 'sessions',
auto_reconnect: true
})
})(req, res, next);
}
});
// connect flash for flash messages
app.use(flash());
app.use(function (req, res, next) {
res.locals.session = req.session;
res.locals.req = req;
next();
});
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.initialize()(req, res, next);
}
});
//app.use(helpers('app name'));
//
// use passport session
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.session()(req, res, next);
}
});
// routes should be at the last
app.use(app.router);
// assume "not found" in the error msgs
// is a 404. this is somewhat silly, but
// valid, you can do whatever you like, set
// properties, use instanceof etc.
app.use(function(err, req, res, next) {
// treat as 404
if (~err.message.indexOf('not found')) return next();
// log it
console.error(err.stack);
// error page
res.status(500).render('500', { error: err.stack });
});
// assume 404 since no middleware responded
app.use(function(req, res, next) {
res.status(404).render('404', { url: req.originalUrl, error: 'Not found' })
});
})
}
I also have another file with passport route definitions if you'd like to see that too, but all that is tested and works okay too.
edit 2:
This is my entry point file:
server.js
/**
* Module dependencies.
*/
var express = require('express')
, fs = require('fs')
, passport = require('passport');
/**
* Main application entry file.
* Please note that the order of loading is important.
*/
// Load configurations
// if test env, load example file
var env = process.env.NODE_ENV || 'development'
, config = require('./config/config')[env]
, auth = require('./config/middlewares/authorization')
, mongoose = require('mongoose');
// Bootstrap db connection
mongoose.connect(config.db);
// Bootstrap models
var models_path = __dirname + '/app/models'
fs.readdirSync(models_path).forEach(function (file) {
require(models_path+'/'+file);
});
// bootstrap passport config
require('./config/passport')(passport, config);
var app = express();
// express settings
require('./config/express')(app, config, passport);
// Bootstrap routes
require('./config/routes')(app, passport, auth);
// Start the app by listening on <port>
var port = 3002;
app.listen(port);
console.log('Express app started on port '+port);
// expose app
exports = module.exports = app;
edit 3:
Here are my route definitions:
config/routes.js
var express = require('express');
var path = require('path');
var fileManager = require('express-file-manager');
var mongoose = require('mongoose');
var Session = mongoose.model('Session');
module.exports = function (app, passport, auth) {
var users = require('../app/controllers/users');
var coupons = require('../app/controllers/coupons');
var magazines = require('../app/controllers/magazines');
var zones = require('../app/controllers/zones');
var transactions = require('../app/controllers/transactions');
var favorites = require('../app/controllers/favorites');
var banners = require('../app/controllers/banners');
var reports = require('../app/controllers/reports');
var coverContest = require('../app/controllers/coverContest');
var contactMessage = require('../app/controllers/contactMessage');
app.post('/api/users/login', users.login);
app.post('/api/users/register', users.register);
app.post('/api/users/logout', users.logout);
app.post('/api/users/sendResetEmail', users.sendResetEmail);
app.post('/api/users/changePassword', users.changePassword);
app.post('/api/users/redeemCoupon', isValidAppUser(), users.redeemCoupon);
app.get('/api/users/validate', isLoggedIn(0), function(req, res) {
res.send(req.user);
});
app.post('/api/coupons', coupons.get);
app.post('/api/coupons/import', isLoggedIn(0), coupons.import);
app.post('/api/coupons/remove', isLoggedIn(0), coupons.remove);
app.post('/api/coupons/upload', isLoggedIn(0), coupons.upload);
app.post('/api/transactions', transactions.get);
app.post('/api/allTransactions', isLoggedIn(0), transactions.getAll);
app.post('/api/magazines', magazines.get);
app.post('/api/magazines/import', isLoggedIn(0), magazines.import);
app.post('/api/magazines/remove', isLoggedIn(0), magazines.remove);
app.post('/api/banners', banners.get);
app.post('/api/banners/import', isLoggedIn(0), banners.import);
app.post('/api/banners/remove', isLoggedIn(0), banners.remove);
app.post('/api/favorites', isValidAppUser(), favorites.get);
app.post('/api/favorites/import', isValidAppUser(), favorites.import);
app.post('/api/zones', zones.get);
app.post('/api/zones/add', zones.add);
app.post('/api/zones/addCoupon', zones.addCoupon);
app.post('/api/zones/addMagazine', zones.addMagazine);
app.post('/api/mail/ccSubmit', coverContest.ccSubmit);
app.post('/api/mail/contactSubmit', contactMessage.contactSubmit);
//app.get('/api/reports/siteUsers', reports.siteUsers);
app.get('/auth/facebook', passport.authenticate('facebook', { scope: [ 'email', 'user_about_me'], failureRedirect: '/login' }), users.signin);
app.get('/auth/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/github', passport.authenticate('github', { failureRedirect: '/login' }), users.signin);
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/twitter', passport.authenticate('twitter', { failureRedirect: '/login' }), users.signin);
app.get('/auth/twitter/callback', passport.authenticate('twitter', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/', successRedirect: '/main.html' }));
}
function isLoggedIn(secLvl) {
return function(req, res, next) {
if(req.isAuthenticated() && req.user.secLvl <= secLvl && req.user.google.email.includes('#bizpub36.com')) {
return next();
}
res.redirect('https://accounts.google.com/logout');
}
}
function isValidAppUser() {
return function(req, res, next) {
Session.findOne({ sess_id: req.body.sess_id }).exec(function(err, session) {
if(!err && session) {
next();
}
else {
res.end({ status: 'error', message: 'invalid session' });
}
});
}
}
If app.use works, my guess would be your protocol, is the app.get correct? You issue is otherwise located somewhere else in your code base as your sample runs fine as a single route express app.
It sounds like one of three things:
Somewhere in your middleware chain, you are not calling next() to allow it to advance to the next level of handlers and thus the request just eventually times out waiting for that middleware to finish (this seems to match the symptoms you describe).
Somehow, your app.get() doesn't actually match the route you expect it to or is not specified correctly.
You're using a router, but have not configured it correctly.
But, because you don't get an immediate 404, but rather it times out, it is probably option #1 above.

Node js permanently locale changes

I'm using Expressjs and i18n for my node js application to manage the multilanguage.
Here is my i18n configuration :
i18n.js
var i18n = require('i18n');
i18n.configure({
locales:['en', 'fr'],
directory: __dirname + '/../locales',
defaultLocale: 'en',
cookie: 'lang',
});
module.exports = function(req, res, next) {
i18n.init(req, res);
res.locals.__= res.__;
var current_locale = i18n.getLocale();
return next();
};
server.js
var i18n = require('./configs/i18n');
...
app.use(i18n);
Actually if I want to change the locale settings I have to do that for each routes :
app.get('/index', function(req, res){
res.setLocale('fr')
res.render('pages/index');
});
It is possible to use setLocale() once and it will permanently change the locale ?
And what is the best practice ? Should I specify the language inside my routes everytime ? E.g :
app.get('/:locale/index', function(req, res){
res.setLocale(req.params.locale)
res.render('pages/index');
});
app.get('/:locale/anotherroute', function(req, res){
res.setLocale(req.params.locale)
res.render('pages/anotherroute');
});
Or I have to store the locale in my database for each user ?
You can use middlewares to avoid the repetition (place this code before your router) :
// Fixed locale
app.use(function (req, res, next) {
res.setLocale('fr');
next();
});
// Locale get by URL parameters
app.use(function (req, res, next) {
if (req.params && req.params.locale){
res.setLocale(req.params.locale);
}
next();
});
Personally, I prefer to store the local setting in database, it avoids to weighing the request with not necessary data.
Another solution is to set the language with HTTP headers Content-Language and Accept-Language and get it with req.acceptsLanguage().

req.session undefined in route

I'm building a simple site with expressjs and passportjs and I'm running into the problem that I can't access session variables in my routes.
There are a lot of threads with this topic but the solutions don't work for me. It looks like my error is somewhere else.
My app is configured like this:
app.configure(function() {
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
path: '/',
secret: 'very secret'
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
Once passport verified the twitter account it's redirected to this site:
app.get('/auth/twitter/callback',
passport.authenticate('twitter', {
failureRedirect: '/login'
}),
function(req, res) {
res.redirect('/');
console.log("populate session")
populateSession(req, res);
});
This works, as I'm seeing the "populate session" output in the console if I'm logged in.
The populateSession() looks like this:
function populateSession(req, res) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// Store data to session
req.session.twitterAccessToken = result.twitterAccessToken;
req.session.twitterAccessTokenSecret = result.twitterAccessTokenSecret;
req.session.lastfmAccountName = result.lastfmAccountName;
console.log("in session: " + req.session.lastfmAccountName)
}
})
}
"in session" is printed the right way. So the session itself works. Now my problem is that I want to have access to my session variables in routes because I want to pass them to my view templates like this:
app.get('/account', ensureAuthenticated, function(req, res) {
console.log(req.session)
console.log("lastfm nick " + req.session.lastfmAccountName)
res.render('account', {
user: req.user,
lastfmnick: req.session.lastfmAccountName
});
That's where I'm running into the problems. req.session contains all the twitter fields passport is populating it with but req.session.lastfmAccountName is undefined.
Any idea what's wrong there or is there a better way to pass variables to the view? I feel like it's not a good idea to have DB queries for the fields in all my routes if it could just be stored in the session.
Thanks!
The session will automatically be saved when the response ends, in this case by res.redirect(), which is done before the modifications are made to the session.
function(req, res) {
res.redirect('/'); // ends the response, saving the session
populateSession(req, res); // modifies the session without saving
});
Since .findOne() is asynchronous, if you revise populateSession() to take and call a callback when the find completes, you can control the order so the session is modified first:
function populateSession(req, res, next) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// ...
}
if (next) next(err);
})
}
app.get('/auth/twitter/callback',
/* ... */,
function(req, res) {
populateSession(req, res, function () {
res.redirect('/');
});
});
It also allows you to use populateSession as middleware:
app.get('/auth/twitter/callback',
/* ... */,
populateSession,
function(req, res) {
res.redirect('/');
});
app.use(lib.express.cookieParser(lib.config.cookieSecret));
app.use(lib.express.session({
secret: 'very secret'
})
}));
This two line should be consecutive and in that order.

Resources