Browse Source

negotiate HTTP locales for incoming browser requests

pull/819/head
missinglink 8 years ago committed by Peter Johnson
parent
commit
ae2c0e7671
  1. 74
      middleware/requestLanguage.js
  2. 2
      package.json
  3. 9
      routes/v1.js

74
middleware/requestLanguage.js

@ -0,0 +1,74 @@
/**
this middleware is responsible for negotiating HTTP locales for incoming
browser requests by reading 'Accept-Language' request headers.
the preferred language will then be available on the $req object:
eg. for 'Accept-Language: fr':
```
console.log( req.language );
{
name: 'French',
type: 'living',
scope: 'individual',
iso6393: 'fra',
iso6392B: 'fre',
iso6392T: 'fra',
iso6391: 'fr',
defaulted: false
}
```
for configuration options see:
https://github.com/florrain/locale
**/
const locale = require('locale');
/**
BCP47 language tags can contain three parts:
1. A language subtag (en, zh).
2. A script subtag (Hant, Latn).
3. A region subtag (US, CN).
at time of writing we will only be concerned with 1. (the language subtag) with
the intention of being compatible with the language standard of whosonfirst data.
whosonfirst data is in ISO 639-3 format so we will need to configure the library
to support all ISO 639-1 (2 char) codes and convert them to 639-1 (3-char) codes.
see: https://github.com/whosonfirst/whosonfirst-names
**/
const iso6393 = require('iso-639-3');
// create a dictionary which maps the ISO 639-1 language subtags to a map
// of it's represenation in several different standards.
const language = {};
iso6393.filter( i => !!i.iso6391 ).forEach( i => language[ i.iso6391 ] = i );
// a pre-processed locale list of language subtags we support (all of them).
const allLocales = new locale.Locales( Object.keys( language ) );
// return the middleware
module.exports = function middleware( req, res, next ){
// parse request & choose best locale
var locales = new locale.Locales( req.headers['accept-language'] || '' );
var best = locales.best( allLocales );
// set $req.language property
req.language = language[ best.language ] || language.en;
req.language.defaulted = best.defaulted;
// 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,
iso6393: req.language.iso6393,
defaulted: req.language.defaulted
};
next();
};

2
package.json

@ -47,8 +47,10 @@
"geojson": "^0.4.0", "geojson": "^0.4.0",
"geojson-extent": "^0.3.1", "geojson-extent": "^0.3.1",
"geolib": "^2.0.18", "geolib": "^2.0.18",
"iso-639-3": "^1.0.0",
"iso3166-1": "^0.3.0", "iso3166-1": "^0.3.0",
"joi": "^10.1.0", "joi": "^10.1.0",
"locale": "^0.1.0",
"lodash": "^4.5.0", "lodash": "^4.5.0",
"markdown": "0.5.0", "markdown": "0.5.0",
"morgan": "1.8.1", "morgan": "1.8.1",

9
routes/v1.js

@ -18,7 +18,8 @@ var sanitizers = {
/** ----------------------- middleware ------------------------ **/ /** ----------------------- middleware ------------------------ **/
var middleware = { var middleware = {
calcSize: require('../middleware/sizeCalculator') calcSize: require('../middleware/sizeCalculator'),
requestLanguage: require('../middleware/requestLanguage')
}; };
/** ----------------------- controllers ----------------------- **/ /** ----------------------- controllers ----------------------- **/
@ -108,6 +109,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
search: createRouter([ search: createRouter([
sanitizers.search.middleware, sanitizers.search.middleware,
middleware.requestLanguage,
middleware.calcSize(), middleware.calcSize(),
// 3rd parameter is which query module to use, use fallback/geodisambiguation // 3rd parameter is which query module to use, use fallback/geodisambiguation
// first, then use original search strategy if first query didn't return anything // first, then use original search strategy if first query didn't return anything
@ -131,6 +133,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
structured: createRouter([ structured: createRouter([
sanitizers.structured_geocoding.middleware, sanitizers.structured_geocoding.middleware,
middleware.requestLanguage,
middleware.calcSize(), middleware.calcSize(),
controllers.search(peliasConfig.api, esclient, queries.structured_geocoding, not(hasResponseDataOrRequestErrors)), controllers.search(peliasConfig.api, esclient, queries.structured_geocoding, not(hasResponseDataOrRequestErrors)),
postProc.trimByGranularityStructured(), postProc.trimByGranularityStructured(),
@ -150,6 +153,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
autocomplete: createRouter([ autocomplete: createRouter([
sanitizers.autocomplete.middleware, sanitizers.autocomplete.middleware,
middleware.requestLanguage,
controllers.search(peliasConfig.api, esclient, queries.autocomplete, not(hasResponseDataOrRequestErrors)), controllers.search(peliasConfig.api, esclient, queries.autocomplete, not(hasResponseDataOrRequestErrors)),
postProc.distances('focus.point.'), postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig.api), postProc.confidenceScores(peliasConfig.api),
@ -165,6 +169,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
reverse: createRouter([ reverse: createRouter([
sanitizers.reverse.middleware, sanitizers.reverse.middleware,
middleware.requestLanguage,
middleware.calcSize(), middleware.calcSize(),
controllers.coarse_reverse(pipService, coarse_reverse_should_execute), controllers.coarse_reverse(pipService, coarse_reverse_should_execute),
controllers.search(peliasConfig.api, esclient, queries.reverse, original_reverse_should_execute), controllers.search(peliasConfig.api, esclient, queries.reverse, original_reverse_should_execute),
@ -184,6 +189,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
nearby: createRouter([ nearby: createRouter([
sanitizers.nearby.middleware, sanitizers.nearby.middleware,
middleware.requestLanguage,
middleware.calcSize(), middleware.calcSize(),
controllers.search(peliasConfig.api, esclient, queries.reverse, not(hasResponseDataOrRequestErrors)), controllers.search(peliasConfig.api, esclient, queries.reverse, not(hasResponseDataOrRequestErrors)),
postProc.distances('point.'), postProc.distances('point.'),
@ -202,6 +208,7 @@ function addRoutes(app, peliasConfig) {
]), ]),
place: createRouter([ place: createRouter([
sanitizers.place.middleware, sanitizers.place.middleware,
middleware.requestLanguage,
controllers.place(peliasConfig.api, esclient), controllers.place(peliasConfig.api, esclient),
postProc.accuracy(), postProc.accuracy(),
postProc.localNamingConventions(), postProc.localNamingConventions(),

Loading…
Cancel
Save