File size: 2,623 Bytes
befd7da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
'use strict';

const parser = require('accept-language-parser');
const _ = require('lodash');
const { languageCodeExists } = require('country-language');

const DEFAULT_LANGUAGE = 'en';

module.exports = setup;

/**
 * Intersect the list of user accept languages over the list of authorized languages
 * and return the first matching language
 *
 * iterate over the ordered list of user accepted languages and:
 *   1/ find exact match (country + region)
 *   2/ find partial match (country code)
 *
 * @param   {Array} authorizedLanguages Array of authorized/known languages.
 * @param   {Array} userAcceptLanguages Array of parsed languages from Accept-Language.
 * @returns {String|null} The first matching language if found, null otherwise.
 */
function findLanguageMatch(authorizedLanguages, userAcceptLanguages) {
  for (const language of userAcceptLanguages) {
    // find exact matching
    const foundExactLanguage = _.find(
      authorizedLanguages,
      lang => lang === `${language.code}-${language.region}`
    );
    if (foundExactLanguage) return foundExactLanguage;

    // compare language country code only
    const foundCountryCode = _.find(authorizedLanguages, lang => {
      // eslint-disable-line no-loop-func
      const parsedLanguage = _.get(parser.parse(lang), '[0]');
      return parsedLanguage.code === language.code;
    });
    if (foundCountryCode) return foundCountryCode;
  }

  return null;
}

/**
 * Setup language middleware
 * @param  {Object} opts the language options
 * @param  {Object} opts.languages the languages list
 * @return {void}
 */
function setup({ languages = [], defaultLanguage = DEFAULT_LANGUAGE } = {}) {
  if (!languages || !languages.length) {
    throw new Error('opts.languages is mandatory');
  }

  /**
   * Middleware
   * @param  {Object}   req  Express request
   * @param  {Object}   res  Express response
   * @param  {Function} next Express next handler
   * @returns {void}
   */
  return function middleware(req, res, next) {
    const acceptLanguage = req.get('Accept-Language');

    if (!acceptLanguage) {
      req.language = defaultLanguage;
      return next();
    }

    // parse and order the locales by priority
    const parsedLanguages = parser.parse(acceptLanguage);

    // validate languages
    const userAcceptLanguages = _.filter(parsedLanguages, language =>
      languageCodeExists(language.code)
    );

    // intersect user accept languages with the known languages list
    const language =
      findLanguageMatch(languages, userAcceptLanguages) || defaultLanguage;

    req.language = language;

    return next();
  };
}