Browse Source

language: accept param as well as request headers

pull/819/head
Peter Johnson 8 years ago
parent
commit
39b1367778
  1. 9
      middleware/requestLanguage.js
  2. 37
      test/ciao/autocomplete/language_querystring_invalid.coffee
  3. 37
      test/ciao/autocomplete/language_querystring_valid.coffee
  4. 37
      test/ciao/place/language_querystring_invalid.coffee
  5. 37
      test/ciao/place/language_querystring_valid.coffee
  6. 37
      test/ciao/reverse/language_querystring_invalid.coffee
  7. 37
      test/ciao/reverse/language_querystring_valid.coffee
  8. 37
      test/ciao/search/language_querystring_invalid.coffee
  9. 37
      test/ciao/search/language_querystring_valid.coffee
  10. 107
      test/unit/middleware/requestLanguage.js

9
middleware/requestLanguage.js

@ -1,10 +1,10 @@
/**
this middleware is responsible for negotiating HTTP locales for incoming
browser requests by reading 'Accept-Language' request headers.
browser requests by reading the querystring param 'lang' or 'Accept-Language' request headers.
the preferred language will then be available on the $req object:
eg. for 'Accept-Language: fr':
eg. for '?lang=fr' or 'Accept-Language: fr':
```
console.log( req.language );
@ -53,8 +53,11 @@ 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'] );
// parse request & choose best locale
var locales = new locale.Locales( req.headers['accept-language'] || '' );
var locales = new locale.Locales( input || '' );
var best = locales.best( allLocales );
// set $req.language property

37
test/ciao/autocomplete/language_querystring_invalid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/autocomplete?lang=example&text=example'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
name: 'English',
iso6391: 'en',
iso6393: 'eng',
defaulted: true
}

37
test/ciao/autocomplete/language_querystring_valid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/autocomplete?lang=fr&text=example'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
defaulted: false,
iso6391: 'fr',
iso6393: 'fra',
name: 'French'
}

37
test/ciao/place/language_querystring_invalid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/place?lang=example&ids=geonames:venue:1'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
name: 'English',
iso6391: 'en',
iso6393: 'eng',
defaulted: true
}

37
test/ciao/place/language_querystring_valid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/place?lang=fr&ids=geonames:venue:1'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
defaulted: false,
iso6391: 'fr',
iso6393: 'fra',
name: 'French'
}

37
test/ciao/reverse/language_querystring_invalid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/reverse?lang=example&point.lat=1&point.lon=2'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
name: 'English',
iso6391: 'en',
iso6393: 'eng',
defaulted: true
}

37
test/ciao/reverse/language_querystring_valid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/reverse?lang=fr&point.lat=1&point.lon=2'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
defaulted: false,
iso6391: 'fr',
iso6393: 'fra',
name: 'French'
}

37
test/ciao/search/language_querystring_invalid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/search?lang=example&text=example'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
name: 'English',
iso6391: 'en',
iso6393: 'eng',
defaulted: true
}

37
test/ciao/search/language_querystring_valid.coffee

@ -0,0 +1,37 @@
#> language
path: '/v1/search?lang=fr&text=example'
#? 200 ok
response.statusCode.should.be.equal 200
response.should.have.header 'charset', 'utf8'
response.should.have.header 'content-type', 'application/json; charset=utf-8'
#? valid geocoding block
should.exist json.geocoding
should.exist json.geocoding.version
should.exist json.geocoding.attribution
should.exist json.geocoding.query
should.exist json.geocoding.engine
should.exist json.geocoding.engine.name
should.exist json.geocoding.engine.author
should.exist json.geocoding.engine.version
should.exist json.geocoding.timestamp
#? valid geojson
json.type.should.be.equal 'FeatureCollection'
json.features.should.be.instanceof Array
#? expected errors
should.not.exist json.geocoding.errors
#? expected warnings
should.not.exist json.geocoding.warnings
#? language
json.geocoding.query['lang'].should.eql {
defaulted: false,
iso6391: 'fr',
iso6393: 'fra',
name: 'French'
}

107
test/unit/middleware/requestLanguage.js

@ -16,7 +16,7 @@ var DEFAULTS = {
module.exports.tests.defaults = function(test, common) {
test('default language', function(t) {
var req = { headers: {} };
var req = {};
middleware(req, {}, function () {
t.deepEqual( req.language, DEFAULTS, '$req.language set' );
@ -40,6 +40,25 @@ module.exports.tests.invalid = function(test, common) {
'accept-language': 'invalid language'
}};
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.end();
});
});
test('query: invalid language', function(t) {
var req = { query: {
lang: 'invalid language'
}};
middleware(req, {}, function () {
t.deepEqual( req.language, DEFAULTS, '$req.language set' );
@ -86,6 +105,36 @@ module.exports.tests.valid = function(test, common) {
t.end();
});
});
test('query: valid language - french', function(t) {
var req = { query: {
lang: 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'
}};
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.end();
});
});
test('headers: valid language - english', function(t) {
@ -109,6 +158,62 @@ module.exports.tests.valid = function(test, common) {
t.end();
});
});
test('query: valid language - english', function(t) {
var req = { query: {
lang: 'en'
}};
var expected = {
defaulted: false,
iso6391: 'en',
iso6392B: 'eng',
iso6392T: 'eng',
iso6393: 'eng',
name: 'English',
scope: 'individual',
type: 'living'
};
middleware(req, {}, function () {
t.deepEqual( req.language, expected, '$req.language set' );
t.end();
});
});
};
module.exports.tests.precedence = function(test, common) {
test('precedence: query has precedence over headers', function(t) {
var req = {
headers: { 'accept-language': 'fr' },
query: { 'lang': 'es' }
};
var expected = {
defaulted: false,
iso6391: 'es',
iso6392B: 'spa',
iso6392T: 'spa',
iso6393: 'spa',
name: 'Spanish',
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.end();
});
});
};
module.exports.all = function (tape, common) {

Loading…
Cancel
Save