0

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .

having 15 routes like this now:

app.get('/terms', function(req, res) {
    res.render('terms',{
...
    });
});

how it have to be set for routes like:

app.get('/es/terms', function(req, res) {
    res.render('terms',{
   ...
    });
});
  1. Should I duplicate this routes and add for example a locale for each like:

    app.get('/es/terms', function(req, res) {
        res.render('terms',{
        ...
         });
     });
    
  2. Or Should do something like:

     if cookie['lang'] && cookie['lang'] is in locales 
        // then redirect to /:lang/terms
      else
        // show default language in /terms
     if req.headers["accept-language"] && req.headers["accept-language"] 
        // then redirect to /:lang/terms
     else 
        //show default language in /terms
    
  3. Or there is another way I should approach this that follows good practices or is better respecting standards?

  4. Miro's Answer in : How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...

Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts

    var rxLocal = /^\/(de|en)/i;
    ...
    app.get(/\/(de|en)\/login/i, routes.login);

thanks in advance

MikZuit
  • 684
  • 5
  • 17
  • The normal way to manage i18n through HTTP is to use the header Accept-Language that is designed to this. Otherwise, the @patrick-hund proposal seems to be the best way to deal with the routes. You can add a middleware to do some generic process for all your routes. – Techniv Oct 17 '17 at 12:10
  • what do you mean with "generic process for all your routes" .. any specific middleware ? @Techniv – MikZuit Oct 17 '17 at 13:12
  • Enrich the `request` or `response` objects with locales data, setup locales data for rendering engine, etc. See [app.use()](http://expressjs.com/en/4x/api.html#app.use) – Techniv Oct 17 '17 at 13:18

2 Answers2

3

You need to consider 2 things :

1. How get the local :

Accept-Language

The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.

  • +Normalized
  • +Natively support by brower
  • -Not easy to by passe by the end user

Path / Cookies

You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.

You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).

Both

To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.

2. Use the local:

Each methods to get the local can be used from different way. I will use random of them in exemple but they are all compatible.

Top level configuration.

In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.

app.use('/:local' (req, res, next) => {
  let localKey = req.param('local');
  res.locals = // Some ingenious method to get the locales from localKey

  next();
}

Check res.locals and your engine documentation.

Use it in controller.

If the local is part of the contoller process. You can get directly is value in controller.

In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.

app.use((req, res, next) => {
  let local = req.cookies.local;
  if(!local) local = req.acceptsLanguages();
  if(!local) local = 'en-US';
  req.local = local;
}

Both

You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.

When you use middle where witch impact the controllers, be sure you declare them before your routes.

Techniv
  • 1,967
  • 15
  • 22
  • this looks to me like a good implementation and consideration of my needs , although let me put in practice both and i would come back to accept an aswer. Thanks @Techniv – MikZuit Oct 17 '17 at 16:19
1

You can use a route parameter to get the locale from the URL, like this:

app.get('/:lang/terms', function (req, res) {
    if (req.params === 'es') {
        res.send('¡Hola!');
    else {
        res.send('Hi!');
    }
});

The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang. See express routing documentation for details.

Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
  • I have try this before . With this I would have a route for "/es/terms" , /de/terms ... but what about when someone goes to "/terms and I want to show them (redirecting) to the specific "user agent or cookie language" they have ? ... should I just create another route for that? – MikZuit Oct 17 '17 at 13:15
  • Yes, I would redirect in that case – Patrick Hund Oct 17 '17 at 13:16
  • What do you think of Techniv's comment ? "Enrich the request or response with app.use(); " if you don't mind me asking. I'm reading about both ideas as I found both ways correct. also I would give time if someone else want to add something. – MikZuit Oct 17 '17 at 13:41
  • The choice of use middleware and what you do with depend of what you really need to do with local. If your controller take data from DB and render a template with an engine that support locales (like EJS), this controller can be local agnostic and you can use a middleware to configure the engine. If the local is used in the controller process you need to passe it to the controller. – Techniv Oct 17 '17 at 13:53
  • You can use the path method of @PatrickHund or the header method in both case. I will tried to synthesize this in an answer. – Techniv Oct 17 '17 at 13:54