language.js 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. const { availableLanguages } = require('../../package.json');
  2. const config = require('../config');
  3. const fs = require('fs');
  4. const path = require('path');
  5. const { negotiateLanguages } = require('@fluent/langneg');
  6. const langData = require('cldr-core/supplemental/likelySubtags.json');
  7. // We return early in the middleware if the lang header is long.
  8. // If that ever changes we should re-evaluate this regex.
  9. // eslint-disable-next-line security/detect-unsafe-regex
  10. const acceptLanguages = /(([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?/g;
  11. function allLangs() {
  12. return fs.readdirSync(path.join(__dirname, '..', '..', 'public', 'locales'));
  13. }
  14. const languages = config.l10n_dev ? allLangs() : availableLanguages;
  15. module.exports = function(req, res, next) {
  16. const header = req.headers['accept-language'] || 'en-US';
  17. if (header.length > 255) {
  18. req.language = 'en-US';
  19. return next();
  20. }
  21. const langs = header.replace(/\s/g, '').match(acceptLanguages) || ['en-US'];
  22. const preferred = langs
  23. .map(l => {
  24. const parts = l.split(';');
  25. return {
  26. locale: parts[0],
  27. q: parts[1] ? parseFloat(parts[1].split('=')[1]) : 1
  28. };
  29. })
  30. .sort((a, b) => b.q - a.q)
  31. .map(x => x.locale);
  32. req.language = negotiateLanguages(preferred, languages, {
  33. strategy: 'lookup',
  34. likelySubtags: langData.supplemental.likelySubtags,
  35. defaultLocale: 'en-US'
  36. })[0];
  37. next();
  38. };