var i18n = require('restify-i18n'),
restify = require('restify'),
cookies = require('restify-cookies'),
mongoose = require(cwd + '/api/database/mongo')(config),
server = restify.createServer();
i18n.set('directory', '/api/i18n/');
server.use(cookies.parse);
server.use(i18n.locale);
require('./routes/example')(server, mongoose);
server.listen(8000);
restify-cookies is required as a separate install at the API level and before i18n is injected as middleware. res.setCookie
needs to be exposed before i18n.locale runs.
Assuming scaffolding above (using mongoose as an example):
// routes/example.js
module.exports = function(server, mongoose) {
var i18n,
cwd = process.cwd(),
controller = require(cwd + '/api/controller/user')(mongoose);
i18n = function(req, res, next) {
if (!!req.locale.lang) {
controller.user.i18 = {
error : require(cwd + req.locale.directory + req.locale.lang + '/user/error')
};
}
next();
}
server.get('/me', i18n, function(req, res, next) {
controller.find(req, res, next);
});
server.post('/register', i18n, function(req, res, next) {
controller.register(req, res, next);
});
}
// controller/user.js
var User = function(mongoose) {
var cwd = process.cwd();
this.user = require(cwd + '/api/models/user')(mongoose);
this.model = mongoose.model(this.user.name, this.user.schema);
};
User.prototype.register = function(req, res, next) {
var self = this;
var user = new this.model({
username : req.params.username,
email : req.params.email,
facebook : {
id : req.params.facebook.id,
token : req.params.facebook.token
},
name : {
first : req.params.name.first,
last : req.params.name.last
},
password : req.params.password,
phone : req.params.phone
});
user.save(function(err, user) {
if (err) {
if (!err.hasOwnProperty('errors')) return next(err);
return next(err.errors[Object.keys(err.errors)[0]]);
}
res.send(user.id);
next();
});
}
User.prototype.find = function(req, res, next) {
var self = this,
username = req.params.username;
if (!username) return next(new Error(this.user.i18.error.username));
this.model.findByUsername(username, function(err, user) {
if (err) return next(err);
else if (!user.length) return next(new Error(self.user.i18.error.none));
res.send(user);
next();
});
}
module.exports = function(mongoose) {
return new User(mongoose);
};
// model/user.js
var Model = function(mongoose) {
var self = this,
cwd = process.cwd(),
config = require(cwd + '/api/config');
this.name = 'User';
this.i18 = { error : require(cwd + '/api/i18n/en-US/user/error') };
this.schema = new mongoose.Schema({
username : { type: String, required: true, index: { unique: true } },
email : { type: String, required: true, index: { unique: true }, lowercase: true, validate: [self.validate.email, self.i18.error.email] },
facebook : {
id : { type: String, required: true, index: { unique: true } },
token : { type: String, required: true },
},
name : {
first : { type: String, required: true, lowercase: true },
last : { type: String, required: true, lowercase: true }
},
password : { type: String, required: true, validate: [self.validate.password, self.i18.error.password] },
phone : { type: String, required: true, trim: true, minlength: 7, maxlength: 11, validate: [self.validate.phone, self.i18.error.phone] }
});
}
Model.prototype.validate = {
email : function(value) {
return /^([\w\W]+)(\@)([\w]+)(.\w+)$/.test(value);
},
password : function(value) {
var valid = true,
validation = {
specialcharacter : value.match(/[\W]/g),
uppercase : value.match(/[A-Z]/g),
digit : value.match(/[\d]/g)
};
for (var prop in validation) {
if (!validation.hasOwnProperty(prop) || (!!validation[prop] && validation[prop].length >= 2)) continue;
valid = false;
break;
}
return valid;
},
phone : function(value) {
return /^\d{10}$/.test(value);
}
}
Model.prototype.statics = function() {
this.schema.statics.findByUsername = function(username, callback) {
return this.find({ username : username }, callback);
}
}
module.exports = function(mongoose) {
var model = new Model(mongoose);
model.statics();
return model;
}
// i18n/en-US/user/error.js
module.exports = {
email : 'Not a valid email address.',
none : 'User not found.',
password : 'Password is violating minimum requirement of documented constraints.',
phone : 'Not a valid phone number.',
username : 'Username required.'
}
- root
-- api
--- i18n
---- en-GB
----- user
------ error.js
---- en-US
----- user
------ error.js
--- server.js
package.json
Utilizing the restify use api, we are intercepting all requests looking for either a 'accept-language' cookie or header.
If either is returned and is different from the plugin default, we pragmatically overwrite the lang file for the path request used in the model.
i18n.set('default', 'en-GB');
Default : 'en-US'
Overwriting the default language will adjust flow when calculating header/cookie differentiation at runtime.
i18n.set('directory', '/api/i18n/');
Default : '/i18n/' of the node working directory
Overwriting the default language will detect wether the new directory exists at runtime. This will also affect how you load the js/json language file in the routing (shown in the example above).