node.js i18n: "ReferenceError: __ is not defined" - node.js

Throughout my app I use i18n without problem. However, for email send through a cron job, I get the error:
ReferenceError: __ is not defined
In app.js I configure i18n:
const i18n = require("i18n");
i18n.configure({
locales: ["en"],
register: global,
directory: path.join(__dirname, "locales"),
defaultLocale: "en",
objectNotation: true,
updateFiles: false,
});
app.use(i18n.init);
Throughout my app I use it as __('authentication.flashes.not-logged-in'), like I said without problems. In a mail controller, that is called upon by a cron job, I use it in the same way: __('mailers.buttons.upgrade-now'). However there, and only there, it produces the mentioned error.
Just to try, I've changed this in the mail controller to i18n.__('authentication.flashes.not-logged-in'). But then I get another error:
(node:11058) UnhandledPromiseRejectionWarning: TypeError: logWarnFn is not a function
at logWarn (/data/web/my_app/node_modules/i18n/i18n.js:1180:5)
Any idea how to make the emails work that are sent through a cron job?

In comments, the asker clarified that the cron job calls mailController.executeCrons() directly, instead of making an HTTP request to the app. Because of this, the i18n global object never gets defined, because the app setup code in app.js isn't run.
The best solution would be to use i18n's instance usage. You could separate the instantiation and configuration of an I18N object out into a separate function, then call it both in app.js to set it up as Express middleware, and in the mailController.executeCrons() function to use it when being called through the cronjob.
Code outline:
i18n.js (new file)
const i18n = require("i18n");
// factory function for centralizing config;
// either register i18n for global use in handling HTTP requests,
// or register it as `i18nObj` for local CLI use
const configureI18n = (isGlobal) => {
let i18nObj = {};
i18n.configure({
locales: ["en"],
register: isGlobal ? global : i18nObj,
directory: path.join(__dirname, "locales"),
defaultLocale: "en",
objectNotation: true,
updateFiles: false,
});
return [i18n, i18nObj];
};
module.exports = configureI18n;
app.js
const configureI18n = require('./path/to/i18n.js');
const [i18n, _] = configureI18n(true);
app.use(i18n.init);
mailController.js
const configureI18n = require('./path/to/i18n.js');
const [_, i18nObj] = configureI18n(false);
executeCrons() {
i18nObj.__('authentication.flashes.not-logged-in');
}

Related

How do you send an object to a helper in Handlebars?

