John Smith
John Smith

Reputation: 31

Dynamic (i18n) links using expressjs and jade

I am using expressjs and i18n-node (https://github.com/mashpie/i18n-node). It's working, except for elegant linking. What I have now is this:

app.all(/^\/(\w{2}\/)+(\w*)?/, function(req, res, next) {
    var lang = req.params[0];
    var type = req.params[1];

    req.url = req.url.replace(lang, "");

    if(type !== 'javascript' && type !== 'img' && type !== 'css') {
      i18n.setLocale(lang.slice(0, 2));
    }   
    next();
});

(I want /foo as well as /en/foo to work). If the language is not specified in the url, the headers are checked, and if not, it defaults to English. By the way, my solution seems less than ideal (I have to make manual checks to see if it is not static content), so if any of you have a better solution I am all ears.

Now, my real problem is making links to internal content. If a user is here: "/en/foo", a "/bar"-link should actually be "/en/bar". I am using jade (for no particular reason other than that it was the default, again open to suggestions..)

I tried adding a helper function to Jade:

app.helpers({
  __i: i18n.__
  ,__n: i18n.__n
  ,link_to: function(link, text) {
    //TODO: how to get request here?
    // this should be defined to the absolute base path
    var baseUrl = "/";
    // only append locale if it is part of the existing url!
    var locale = i18n.getLocale();
    return '<a href="' + baseUrl + locale + '/' + link + '">' + text + '</a>';
  }
});

.. but it has many problems:

  1. How do I get the base url? So for instance, if the site is located at "http://www.example.com/foo/bar", and I link to "testLink", link_to will generate "http://www.example.com/foo/bar/testLink", instead of "/testLink" or "http://www.example.com/testLink" etc..
  2. How do I determine if the url contains a localization parameter or not? I don't want to append a localization-parameter to the link if it is unnecessary.
  3. The function itself is not great; In Jade it is called like this: := link_to("testUrl", "some link description"). I would rather do something like this: link_to(href="someUrl") div some more jade code
  4. (minor) The HTML is written directly in the JS

What is the preferred way to do this? I've been searching and searching, but unable to find a good answer..

This is actually also a general question; i.e how do you link to other internal content, like the link_to in Rails. Simply doing a(href="someAction") is not good enough, as you want it to generate absolute URLs so that the pretty URLs do not break static content links.

Thanks!

Upvotes: 3

Views: 2526

Answers (2)

Exos
Exos

Reputation: 3988

I paste code from an own project with i18n, I use a class for dicto and another class to search language by IP:

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser());
  app.use(express.session({
        secret: "sessid",
        key: 'uwsid',
        store: sessionStore
  }));
  app.use(function (req,res,next) {

      if (req.session.uid) {
          req.lang = req.session.uid.lang;
          next();
      } else if (req.cookies.lang) {
          req.lang = req.cookies.lang;
          next();
      } else {

          var alang = typeof req.headers['accept-language'] != "undefined" ? req.headers['accept-language'].substr(0,2) : null;

          var ipinfows = ipinfo.getInstance();

          ipinfows.getInfo(req.connection.remoteAddress, function (err,data) {

              if (err) {
                  req.lang = alang;
                  res.cookie('lang', alang);
              } else if (data && data.error) {
                  req.lang = alang;
                  res.cookie('lang', alang);
              } else {

                  console.log("seteando");

                  req.lang = data.lang.toLowerCase();

                  for (i in countryLangs) {
                      if (countryLangs[i].indexOf(data.lang) != -1) {
                          req.lang = i;
                      }
                  }

                  if (alang != req.lang) {
                      req.langdifference = alang;
                  }

                  res.cookie('lang', req.lang);

              }

              next();

          });
      }



  });
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

Before of the routing app.use(app.router); you can define callbacks, in this case I search the lang and define it in a cookie. After I add an dynamicHelper to include the Dicto object into the template:

app.dynamicHelpers({

  i18n: function (req,res) {
      return new i18n({lang: req.lang});
  }

});

With the lang define before that routing. (saving in req.lang), And I can use the i18n halper now from template (with jade):

form.uniForm(action="/account",method="post")
    fieldset.inlineLabels
        .ctrlHolder
            label(for="nickname") #{**i18n.getText('user:nick')**}:
            input(type="text",name="nickname",value=everyauth.user.nick)
            p.formHint

i18n object now is the same defined on the dynamic helper.

Upvotes: 2

major-mann
major-mann

Reputation: 2652

As far as I understand it, express will only call the helper function once (When the template is compiled...)

In the express guide, http://expressjs.com/guide.html, check out dynamicHelpers which should be more useful for your situation (dynamicHelpers provide request and response objects).

Upvotes: 0

Related Questions