Browse Source

language: fallback scenario from invalid querystring to valid header

pull/819/head
Peter Johnson 8 years ago
parent
commit
43131248b7
  1. 45
      middleware/requestLanguage.js
  2. 96
      test/unit/middleware/requestLanguage.js

45
middleware/requestLanguage.js

@ -1,4 +1,6 @@
const _ = require('lodash');
/**
this middleware is responsible for negotiating HTTP locales for incoming
browser requests by reading the querystring param 'lang' or 'Accept-Language' request headers.
@ -53,19 +55,46 @@ const allLocales = new locale.Locales( Object.keys( language ) );
// return the middleware
module.exports = function middleware( req, res, next ){
// input language, either from query param or header
var input = ( req.query && req.query.lang ) || ( req.headers && req.headers['accept-language'] );
// init an object to store clean (sanitized) input parameters if not initialized
req.clean = req.clean || {};
// init warnings array if not initialized
req.warnings = req.warnings || [];
// set defaults
var lang = language.en;
var isDefault = true;
var locales, best;
// input language via query param
if( isDefault && req.query && req.query.lang ){
locales = new locale.Locales( req.query.lang );
best = locales.best( allLocales );
if( best.defaulted ){
req.warnings.push( 'invalid language provided via querystring' );
} else {
lang = language[ best.language ];
isDefault = false;
}
}
// parse request & choose best locale
var locales = new locale.Locales( input || '' );
var best = locales.best( allLocales );
// input language via request headers
if( isDefault && req.headers && req.headers['accept-language'] ){
locales = new locale.Locales( req.headers['accept-language'] );
best = locales.best( allLocales );
if( best.defaulted ){
req.warnings.push( 'invalid language provided via header' );
} else {
lang = language[ best.language ];
isDefault = false;
}
}
// set $req.language property
req.language = language[ best.language ] || language.en;
req.language.defaulted = best.defaulted;
req.language = _.clone( lang );
req.language.defaulted = isDefault;
// set $req.clean property in order to print language info in response header
req.clean = req.clean || {};
req.clean.lang = {
name: req.language.name,
iso6391: req.language.iso6391,

96
test/unit/middleware/requestLanguage.js

@ -28,6 +28,33 @@ module.exports.tests.defaults = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
test('both querystring & header invalid', function(t) {
var req = {
headers: { 'accept-language': 'foobar' },
query: { 'lang': 'foobar' }
};
middleware(req, {}, function () {
t.deepEqual( req.language, DEFAULTS, '$req.language set' );
t.deepEqual( req.clean.lang, {
defaulted: req.language.defaulted,
iso6391: req.language.iso6391,
iso6393: req.language.iso6393,
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, [
'invalid language provided via querystring',
'invalid language provided via header'
]);
t.end();
});
});
@ -50,6 +77,10 @@ module.exports.tests.invalid = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, [
'invalid language provided via header'
]);
t.end();
});
});
@ -69,6 +100,10 @@ module.exports.tests.invalid = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, [
'invalid language provided via querystring'
]);
t.end();
});
});
@ -102,6 +137,8 @@ module.exports.tests.valid = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
@ -132,6 +169,8 @@ module.exports.tests.valid = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
@ -155,6 +194,16 @@ module.exports.tests.valid = function(test, common) {
middleware(req, {}, function () {
t.deepEqual( req.language, expected, '$req.language set' );
t.deepEqual( req.clean.lang, {
defaulted: req.language.defaulted,
iso6391: req.language.iso6391,
iso6393: req.language.iso6393,
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
@ -177,6 +226,16 @@ module.exports.tests.valid = function(test, common) {
middleware(req, {}, function () {
t.deepEqual( req.language, expected, '$req.language set' );
t.deepEqual( req.clean.lang, {
defaulted: req.language.defaulted,
iso6391: req.language.iso6391,
iso6393: req.language.iso6393,
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
@ -211,6 +270,43 @@ module.exports.tests.precedence = function(test, common) {
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, []);
t.end();
});
});
test('precedence: invalid querystring but valid header', function(t) {
var req = {
headers: { 'accept-language': 'fr' },
query: { 'lang': 'foobar' }
};
var expected = {
defaulted: false,
iso6391: 'fr',
iso6392B: 'fre',
iso6392T: 'fra',
iso6393: 'fra',
name: 'French',
scope: 'individual',
type: 'living'
};
middleware(req, {}, function () {
t.deepEqual( req.language, expected, '$req.language set' );
t.deepEqual( req.clean.lang, {
defaulted: req.language.defaulted,
iso6391: req.language.iso6391,
iso6393: req.language.iso6393,
name: req.language.name
}, '$req.clean.lang set' );
t.deepEqual( req.warnings, [
'invalid language provided via querystring'
]);
t.end();
});
});

Loading…
Cancel
Save