From a07d4dad128f71e4c47fa92c1e25a4f0ba6467f4 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 12 Nov 2015 15:28:43 +0100 Subject: [PATCH 01/19] port i18n lib to es6 and extract middleware --- website/src/libs/api-v3/i18n.js | 98 +++++++++++++++++++ .../src/middlewares/api-v3/getUserLanguage.js | 85 ++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 website/src/libs/api-v3/i18n.js create mode 100644 website/src/middlewares/api-v3/getUserLanguage.js diff --git a/website/src/libs/api-v3/i18n.js b/website/src/libs/api-v3/i18n.js new file mode 100644 index 0000000000..96ad15a2e7 --- /dev/null +++ b/website/src/libs/api-v3/i18n.js @@ -0,0 +1,98 @@ +import fs from 'fs'; +import path from 'path'; +import _ from 'lodash'; +import shared from '../../../../common'; + +const localePath = path.join(__dirname, '/../../../../common/locales/'); + +// Store translations +export let translations = {}; +// Store MomentJS localization files +export let momentLangs = {}; + +// Handle differencies in language codes between MomentJS and /locales +let momentLangsMapping = { + en: 'en-gb', + en_GB: 'en-gb', // eslint-disable-line camelcase + no: 'nn', + zh: 'zh-cn', + es_419: 'es', // eslint-disable-line camelcase +}; + +function _loadTranslations (locale) { + let files = fs.readdirSync(path.join(localePath, locale)); + + translations[locale] = {}; + + files.forEach((file) => { + if (path.extname(file) !== '.json') return; + + // We use require to load and parse a JSON file + _.merge(translations[locale], require(path.join(localePath, locale, file))); // eslint-disable-line global-require + }); +} + +// First fetch English strings so we can merge them with missing strings in other languages +_loadTranslations('en'); + +// Then load all other languages +fs.readdirSync(localePath).forEach((file) => { + if (file === 'en' || fs.statSync(path.join(localePath, file)).isDirectory() === false) return; + _loadTranslations(file); + + // Merge missing strings from english + _.defaults(translations[file], translations.en); +}); + +// Add translations to shared +shared.i18n.translations = translations; + +export let langCodes = Object.keys(translations); + +export let avalaibleLanguages = langCodes.map((langCode) => { + return { + code: langCode, + name: translations[langCode].languageName, + }; +}); + +langCodes.forEach((code) => { + let lang = _.find(avalaibleLanguages, {code}); + + lang.momentLangCode = momentLangsMapping[code] || code; + + try { + // MomentJS lang files are JS files that has to be executed in the browser so we load them as plain text files + // We wrap everything in a try catch because the file might not exist + let f = fs.readFileSync(path.join(__dirname, `/../../../node_modules/moment/locale/${lang.momentLangCode}.js`), 'utf8'); + + momentLangs[code] = f; + } catch (e) { // eslint-disable-lint no-empty + // TODO implement some type of error loggin? + // The catch block is mandatory so can't be removed + } +}); + +// Remove en_GB from langCodes checked by browser to avoid it being +// used in place of plain original 'en' (it's an optional language that can be enabled only in setting) +export let defaultLangCodes = _.without(langCodes, 'en_GB'); + +// A map of languages that have different versions and the relative versions +export let multipleVersionsLanguages = { + es: ['es-419', 'es-mx', 'es-gt', 'es-cr', 'es-pa', 'es-do', 'es-ve', 'es-co', 'es-pe', + 'es-ar', 'es-ec', 'es-cl', 'es-uy', 'es-py', 'es-bo', 'es-sv', 'es-hn', + 'es-ni', 'es-pr'], + zh: ['zh-tw'], +}; + +// Export en strings only, temporary solution for mobile +// This is copied from middlewares/locals#t() +// TODO review if this can be removed since the old mobile app is no longer active +// stringName and vars are the allowed parameters +export function enTranslations (...args) { + let language = _.find(avalaibleLanguages, {code: 'en'}); + + // language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined); + args.push(language.code); + return shared.i18n.t(...args); +} \ No newline at end of file diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js new file mode 100644 index 0000000000..9a273b79bf --- /dev/null +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -0,0 +1,85 @@ +import { model as User } from '../../models/user'; +import accepts from 'accepts'; +import _ from 'lodash'; +import { + translations, + defaultLangCodes, + multipleVersionsLanguages, +} from '../../libs/api-v3/i18n'; + +function _getFromBrowser (req) { + let acceptedLanguages = accepts(req).languages(); + + let acceptable = _(acceptedLanguages).map((lang) => { + return lang.slice(0, 2); + }).uniq().value(); + + let matches = _.intersection(acceptable, defaultLangCodes); + + let iAcceptedCompleteLang = matches.length > 0 ? multipleVersionsLanguages.indexOf(matches[0].toLowerCase()) : -1; + + if (iAcceptedCompleteLang !== -1) { + let acceptedCompleteLang = _.find(acceptedLanguages, (accepted) => { + return accepted.slice(0, 2) === multipleVersionsLanguages[iAcceptedCompleteLang]; + }); + + if (acceptedCompleteLang) { + acceptedCompleteLang = acceptedCompleteLang.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(); + } else { + return 'en'; + } +} + +function _getFromUser (user, req) { + let lang; + + if (user && user.preferences.language && translations[user.preferences.language]) { + lang = user.preferences.language; + } else { + let preferred = _getFromBrowser(req); + + lang = translations[preferred] ? preferred : 'en'; + } + + return lang; +} + +export default function getUserLanguage (req, res, next) { + if (req.query.lang) { // In case the language is specified in the request url, use it + req.language = translations[req.query.lang] ? req.query.lang : 'en'; + return next(); + } else if (req.locals && req.locals.user) { // If the request is authenticated, use the user's preferred language + req.language = _getFromUser(req.locals.user, req); + return next(); + } else if (req.session && req.session.userId) { // Same thing if the user has a valid session + User + .findOne({ + _id: req.session.userId, + }, 'preferences.language') + .exec() + .then((user) => { + req.language = _getFromUser(user, req); + return next(); + }) + .catch(next); + } else { // Otherwise get from browser + req.language = _getFromUser(null, req); + return next(); + } +} \ No newline at end of file From 4af8a8f7aae815987b881d6b53961f629878e317 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 12 Nov 2015 16:24:08 +0100 Subject: [PATCH 02/19] add tests for i18n and getUserLanguage --- test/api/v3/unit/libs/i18n.test.js | 38 ++++++++ .../unit/middlewares/getUserLanguage.test.js | 89 +++++++++++++++++++ website/src/libs/api-v3/i18n.js | 2 +- .../src/middlewares/api-v3/getUserLanguage.js | 1 + 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 test/api/v3/unit/libs/i18n.test.js create mode 100644 test/api/v3/unit/middlewares/getUserLanguage.test.js diff --git a/test/api/v3/unit/libs/i18n.test.js b/test/api/v3/unit/libs/i18n.test.js new file mode 100644 index 0000000000..8acef06486 --- /dev/null +++ b/test/api/v3/unit/libs/i18n.test.js @@ -0,0 +1,38 @@ +import { + translations, + localePath, + langCodes, +} from '../../../../../website/src/libs/api-v3/i18n'; +import fs from 'fs'; +import path from 'path'; + +describe('i18n', () => { + describe('translations', () => { + it('loads all locales', (done) => { + fs.readdir(localePath, (err, files) => { + if (err) return done(err); + let locales = []; + + files.forEach((file) => { + if (fs.statSync(path.join(localePath, file)).isDirectory() === false) return; + locales.push(file); + }); + + locales = locales.sort(); + let loaded = Object.keys(translations).sort(); + + expect(locales).to.eql(loaded); + done(); + }); + }); + + it('keeps a list all locales', () => { + expect(Object.keys(translations).sort()).to.eql(langCodes.sort()); + }); + + it('has an english translations', () => { + expect(langCodes).to.contain('en'); + expect(translations.en).to.be.an('object'); + }); + }); +}); diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js new file mode 100644 index 0000000000..4a9bb0ec04 --- /dev/null +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -0,0 +1,89 @@ +import { + generateRes, + generateReq, + generateNext, +} from '../../../../helpers/api-unit.helper'; +import getUserLanguage from '../../../../../website/src/middlewares/api-v3/getUserLanguage'; +import Q from 'q'; +import { model as User } from '../../../../../website/src/models/user'; +import { translations } from '../../../../../website/src/libs/api-v3/i18n'; +import accepts from 'accepts'; + +describe('getUserLanguage', () => { + let res, req, next; + + beforeEach(() => { + res = generateRes(); + req = generateReq(); + next = generateNext(); + + sandbox.stub(User, 'findOne').returns({ + exec() { + return Q.resolve({ + preferences: { + language: 'it', + } + }); + } + }); + }); + + describe('query parameter', () => { + it('uses the language in the query parameter if avalaible', () => { + req.query = { + lang: 'es', + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('es'); + }); + + it('falls back to english if the query parameter language does not exists', () => { + req.query = { + lang: 'bla', + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('en'); + }); + }); + + describe('authorized request', () => { + it('uses the user preferred language if avalaible', () => { + req.locals = { + user: { + preferences: { + language: 'it', + }, + }, + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('it'); + }); + + xit('falls back to english if the user preferred language is not avalaible', () => { + req.locals = { + user: { + preferences: { + language: 'bla', + }, + }, + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('en'); + }); + }); + + describe('request with session', () => { + it('uses the user preferred language if avalaible', () => { + req.session = { + userId: 123 + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('it'); + }); + }); +}); diff --git a/website/src/libs/api-v3/i18n.js b/website/src/libs/api-v3/i18n.js index 96ad15a2e7..e8a1b630cc 100644 --- a/website/src/libs/api-v3/i18n.js +++ b/website/src/libs/api-v3/i18n.js @@ -3,7 +3,7 @@ import path from 'path'; import _ from 'lodash'; import shared from '../../../../common'; -const localePath = path.join(__dirname, '/../../../../common/locales/'); +export const localePath = path.join(__dirname, '/../../../../common/locales/'); // Store translations export let translations = {}; diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index 9a273b79bf..813fe848fc 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -75,6 +75,7 @@ export default function getUserLanguage (req, res, next) { .exec() .then((user) => { req.language = _getFromUser(user, req); + console.log(req.language); return next(); }) .catch(next); From de21b72027403dc70e6a75de952d9f1164673fe7 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 12 Nov 2015 16:43:02 +0100 Subject: [PATCH 03/19] move old i18n version to api-v2 folder and use new i18n where possible --- test/common/algos.mocha.coffee | 2 +- test/common/dailies.coffee | 2 +- test/common/user.fns.ultimateGear.test.js | 2 +- test/helpers/api-integration.helper.js | 2 +- test/helpers/api-unit.helper.js | 2 +- test/helpers/content.helper.js | 2 +- website/src/controllers/api-v2/auth.js | 2 +- website/src/libs/api-v2/analytics.js | 2 +- website/src/libs/{ => api-v2}/i18n.js | 0 website/src/libs/api-v3/analyticsService.js | 1 - website/src/middlewares/locals.js | 2 +- website/src/routes/api-v2/auth.js | 2 +- website/src/routes/api-v2/coupon.js | 2 +- website/src/routes/api-v2/swagger.js | 2 +- website/src/routes/api-v2/unsubscription.js | 2 +- website/src/routes/dataexport.js | 2 +- website/src/routes/pages.js | 2 +- website/src/routes/payments.js | 2 +- website/src/server.js | 2 +- 19 files changed, 17 insertions(+), 18 deletions(-) rename website/src/libs/{ => api-v2}/i18n.js (100%) diff --git a/test/common/algos.mocha.coffee b/test/common/algos.mocha.coffee index b413c3e000..cec964a8bd 100644 --- a/test/common/algos.mocha.coffee +++ b/test/common/algos.mocha.coffee @@ -3,7 +3,7 @@ expect = require 'expect.js' sinon = require 'sinon' moment = require 'moment' shared = require '../../common/script/index.coffee' -shared.i18n.translations = require('../../website/src/libs/i18n.js').translations +shared.i18n.translations = require('../../website/src/libs/api-v2/i18n.js').translations test_helper = require './test_helper' test_helper.addCustomMatchers() $w = (s)->s.split(' ') diff --git a/test/common/dailies.coffee b/test/common/dailies.coffee index e5e3fad81a..869583d37b 100644 --- a/test/common/dailies.coffee +++ b/test/common/dailies.coffee @@ -3,7 +3,7 @@ expect = require 'expect.js' sinon = require 'sinon' moment = require 'moment' shared = require '../../common/script/index.coffee' -shared.i18n.translations = require('../../website/src/libs/i18n.js').translations +shared.i18n.translations = require('../../website/src/libs/api-v2/i18n.js').translations repeatWithoutLastWeekday = ()-> repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true} diff --git a/test/common/user.fns.ultimateGear.test.js b/test/common/user.fns.ultimateGear.test.js index d2b82f43e8..33d3c4f52c 100644 --- a/test/common/user.fns.ultimateGear.test.js +++ b/test/common/user.fns.ultimateGear.test.js @@ -1,7 +1,7 @@ 'use strict'; var shared = require('../../common/script/index.coffee'); -shared.i18n.translations = require('../../website/src/libs/i18n.js').translations +shared.i18n.translations = require('../../website/src/libs/api-v2/i18n.js').translations require('./test_helper'); diff --git a/test/helpers/api-integration.helper.js b/test/helpers/api-integration.helper.js index 1ddaa75b0d..6e78046f8e 100644 --- a/test/helpers/api-integration.helper.js +++ b/test/helpers/api-integration.helper.js @@ -9,7 +9,7 @@ import {v4 as generateUUID} from 'uuid'; import superagent from 'superagent'; import i18n from '../../common/script/src/i18n'; require('coffee-script'); -i18n.translations = require('../../website/src/libs/i18n.js').translations; +i18n.translations = require('../../website/src/libs/api-v3/i18n').translations; const API_TEST_SERVER_PORT = 3003; diff --git a/test/helpers/api-unit.helper.js b/test/helpers/api-unit.helper.js index 95a34316e1..2e789d53a9 100644 --- a/test/helpers/api-unit.helper.js +++ b/test/helpers/api-unit.helper.js @@ -3,7 +3,7 @@ import { model as User } from '../../website/src/models/user' import { model as Group } from '../../website/src/models/group' import i18n from '../../common/script/src/i18n'; require('coffee-script'); -i18n.translations = require('../../website/src/libs/i18n.js').translations; +i18n.translations = require('../../website/src/libs/api-v3/i18n.js').translations; afterEach(() => { sandbox.restore(); diff --git a/test/helpers/content.helper.js b/test/helpers/content.helper.js index c54a62d508..2f885f0830 100644 --- a/test/helpers/content.helper.js +++ b/test/helpers/content.helper.js @@ -3,7 +3,7 @@ import {each} from 'lodash'; import i18n from '../../common/script/src/i18n'; require('coffee-script'); -i18n.translations = require('../../website/src/libs/i18n.js').translations; +i18n.translations = require('../../website/src/libs/api-v3/i18n').translations; export const STRING_ERROR_MSG = 'Error processing the string. Please see Help > Report a Bug.'; export const STRING_DOES_NOT_EXIST_MSG = /^String '.*' not found.$/; diff --git a/website/src/controllers/api-v2/auth.js b/website/src/controllers/api-v2/auth.js index b01e1aba3d..524615760e 100644 --- a/website/src/controllers/api-v2/auth.js +++ b/website/src/controllers/api-v2/auth.js @@ -10,7 +10,7 @@ var FirebaseTokenGenerator = require('firebase-token-generator'); var User = require('../../models/user').model; var EmailUnsubscription = require('../../models/emailUnsubscription').model; var analytics = utils.analytics; -var i18n = require('./../../libs/i18n'); +var i18n = require('./../../libs/api-v2/i18n'); var isProd = nconf.get('NODE_ENV') === 'production'; diff --git a/website/src/libs/api-v2/analytics.js b/website/src/libs/api-v2/analytics.js index 315dc4b618..f7c1391a11 100644 --- a/website/src/libs/api-v2/analytics.js +++ b/website/src/libs/api-v2/analytics.js @@ -1,5 +1,5 @@ require('coffee-script'); -require('./i18n'); +require('./api-v2/i18n'); var _ = require('lodash'); var Content = require('../../../common').content; diff --git a/website/src/libs/i18n.js b/website/src/libs/api-v2/i18n.js similarity index 100% rename from website/src/libs/i18n.js rename to website/src/libs/api-v2/i18n.js diff --git a/website/src/libs/api-v3/analyticsService.js b/website/src/libs/api-v3/analyticsService.js index 6e460097eb..81be37322e 100644 --- a/website/src/libs/api-v3/analyticsService.js +++ b/website/src/libs/api-v3/analyticsService.js @@ -10,7 +10,6 @@ import { import { content as Content } from '../../../../common'; require('coffee-script'); -require('../../libs/i18n'); const AMPLIUDE_TOKEN = nconf.get('AMPLITUDE_KEY'); const GA_TOKEN = nconf.get('GA_ID'); diff --git a/website/src/middlewares/locals.js b/website/src/middlewares/locals.js index 2f238a6a99..e6b2fa8818 100644 --- a/website/src/middlewares/locals.js +++ b/website/src/middlewares/locals.js @@ -2,7 +2,7 @@ var nconf = require('nconf'); var _ = require('lodash'); var utils = require('../libs/utils'); var shared = require('../../../common'); -var i18n = require('../libs/i18n'); +var i18n = require('../libs/api-v2/i18n'); var buildManifest = require('../libs/buildManifest'); var shared = require('../../../common'); var forceRefresh = require('./forceRefresh'); diff --git a/website/src/routes/api-v2/auth.js b/website/src/routes/api-v2/auth.js index c60f44547b..d76891e40d 100644 --- a/website/src/routes/api-v2/auth.js +++ b/website/src/routes/api-v2/auth.js @@ -1,6 +1,6 @@ var auth = require('../../controllers/api-v2/auth'); var express = require('express'); -var i18n = require('../../libs/i18n'); +var i18n = require('../../libs/api-v2/i18n'); var router = new express.Router(); /* auth.auth*/ diff --git a/website/src/routes/api-v2/coupon.js b/website/src/routes/api-v2/coupon.js index 811d81a6f2..132184a585 100644 --- a/website/src/routes/api-v2/coupon.js +++ b/website/src/routes/api-v2/coupon.js @@ -3,7 +3,7 @@ var express = require('express'); var router = new express.Router(); var auth = require('../../controllers/api-v2/auth'); var coupon = require('../../controllers/api-v2/coupon'); -var i18n = require('../../libs/i18n'); +var i18n = require('../../libs/api-v2/i18n'); router.get('/api/v2/coupons', auth.authWithUrl, i18n.getUserLanguage, coupon.ensureAdmin, coupon.getCoupons); router.post('/api/v2/coupons/generate/:event', auth.auth, i18n.getUserLanguage, coupon.ensureAdmin, coupon.generateCoupons); diff --git a/website/src/routes/api-v2/swagger.js b/website/src/routes/api-v2/swagger.js index d93cfe6271..94bac8ad7b 100644 --- a/website/src/routes/api-v2/swagger.js +++ b/website/src/routes/api-v2/swagger.js @@ -20,7 +20,7 @@ var nconf = require("nconf"); var cron = user.cron; var _ = require('lodash'); var content = require('../../../../common').content; -var i18n = require('../../libs/i18n'); +var i18n = require('../../libs/api-v2/i18n'); var forceRefresh = require('../../middlewares/forceRefresh').middleware; module.exports = function(swagger, v2) { diff --git a/website/src/routes/api-v2/unsubscription.js b/website/src/routes/api-v2/unsubscription.js index 942a396eef..3b31305b5a 100644 --- a/website/src/routes/api-v2/unsubscription.js +++ b/website/src/routes/api-v2/unsubscription.js @@ -1,6 +1,6 @@ var express = require('express'); var router = new express.Router(); -var i18n = require('../../libs/i18n'); +var i18n = require('../../libs/api-v2/i18n'); var unsubscription = require('../../controllers/api-v2/unsubscription'); router.get('/unsubscribe', i18n.getUserLanguage, unsubscription.unsubscribe); diff --git a/website/src/routes/dataexport.js b/website/src/routes/dataexport.js index 5bf02a228c..d7328434a0 100644 --- a/website/src/routes/dataexport.js +++ b/website/src/routes/dataexport.js @@ -3,7 +3,7 @@ var router = new express.Router(); var dataexport = require('../controllers/dataexport'); var auth = require('../controllers/api-v2/auth'); var nconf = require('nconf'); -var i18n = require('../libs/i18n'); +var i18n = require('../libs/api-v2/i18n'); var locals = require('../middlewares/locals'); /* Data export */ diff --git a/website/src/routes/pages.js b/website/src/routes/pages.js index 27bc9a3619..7c847722e7 100644 --- a/website/src/routes/pages.js +++ b/website/src/routes/pages.js @@ -3,7 +3,7 @@ var express = require('express'); var router = new express.Router(); var _ = require('lodash'); var locals = require('../middlewares/locals'); -var i18n = require('../libs/i18n'); +var i18n = require('../libs/api-v2/i18n'); // -------- App -------- router.get('/', i18n.getUserLanguage, locals, function(req, res) { diff --git a/website/src/routes/payments.js b/website/src/routes/payments.js index 41c03210be..4989b113a1 100644 --- a/website/src/routes/payments.js +++ b/website/src/routes/payments.js @@ -3,7 +3,7 @@ var express = require('express'); var router = new express.Router(); var auth = require('../controllers/api-v2/auth'); var payments = require('../controllers/payments'); -var i18n = require('../libs/i18n'); +var i18n = require('../libs/api-v2/i18n'); router.get('/paypal/checkout', auth.authWithUrl, i18n.getUserLanguage, payments.paypalCheckout); router.get('/paypal/checkout/success', i18n.getUserLanguage, payments.paypalCheckoutSuccess); diff --git a/website/src/server.js b/website/src/server.js index 224022b192..5dd297638d 100644 --- a/website/src/server.js +++ b/website/src/server.js @@ -17,7 +17,7 @@ import attachMiddlewares from './middlewares/api-v3/index'; utils.setupConfig(); // Setup translations -// let i18n = require('./libs/i18n'); +// let i18n = require('./libs/api-v2/i18n'); const IS_PROD = nconf.get('IS_PROD'); // const IS_DEV = nconf.get('IS_DEV'); From eab9d7b3bab3740eb45c4fb2418381ac6ab3f0c7 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 09:15:11 -0600 Subject: [PATCH 04/19] Reorganize i18n tests. --- test/api/v3/unit/libs/i18n.test.js | 48 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/test/api/v3/unit/libs/i18n.test.js b/test/api/v3/unit/libs/i18n.test.js index 8acef06486..5bafc201dc 100644 --- a/test/api/v3/unit/libs/i18n.test.js +++ b/test/api/v3/unit/libs/i18n.test.js @@ -7,32 +7,40 @@ import fs from 'fs'; import path from 'path'; describe('i18n', () => { + let listOfLocales = []; + + before((done) => { + fs.readdir(localePath, (err, files) => { + if (err) return done(err); + + files.forEach((file) => { + if (fs.statSync(path.join(localePath, file)).isDirectory() === false) return; + listOfLocales.push(file); + }); + + listOfLocales = listOfLocales.sort(); + done(); + }); + }); + describe('translations', () => { - it('loads all locales', (done) => { - fs.readdir(localePath, (err, files) => { - if (err) return done(err); - let locales = []; - - files.forEach((file) => { - if (fs.statSync(path.join(localePath, file)).isDirectory() === false) return; - locales.push(file); - }); - - locales = locales.sort(); - let loaded = Object.keys(translations).sort(); - - expect(locales).to.eql(loaded); - done(); + it('includes a translation object for each locale', () => { + listOfLocales.forEach((locale) => { + expect(translations[locale]).to.be.an('object'); }); }); + }); - it('keeps a list all locales', () => { - expect(Object.keys(translations).sort()).to.eql(langCodes.sort()); + describe('localePath', () => { + it('is an absolute path to common/locales/', () => { + expect(localePath).to.match(/.*\/common\/locales\//); + expect(localePath) }); + }); - it('has an english translations', () => { - expect(langCodes).to.contain('en'); - expect(translations.en).to.be.an('object'); + describe('langCodes', () => { + it('is a list of all the language codes', () => { + expect(langCodes.sort()).to.eql(listOfLocales); }); }); }); From 939bc893c66a025ebeaf00657f8fa5444d35751b Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 09:21:22 -0600 Subject: [PATCH 05/19] Correct test to check req status after function is finished. --- test/api/v3/unit/middlewares/getUserLanguage.test.js | 5 +++-- website/src/middlewares/api-v3/getUserLanguage.js | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index 4a9bb0ec04..3346ef40d6 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -82,8 +82,9 @@ describe('getUserLanguage', () => { userId: 123 }; - getUserLanguage(req, res, next); - expect(req.language).to.equal('it'); + getUserLanguage(req, res, () => { + expect(req.language).to.equal('it'); + }); }); }); }); diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index 813fe848fc..fd959cab94 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -75,7 +75,6 @@ export default function getUserLanguage (req, res, next) { .exec() .then((user) => { req.language = _getFromUser(user, req); - console.log(req.language); return next(); }) .catch(next); @@ -83,4 +82,4 @@ export default function getUserLanguage (req, res, next) { req.language = _getFromUser(null, req); return next(); } -} \ No newline at end of file +} From db67451a38401fd2de2c2fa5c3058a1537325d68 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 09:24:03 -0600 Subject: [PATCH 06/19] Add done callback to test. --- test/api/v3/unit/middlewares/getUserLanguage.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index 3346ef40d6..232a036cd3 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -77,13 +77,14 @@ describe('getUserLanguage', () => { }); describe('request with session', () => { - it('uses the user preferred language if avalaible', () => { + it('uses the user preferred language if avalaible', (done) => { req.session = { userId: 123 }; getUserLanguage(req, res, () => { expect(req.language).to.equal('it'); + done(); }); }); }); From a6a9c3c74f87d95569fa0c650272f7e362f9125e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 09:27:17 -0600 Subject: [PATCH 07/19] Use context blocks instead of describe blocks. --- test/api/v3/unit/middlewares/getUserLanguage.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index 232a036cd3..7f5f9da7b3 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -28,7 +28,7 @@ describe('getUserLanguage', () => { }); }); - describe('query parameter', () => { + context('query parameter', () => { it('uses the language in the query parameter if avalaible', () => { req.query = { lang: 'es', @@ -48,7 +48,7 @@ describe('getUserLanguage', () => { }); }); - describe('authorized request', () => { + context('authorized request', () => { it('uses the user preferred language if avalaible', () => { req.locals = { user: { From 810a818334c42e3e5f96e05a7b268a19eef2d9e6 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 09:36:18 -0600 Subject: [PATCH 08/19] Add headers to req generator and unpend failing test. --- test/api/v3/unit/middlewares/getUserLanguage.test.js | 8 +++++--- test/helpers/api-unit.helper.js | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index 7f5f9da7b3..a8fbb88de7 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -62,7 +62,7 @@ describe('getUserLanguage', () => { expect(req.language).to.equal('it'); }); - xit('falls back to english if the user preferred language is not avalaible', () => { + it('falls back to english if the user preferred language is not avalaible', (done) => { req.locals = { user: { preferences: { @@ -71,8 +71,10 @@ describe('getUserLanguage', () => { }, }; - getUserLanguage(req, res, next); - expect(req.language).to.equal('en'); + getUserLanguage(req, res, () => { + expect(req.language).to.equal('en'); + done(); + }); }); }); diff --git a/test/helpers/api-unit.helper.js b/test/helpers/api-unit.helper.js index 2e789d53a9..62516f2751 100644 --- a/test/helpers/api-unit.helper.js +++ b/test/helpers/api-unit.helper.js @@ -35,6 +35,7 @@ export function generateReq(options={}) { let defaultReq = { body: {}, query: {}, + headers: {}, }; return defaults(options, defaultReq); From 500c520c5983e05aa7bbdac88517dd2810f04a36 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 11:08:28 -0600 Subject: [PATCH 09/19] Move sandbox stub to test that uses it --- .../unit/middlewares/getUserLanguage.test.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index a8fbb88de7..a222e2e9c3 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -16,16 +16,6 @@ describe('getUserLanguage', () => { res = generateRes(); req = generateReq(); next = generateNext(); - - sandbox.stub(User, 'findOne').returns({ - exec() { - return Q.resolve({ - preferences: { - language: 'it', - } - }); - } - }); }); context('query parameter', () => { @@ -80,6 +70,16 @@ describe('getUserLanguage', () => { describe('request with session', () => { it('uses the user preferred language if avalaible', (done) => { + sandbox.stub(User, 'findOne').returns({ + exec() { + return Q.resolve({ + preferences: { + language: 'it', + } + }); + } + }); + req.session = { userId: 123 }; From 43058f1642838451654e67b82c841b984408ab8b Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 11:10:22 -0600 Subject: [PATCH 10/19] Add queries for which req pieces take precedence. --- .../unit/middlewares/getUserLanguage.test.js | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test/api/v3/unit/middlewares/getUserLanguage.test.js b/test/api/v3/unit/middlewares/getUserLanguage.test.js index a222e2e9c3..f587dafe04 100644 --- a/test/api/v3/unit/middlewares/getUserLanguage.test.js +++ b/test/api/v3/unit/middlewares/getUserLanguage.test.js @@ -36,6 +36,27 @@ describe('getUserLanguage', () => { getUserLanguage(req, res, next); expect(req.language).to.equal('en'); }); + + it('uses query even if the request includes a user and session', () => { + req.query = { + lang: 'es', + }; + + req.locals = { + user: { + preferences: { + language: 'it', + }, + }, + }; + + req.session = { + userId: 123 + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('es'); + }); }); context('authorized request', () => { @@ -66,9 +87,26 @@ describe('getUserLanguage', () => { done(); }); }); + + it('uses the user preferred language even if a session is included in request', () => { + req.locals = { + user: { + preferences: { + language: 'it', + }, + }, + }; + + req.session = { + userId: 123 + }; + + getUserLanguage(req, res, next); + expect(req.language).to.equal('it'); + }); }); - describe('request with session', () => { + context('request with session', () => { it('uses the user preferred language if avalaible', (done) => { sandbox.stub(User, 'findOne').returns({ exec() { From 8372a56d888df56867af5126fbddd6d7d08e68ea Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 11:10:56 -0600 Subject: [PATCH 11/19] Adjust style of User.findOne call. --- website/src/middlewares/api-v3/getUserLanguage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index fd959cab94..475ff98742 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -68,8 +68,7 @@ export default function getUserLanguage (req, res, next) { req.language = _getFromUser(req.locals.user, req); return next(); } else if (req.session && req.session.userId) { // Same thing if the user has a valid session - User - .findOne({ + User.findOne({ _id: req.session.userId, }, 'preferences.language') .exec() From 06dd343d473bbe4fe4f338cc762cb35c9ebc1575 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 15:46:22 -0600 Subject: [PATCH 12/19] Adjust i18n script to actually display zh_TW language. --- website/src/libs/api-v2/i18n.js | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/website/src/libs/api-v2/i18n.js b/website/src/libs/api-v2/i18n.js index b769afe697..335b4d5789 100644 --- a/website/src/libs/api-v2/i18n.js +++ b/website/src/libs/api-v2/i18n.js @@ -60,18 +60,38 @@ _.each(langCodes, function(code){ }catch (e){} }); -// Remove en_GB from langCodes checked by browser to avaoi it being +// Remove en_GB from langCodes checked by browser to avaoi it being // used in place of plain original 'en' var defaultLangCodes = _.without(langCodes, 'en_GB'); // A list of languages that have different versions var multipleVersionsLanguages = ['es', 'zh']; -var latinAmericanSpanishes = ['es-419', 'es-mx', 'es-gt', 'es-cr', 'es-pa', 'es-do', 'es-ve', 'es-co', 'es-pe', - 'es-ar', 'es-ec', 'es-cl', 'es-uy', 'es-py', 'es-bo', 'es-sv', 'es-hn', - 'es-ni', 'es-pr']; +var latinAmericanSpanishes = { + 'es-419': 'es_419', + 'es-mx': 'es_419', + 'es-gt': 'es_419', + 'es-cr': 'es_419', + 'es-pa': 'es_419', + 'es-do': 'es_419', + 'es-ve': 'es_419', + 'es-co': 'es_419', + 'es-pe': 'es_419', + 'es-ar': 'es_419', + 'es-ec': 'es_419', + 'es-cl': 'es_419', + 'es-uy': 'es_419', + 'es-py': 'es_419', + 'es-bo': 'es_419', + 'es-sv': 'es_419', + 'es-hn': 'es_419', + 'es-ni': 'es_419', + 'es-pr': 'es_419', +}; -var chineseVersions = ['zh-tw']; +var chineseVersions = { + 'zh-tw': 'zh_TW', +}; var getUserLanguage = function(req, res, next){ var getFromBrowser = function(){ @@ -97,10 +117,9 @@ var getUserLanguage = function(req, res, next){ } if(matches[0] === 'es'){ - return (latinAmericanSpanishes.indexOf(acceptedCompleteLang) !== -1) ? 'es_419' : 'es'; + return latinAmericanSpanishes[acceptedCompleteLang] || 'es'; }else if(matches[0] === 'zh'){ - var iChinese = chineseVersions.indexOf(acceptedCompleteLang.toLowerCase()); - return (iChinese !== -1) ? chineseVersions[iChinese] : 'zh'; + return chineseVersions[acceptedCompleteLang] || 'zh'; }else{ return en; } @@ -158,4 +177,4 @@ module.exports.enTranslations = function(){ // stringName and vars are the allow var args = Array.prototype.slice.call(arguments, 0); args.push(language.code); return shared.i18n.t.apply(null, args); -}; \ No newline at end of file +}; From dc8d52e00a7aca9e419f5a100de410dbdf19fe32 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 16:58:48 -0600 Subject: [PATCH 13/19] Correct paths in v2 lib --- website/src/libs/api-v2/i18n.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/libs/api-v2/i18n.js b/website/src/libs/api-v2/i18n.js index 335b4d5789..e8295deb03 100644 --- a/website/src/libs/api-v2/i18n.js +++ b/website/src/libs/api-v2/i18n.js @@ -1,12 +1,12 @@ var fs = require('fs'), path = require('path'), _ = require('lodash'), - User = require('../models/user').model, + User = require('../../models/user').model, accepts = require('accepts'), - shared = require('../../../common'), + shared = require('../../../../common'), translations = {}; -var localePath = path.join(__dirname, "/../../../common/locales/") +var localePath = path.join(__dirname, "/../../../../common/locales/") var loadTranslations = function(locale){ var files = fs.readdirSync(path.join(localePath, locale)); From f672ac8c59ff2d037a46a68d0c4f21265493cfa1 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 18:05:28 -0600 Subject: [PATCH 14/19] Port over change to v2 lib --- website/src/libs/api-v3/i18n.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/website/src/libs/api-v3/i18n.js b/website/src/libs/api-v3/i18n.js index e8a1b630cc..8b8c30e498 100644 --- a/website/src/libs/api-v3/i18n.js +++ b/website/src/libs/api-v3/i18n.js @@ -79,10 +79,30 @@ export let defaultLangCodes = _.without(langCodes, 'en_GB'); // A map of languages that have different versions and the relative versions export let multipleVersionsLanguages = { - es: ['es-419', 'es-mx', 'es-gt', 'es-cr', 'es-pa', 'es-do', 'es-ve', 'es-co', 'es-pe', - 'es-ar', 'es-ec', 'es-cl', 'es-uy', 'es-py', 'es-bo', 'es-sv', 'es-hn', - 'es-ni', 'es-pr'], - zh: ['zh-tw'], + es: { + 'es-419': 'es_419', + 'es-mx': 'es_419', + 'es-gt': 'es_419', + 'es-cr': 'es_419', + 'es-pa': 'es_419', + 'es-do': 'es_419', + 'es-ve': 'es_419', + 'es-co': 'es_419', + 'es-pe': 'es_419', + 'es-ar': 'es_419', + 'es-ec': 'es_419', + 'es-cl': 'es_419', + 'es-uy': 'es_419', + 'es-py': 'es_419', + 'es-bo': 'es_419', + 'es-sv': 'es_419', + 'es-hn': 'es_419', + 'es-ni': 'es_419', + 'es-pr': 'es_419', + }, + zh: { + 'zh-tw': 'zh_TW', + } }; // Export en strings only, temporary solution for mobile @@ -95,4 +115,4 @@ export function enTranslations (...args) { // language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined); args.push(language.code); return shared.i18n.t(...args); -} \ No newline at end of file +} From 4cd4c588a8db01ae12597d04648bf16c8b6339ec Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 18:05:45 -0600 Subject: [PATCH 15/19] 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'; } } From 805d4bba241dcf71ca1817f8de62d59204a86e90 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 18:29:19 -0600 Subject: [PATCH 16/19] Adjust for linter. --- website/src/libs/api-v3/i18n.js | 2 +- website/src/middlewares/api-v3/getUserLanguage.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/website/src/libs/api-v3/i18n.js b/website/src/libs/api-v3/i18n.js index 8b8c30e498..ed2134f7bf 100644 --- a/website/src/libs/api-v3/i18n.js +++ b/website/src/libs/api-v3/i18n.js @@ -102,7 +102,7 @@ export let multipleVersionsLanguages = { }, zh: { 'zh-tw': 'zh_TW', - } + }, }; // Export en strings only, temporary solution for mobile diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index 4174d833ea..64d98475d5 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -20,6 +20,7 @@ function _getUniqueListOfLanguages (languages) { function _checkForApplicableLanguageVariant (originalLanguageOptions) { let languageVariant = _.find(originalLanguageOptions, (accepted) => { let trimmedAccepted = accepted.slice(0, 2); + return multipleVersionsLanguages[trimmedAccepted]; }); From 1ae9b7aff03ed0ce340a5d027b5598fdb03f8abb Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 18:40:46 -0600 Subject: [PATCH 17/19] Correct path to i18n --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index e2fc2b1083..18ce157de2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -129,7 +129,7 @@ module.exports = function(grunt) { grunt.registerTask('test:prepare:translations', function() { require('babel/register'); - var i18n = require('./website/src/libs/i18n'), + var i18n = require('./website/src/libs/api-v3/i18n'), fs = require('fs'); fs.writeFileSync('test/spec/mocks/translations.js', "if(!window.env) window.env = {};\n" + From 4a52f227741d5bcdb3a5dd4c55e1eff638581548 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 19:30:36 -0600 Subject: [PATCH 18/19] Correct path to old i18n file --- website/src/libs/api-v2/analytics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/libs/api-v2/analytics.js b/website/src/libs/api-v2/analytics.js index 6f585eb2fe..6c1ccd3020 100644 --- a/website/src/libs/api-v2/analytics.js +++ b/website/src/libs/api-v2/analytics.js @@ -1,4 +1,4 @@ -require('./api-v2/i18n'); +require('./i18n'); var _ = require('lodash'); var Content = require('../../../../common').content; From 2e21d227e0906937beea93666c6bbdb5a2420631 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 13 Nov 2015 19:36:49 -0600 Subject: [PATCH 19/19] Simplify get language from user function. --- website/src/middlewares/api-v3/getUserLanguage.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/website/src/middlewares/api-v3/getUserLanguage.js b/website/src/middlewares/api-v3/getUserLanguage.js index 64d98475d5..234a78a29c 100644 --- a/website/src/middlewares/api-v3/getUserLanguage.js +++ b/website/src/middlewares/api-v3/getUserLanguage.js @@ -49,15 +49,8 @@ function _getFromBrowser (req) { } function _getFromUser (user, req) { - let lang; - - if (user && user.preferences.language && translations[user.preferences.language]) { - lang = user.preferences.language; - } else { - let preferred = _getFromBrowser(req); - - lang = translations[preferred] ? preferred : 'en'; - } + let preferredLang = user && user.preferences && user.preferences.language; + let lang = translations[preferredLang] ? preferredLang : _getFromBrowser(req); return lang; }