|
|
|
const _ = require('lodash');
|
|
|
|
const logger = require( 'pelias-logger' ).get( 'api' );
|
|
|
|
|
|
|
|
/**
|
|
|
|
this middleware is responsible for negotiating HTTP locales for incoming
|
|
|
|
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 '?lang=fr' or '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 ){
|
|
|
|
|
|
|
|
// 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 locales, best, via = 'default';
|
|
|
|
|
|
|
|
// input language via query param
|
|
|
|
if( via === 'default' && 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 ];
|
|
|
|
via = 'querystring';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// input language via request headers
|
|
|
|
if( via === 'default' && 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 ];
|
|
|
|
via = 'header';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set $req.language property
|
|
|
|
req.language = _.clone( lang );
|
|
|
|
req.language.defaulted = ( via === 'default' );
|
|
|
|
|
|
|
|
// set $req.clean property in order to print language info in response header
|
|
|
|
req.clean.lang = {
|
|
|
|
name: req.language.name,
|
|
|
|
iso6391: req.language.iso6391,
|
|
|
|
iso6393: req.language.iso6393,
|
|
|
|
defaulted: req.language.defaulted
|
|
|
|
};
|
|
|
|
|
|
|
|
logger.debug( '[lang] \'%s\' via \'%s\'', lang.iso6391, via );
|
|
|
|
|
|
|
|
next();
|
|
|
|
};
|