You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

108 lines
3.2 KiB

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
};
// logging
logger.info( '[lang] \'%s\' via \'%s\'', lang.iso6391, via );
next();
};