ericf / express-handlebars

A Handlebars view engine for Express which doesn't suck.
BSD 3-Clause "New" or "Revised" License
2.31k stars 384 forks source link

How to add Internationalization #281

Closed averri closed 4 years ago

averri commented 4 years ago

I would like to know how to add Internationalization. A great example of i18n support is the great http://formatjs.io/handlebars.

UziTech commented 4 years ago

The handlebars config property takes a Handlebars class to use to render the views so you might be able to do the following:

const expressHandlebars = require('express-handlebars');
const Handlebars = require('handlebars');
const HandlebarsIntl = require('handlebars-intl');

// register template helpers
HandlebarsIntl.registerWith(Handlebars);

// use Handlebars class with registered helpers in express-handlebars
const exhbs = expressHandlebars.create({ handlebars: Handlebars });

app.engine('handlebars', exhbs.engine);
app.set('view engine', 'handlebars');
...

This code has not been tested but it should work.

UziTech commented 4 years ago

Any new development is done on express-handlebars/express-handlebars so if you still have issues you should create them there.

averri commented 4 years ago

Thanks @UziTech. There is still a missing step, that is how to pass the metadata to the template. See this working code for example:

const Handlebars = require('handlebars')
const HandlebarsIntl = require('handlebars-intl')

HandlebarsIntl.registerWith(Handlebars)

const context = {
  post: {
    date: 1422046290531,
    comments: ['Hi', 'Hello']
  }
}

const intl = {
  'locales': ['en-US'],
  'messages': {
    'posted': 'Posted {ago}, {num, plural, one{# comment} other{# comments}}'
  }
}

const src = `{{formatMessage (intlGet "messages.posted")
    num=post.comments.length
    ago=(formatRelative post.date)}}`

const template = Handlebars.compile(src)

const html = template(context, {
  data: {intl} // This is the missing step in express-handlebars. How to pass it?
})

console.info(html)

This works fine in standalone mode, but I'm having difficulty to integrate with nodemailer-express-handlebars which uses the express-handlebars.

In the code below, where to pass the data: {intl} which contains the i18n information?

const nodemailer = require('nodemailer')
const hbs = require('nodemailer-express-handlebars')

module.exports = app => {

  const transport = nodemailer.createTransport(app.get('mailer'))

  transport.use('compile', hbs({
    viewEngine: {
      extName: '.hbs',
      partialsDir: 'src/templates',
      layoutsDir: 'src/templates/layouts',
      defaultLayout: 'main.hbs',
    },
    viewPath: 'src/templates',
    extName: '.hbs'
  }))

  app.use('mailer', {
    async create(data) {
      // 'sendMail' returns a promise if no callback is provided.
      return transport.sendMail(data)
    }
  })

  app.service('mailer').hooks(require('./hooks'))
}
UziTech commented 4 years ago

That seems like a question for nodemailer-express-handlebars.

With express-handlebars you would add it as a third argument to the render function:

exhbs.render(filepath, context, {data: {intl}});
UziTech commented 4 years ago

or as a data property on the context object for renderView:

exhbs.renderView(viewpath, { ...context, data: {intl}}, callback);
averri commented 4 years ago

Thanks @UziTech.