From 4cd4c588a8db01ae12597d04648bf16c8b6339ec Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 18:05:45 -0600 Subject: [PATCH] Add tests for browser selection of language and refactor --- .../unit/middlewares/getUserLanguage.test.js | 110 ++++++++++++++++++ .../src/middlewares/api-v3/getUserLanguage.js | 51 ++++---- 2 files changed, 136 insertions(+), 25 deletions(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index f587dafe04..2cbf1d2c87 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -128,4 +128,114 @@ describe('getUserLanguage', () => { }); }); }); + + context('browser fallback', () => { + it('uses browser specificed language', (done) => { + req.headers['accept-language'] = 'pt'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('pt'); + done(); + }); + }); + + it('uses first language in series if browser specifies multiple', (done) => { + req.headers['accept-language'] = 'he, pt, it'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('he'); + done(); + }); + }); + + it('skips invalid lanaguages and uses first language in series if browser specifies multiple', (done) => { + req.headers['accept-language'] = 'blah, he, pt, it'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('he'); + done(); + }); + }); + + it('uses normal version of language if specialized locale is passed in', (done) => { + req.headers['accept-language'] = 'fr-CA'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('fr'); + done(); + }); + }); + + it('uses normal version of language if specialized locale is passed in', (done) => { + req.headers['accept-language'] = 'fr-CA'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('fr'); + done(); + }); + }); + + it('uses es if es is passed in', (done) => { + req.headers['accept-language'] = 'es'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('es'); + done(); + }); + }); + + it('uses es_419 if applicable es-languages are passed in', (done) => { + req.headers['accept-language'] = 'es-mx'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('es_419'); + done(); + }); + }); + + it('uses es_419 if multiple es languages are passed in', (done) => { + req.headers['accept-language'] = 'es-GT, es-MX, es-CR'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('es_419'); + done(); + }); + }); + + it('zh', (done) => { + req.headers['accept-language'] = 'zh-TW'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('zh_TW'); + done(); + }); + }); + + it('uses english if browser specified language is not compatible', (done) => { + req.headers['accept-language'] = 'blah'; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('en'); + done(); + }); + }); + + it('uses english if browser does not specify', (done) => { + req.headers['accept-language'] = ''; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('en'); + done(); + }); + }); + + it('uses english if browser does not supply an accept-language header', (done) => { + delete req.headers['accept-language']; + + getUserLanguage(req, res, () => { + expect(req.language).to.equal('en'); + done(); + }); + }); + }); }); diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index 475ff98742..4174d833ea 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -7,42 +7,43 @@ import { multipleVersionsLanguages, } from '../../libs/api-v3/i18n'; -function _getFromBrowser (req) { - let acceptedLanguages = accepts(req).languages(); - - let acceptable = _(acceptedLanguages).map((lang) => { +function _getUniqueListOfLanguages (languages) { + let acceptableLanguages = _(languages).map((lang) => { return lang.slice(0, 2); }).uniq().value(); - let matches = _.intersection(acceptable, defaultLangCodes); + let uniqueListOfLanguages = _.intersection(acceptableLanguages, defaultLangCodes); - let iAcceptedCompleteLang = matches.length > 0 ? multipleVersionsLanguages.indexOf(matches[0].toLowerCase()) : -1; + return uniqueListOfLanguages; +} - if (iAcceptedCompleteLang !== -1) { - let acceptedCompleteLang = _.find(acceptedLanguages, (accepted) => { - return accepted.slice(0, 2) === multipleVersionsLanguages[iAcceptedCompleteLang]; - }); +function _checkForApplicableLanguageVariant (originalLanguageOptions) { + let languageVariant = _.find(originalLanguageOptions, (accepted) => { + let trimmedAccepted = accepted.slice(0, 2); + return multipleVersionsLanguages[trimmedAccepted]; + }); - if (acceptedCompleteLang) { - acceptedCompleteLang = acceptedCompleteLang.toLowerCase(); + return languageVariant; +} + +function _getFromBrowser (req) { + let originalLanguageOptions = accepts(req).languages(); + let uniqueListOfLanguages = _getUniqueListOfLanguages(originalLanguageOptions); + let baseLanguage = (uniqueListOfLanguages[0] || '').toLowerCase(); + let languageMapping = multipleVersionsLanguages[baseLanguage]; + + if (languageMapping) { + let languageVariant = _checkForApplicableLanguageVariant(originalLanguageOptions); + + if (languageVariant) { + languageVariant = languageVariant.toLowerCase(); } else { return 'en'; } - if (matches[0] === 'es') { - // In case of a Latin American version of Spanish use 'es_419' - return multipleVersionsLanguages.es.indexOf(acceptedCompleteLang !== -1) ? 'es_419' : 'es'; - } else if (matches[0] === 'zh') { - let iChinese = multipleVersionsLanguages.zh.indexOf(acceptedCompleteLang.toLowerCase()); - - return iChinese !== -1 ? multipleVersionsLanguages.zh[iChinese] : 'zh'; - } else { - return 'en'; - } - } else if (matches.length > 0) { - return matches[0].toLowerCase(); + return languageMapping[languageVariant] || baseLanguage; } else { - return 'en'; + return baseLanguage || 'en'; } }