Node.js internationalization and form validation
In any application, no matter the languages or the environment you are using, it is always necessary to ensure that all data is validated before we try any operation. It's tedious, boring and maybe you prefer to focus on more interesting things but you may fear not only hackers, but also the users who from time to time become true hackers without been really aware of it: just applying unexpected input. For instance a form field could ask for the zip code and a regular user could answer (sincerely) 'I don't know'. And he is being honest but at the same time he is creating an exception and blowing out your super-duper node server.
In addition to that and in order to be nice with a wider range of users you have to offer internationalization support. Once again, a boring but a mandatory task to take into account when you are dealing with the views. In this post I'll show one approach to solve these issues node.js trying to keep the code simple. Let's start with i18n.
Internationalization in Node.js
For Node.js and in general for any javascript based software we have then i18next package. It comes with all you need to write multilingual applications, many available options for the language file format and many niceties as well. i18next can be used in both server and client side javascript. In this case I'll show just the translation files format, how to init i18next and how to apply in jade templates. In the event that you're using Angular (or maybe something not so mainstream :P) framework in the frontend, it is possible to reuse the work done in server side.
Language files
In the root of your project, just place a directory called locales
and inside it one directory for each language you give support to (locales/en, locales/es), including locales/dev/
which is the lang by default. And inside each directory, place a json file called translation.json
. So we are using json format, but there are other formats available, and many other options including namespaces support.
This is how the translation.json file looks like. Note that you can organize translations in groups
Applying i18next
In our main program, app.js, we will have to require, init and apply i18next to our express application to make it available for jade templates. This is app.js now, some init alternatives are commented. The loading of the translation files is done asynchronously.
This is how we apply i18n in a jade template using function t(key), where key must have an entry in the translation files.
Simple form validation
As simple as using validation facilities from the mongoose driver. There are another options but in this case I wanted to make use of something that I already have instead of trying other packages. Check these modules if you need something else or if you have forms that have nothing to do with an underlying database. Anyway, if we are using mongoose, we'll have to create validations.
MongoDB is a schemaless document-oriented database and it can handle anything: each row in a collection could be completely different. However, Mongoose driver is much more than a simple way to access a database: it forces you to define schemas to map each collection in database, where at least you have to detail fields and data types. In a way, that is a first defense line. In those schema definitions (which deserve another long post devoted to mongoose exclusively) is where we can apply validations (built-in and custom), limits and even define custom error messages! Here I'm improving the schema for the guestbook collection.
Internationalization in mongoose validation messages
I'm applying i18n in the mongoose schema in a ... cryptic way. There is a problem if we try to use the i18next.t(key) function to apply messages in schemas. In the main app.js we are loading express routes. Routes are requiring schemas and as i18next are not loaded synchronously (and init options for that are not working as expected) so those t(key) calls in mongoose models will fail. We could refactor and reshape the main app.js, we could maybe use packages to force synchronous behaviour, but It is much more easier to use the key from translation files, which are simple strings.
This is the guestbook.jade template, now with some error handling conditionals to show error messages. Mongoose stores all validation information in an err object which we simply pass to this jade template to be displayed.
And finally the router where we get validation data from mongoose and we move to the template to be rendered.
Once again I tried to keep main app.js simple. There may be many other (and better) recipes because Node.js is like a bazaar with thousands of very active contributors. Greets to any.