Learning handlebars and Express I'm trying to learn of a way to send an object without always having to build in the render. For example if I have:
const details = {
version: process.env.npm_package_version,
author: 'foobar'
}
I can send this to my footer.hbs in partials from:
app.get('/', (req, res) => {
res.render('index', {
details
})
})
but looking for a way to send it to a template file and not always in a render I've read in the documentation about block helpers and tried:
// Define paths for Express config
const publicDir = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')
// Setup hbs engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)
hbs.registerHelper('appDetails', () => {
const details = {
version: process.env.npm_package_version,
author: 'foobar'
}
return details
})
but in my directory /partials from file footer.hbs I try to use the helper:
<footer>
<p>Created by {{details.author}} | version: {{details.version}}</p>
</footer>
and it doesn't work. I've searched the site and I've read:
How to set a variable for the main handlebars layout without passing it to every route?
nodejs + HBS (handlebars) : passing data to partials
Passing variables through handlebars partial
In my Node and Express app is there a way to send data to the partials file without having to always send it in render?
There are two ways a Handlebars Helper can add data to the rendering context of a template:
1) By directly mutating the template's context or 2) By using private variables
Example: https://codepen.io/weft_digital/pen/JjPZwvQ
The following helper updates or adds the data points name and newData to the template's global context, and it also passes a private variable, using the data option.
Handlebars.registerHelper('datainjection', function(context, options) {
this.name = "Bob";
this.newData = "Updated"
return context.fn(this, { data: {private:"pirate"} });
});
Before you call the {{#datainjection}}{{/datainjection}} block in your template, {{name}} will be set to whatever value you pass to the render function, but every occurrence of {{name}} within or after the {{#datainjection}}{{/datainjection}} block will use the updated value, which is "Bob" in this case.
The private variable we are passing can only be accessed within the {{#datainjection}}{{/datainjection}} block using the "#" decorator.
For example:
{{#datainjection}}{{#private}}{{/datainjection}}
will render the string "pirate"

How can my client get application configuration from the server when using Webpack?

I'm adding Webpack to a Node/Express app that previously used RequireJS. When the client needed some configuration from the server, we previously used a custom Express route that retrieved specific configs as JSON:
server/index.js - Set up Express routes for config files
const app = express();
const configRouter = express.Router();
configRouter.get('/some-config.json', (req, res) => {
const someConfig = {
prop1: getProp1(),
prop2: getProp2()
}
res.json(someConfig);
}
app.use('/config', configRouter);
client/controller.js - Use/config/some-config.json during initialization
define(['text!/config/some-config.json'], function(SomeConfig) {
// do something with SomeConfig
});
But removing RequireJS means I can no longer retrieve the JSON this way as a dependency. And it's not static JSON either, so it's not as simple as just placing it alongside client code and importing it.
So what is the best way to do this with Webpack? Any help greatly appreciated. Thanks!

Can I pass variable to required file?

In express, I'm trying to move my minification to a requierd file:
app.js:
var app = express();
var minify = require("./minify.js");
In that file I try to set my template engine.
minify.js:
var app = express();
app.engine('html', mustacheExpress());
Later when I try to use to use the rendering engine in app.js, I get the error that no template-engine is set. It works if I run it all in the same file. I think the problem is that I declare the app-variable twice. How can I pass the app-variable into minify.js?
The problem is that you define new app variable, and you currently instantiate brand new express instance by calling express().
What you need to do is start using functions so that you can pass params (there are other methods too, but this is one that will work for you):
// app.js
var app = express();
var minify = require('./minify'); // don't include .js!
minify(app); // CALL the function that minify.js exports, passing params
// minify.js
module.exports = function(app) {
// because app comes as a parameter, it's the very same you've created in app.js
app.engine('html', mustacheExpress());
}
Again, there are many different methods and maybe proper approaches, depending on what you want to do, but this will do the job in your case. Read more about NodeJS and it's require system.
You can pass 'app' from app.js to your minify by using function in your module like Andrey said. You can do it like this too for example :
minify.js
module.exports = {
setAppEngine : function(app) {
app.engine( [...] );
}
}
And calling it like this in your app.js:
app.js
var app = express();
var minify = require("./minify.js").setAppEngine(app);
This solution is very useful because you can set and call others methods in minify.js. For example, you can do with the same code in minify.js:
app.js
var app = express();
var minify = require("./minify.js");
minify.setAppEngine(app);

NodeJS (Express) - project structure and mongo connection

I started a new project from scratch with ExpressJS.
Everything works fine but now I begin to have a dozen of 'app.get(....)' function and I need to give the project a structure.
What I have in mind is quite simple, it should have a folder named 'routes' containing a file such as 'module1.js', with all of the app.get related to that module. (like I've seen in many examples)
The issue is how to tell Express to route 'http://url/module1/' to that route file and how to pass it a param variable, containing for instance the mongodb connection.
what I tried is :
var params = {
db: myMongoConnection
};
var mod1 = require('routes/module1');
app.use('/module1', mod1);
but now I still miss the 'params'.
If I try to pass it as an argument to the require method i get an error saying it needs middleware.
Another issue is related to the fact that the myMongoConnection is valid in the connection callback, so I think i need to require and use the route.js inside the MongoClient connect callback.
Any idea?
thanks a lot
For custom modules, create a folder, call it modules
In its index.js, expose the modules that you need.
Something like,
var mods = [
'mod1',
'mod2',
];
function init() {
var expose = {};
var params = {
db: myMongoConnection
};
mods.forEach(mods, function (mod) {
expose[mod] = require('./' + mod)(params);
});
return expose;
}
// export init
module.exports = init;
In mod1.js, wrap the params
module.exports = function(params) {
// all your functions here will have access to params.
}
Then in, server/app.js, require this and set it in the app.
app.set('mods', require('path-to/modules'));
Now, you can access all your modules, using app.get('mods').moduleName.methodname

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

Resources