diff --git a/src/core/actions/index.js b/src/core/actions/index.js index 00cd2e8b12a..c96a85f6242 100644 --- a/src/core/actions/index.js +++ b/src/core/actions/index.js @@ -5,6 +5,13 @@ export function setJWT(token) { }; } +export function setLang(lang) { + return { + type: 'SET_LANG', + payload: {lang}, + }; +} + export function loadEntities(entities) { return { type: 'ENTITIES_LOADED', diff --git a/src/core/api/index.js b/src/core/api/index.js index 5cd8ba23b7d..2f0ad408ae0 100644 --- a/src/core/api/index.js +++ b/src/core/api/index.js @@ -14,9 +14,9 @@ function makeQueryString(query) { return url.format({query}); } -export function callApi({endpoint, schema, params, auth = false, state = {}, method = 'get', body, - credentials}) { - const queryString = makeQueryString(params); +export function callApi({endpoint, schema, params = {}, auth = false, state = {}, method = 'get', + body, credentials}) { + const queryString = makeQueryString({...params, lang: state.lang}); const options = { headers: {}, method, @@ -48,7 +48,7 @@ export function search({ api, page, query }) { return callApi({ endpoint: 'internal/addons/search', schema: {results: arrayOf(addon)}, - params: {q: query, lang: 'en-US', page}, + params: {q: query, page}, state: api, auth: true, }); @@ -58,7 +58,6 @@ export function fetchAddon({ api, slug }) { return callApi({ endpoint: `addons/addon/${slug}`, schema: addon, - params: {lang: 'en-US'}, auth: true, state: api, }); @@ -69,7 +68,6 @@ export function login({ api, code, state }) { endpoint: 'internal/accounts/login', method: 'post', body: {code, state}, - params: {lang: 'en-US'}, state: api, credentials: true, }); @@ -83,7 +81,6 @@ export function fetchProfile({ api }) { return callApi({ endpoint: 'accounts/profile', schema: user, - params: {lang: 'en-US'}, auth: true, state: api, }); diff --git a/src/core/reducers/api.js b/src/core/reducers/api.js index 83f6516cb16..b121f485e08 100644 --- a/src/core/reducers/api.js +++ b/src/core/reducers/api.js @@ -2,6 +2,8 @@ export default function api(state = {}, action) { switch (action.type) { case 'SET_JWT': return {...state, token: action.payload.token}; + case 'SET_LANG': + return {...state, lang: action.payload.lang}; default: return state; } diff --git a/src/core/server/base.js b/src/core/server/base.js index e48c425188c..199028ebaac 100644 --- a/src/core/server/base.js +++ b/src/core/server/base.js @@ -17,7 +17,7 @@ import WebpackIsomorphicToolsConfig from 'webpack-isomorphic-tools-config'; import ServerHtml from 'core/containers/ServerHtml'; import config from 'config'; -import { setJWT } from 'core/actions'; +import { setLang, setJWT } from 'core/actions'; import log from 'core/logger'; import { getDirection, getLangFromRouter, langToLocale } from 'core/i18n/utils'; import I18nProvider from 'core/i18n/Provider'; @@ -114,6 +114,7 @@ function baseServer(routes, createStore, { appInstanceName = appName } = {}) { const lang = getLangFromRouter(renderProps); const dir = getDirection(lang); const locale = langToLocale(lang); + store.dispatch(setLang(lang)); function hydrateOnClient(props = {}) { const pageProps = { diff --git a/src/disco/api.js b/src/disco/api.js index a75dca32342..073aed9f019 100644 --- a/src/disco/api.js +++ b/src/disco/api.js @@ -10,7 +10,6 @@ export function getDiscoveryAddons({ api }) { return callApi({ endpoint: 'discovery', schema: {results: arrayOf(discoResult)}, - params: {lang: 'en-US'}, state: api, }); } diff --git a/src/disco/store.js b/src/disco/store.js index 23bb9032a52..647f0be0e6f 100644 --- a/src/disco/store.js +++ b/src/disco/store.js @@ -3,12 +3,13 @@ import { reducer as reduxAsyncConnect } from 'redux-async-connect'; import { middleware } from 'core/store'; import addons from 'core/reducers/addons'; +import api from 'core/reducers/api'; import discoResults from 'disco/reducers/discoResults'; import installations from 'disco/reducers/installations'; export default function createStore(initialState = {}) { return _createStore( - combineReducers({addons, discoResults, installations, reduxAsyncConnect}), + combineReducers({addons, api, discoResults, installations, reduxAsyncConnect}), initialState, middleware(), ); diff --git a/tests/client/core/actions/test_index.js b/tests/client/core/actions/test_index.js index 4dcd530f989..259fb41b0ff 100644 --- a/tests/client/core/actions/test_index.js +++ b/tests/client/core/actions/test_index.js @@ -8,6 +8,14 @@ describe('core actions setJWT', () => { }); }); +describe('core actions setLang', () => { + it('creates the SET_LANG action', () => { + assert.deepEqual( + actions.setLang('fr'), + {type: 'SET_LANG', payload: {lang: 'fr'}}); + }); +}); + describe('ENTITIES_LOADED', () => { const entities = sinon.stub(); const action = actions.loadEntities(entities); diff --git a/tests/client/core/api/test_api.js b/tests/client/core/api/test_api.js index 0a38cd649b0..6e144d30355 100644 --- a/tests/client/core/api/test_api.js +++ b/tests/client/core/api/test_api.js @@ -28,10 +28,10 @@ describe('api', () => { // FIXME: This shouldn't fail if the args are in a different order. mockWindow.expects('fetch') .withArgs( - 'https://addons.mozilla.org/api/v3/internal/addons/search/?q=foo&lang=en-US&page=3') + 'https://addons.mozilla.org/api/v3/internal/addons/search/?q=foo&page=3&lang=en-US') .once() .returns(mockResponse()); - return api.search({query: 'foo', page: 3}) + return api.search({api: {lang: 'en-US'}, query: 'foo', page: 3}) .then(() => mockWindow.verify()); }); @@ -71,7 +71,7 @@ describe('api', () => { {headers: {}, method: 'get'}) .once() .returns(mockResponse()); - return api.fetchAddon({slug: 'foo'}) + return api.fetchAddon({api: {lang: 'en-US'}, slug: 'foo'}) .then(() => mockWindow.verify()); }); @@ -93,7 +93,7 @@ describe('api', () => { {headers: {}, method: 'get'}) .once() .returns(Promise.resolve({ok: false})); - return api.fetchAddon({slug: 'foo'}) + return api.fetchAddon({api: {lang: 'en-US'}, slug: 'foo'}) .then(unexpectedSuccess, (error) => assert.equal(error.message, 'Error calling API')); }); @@ -107,12 +107,13 @@ describe('api', () => { {headers: {authorization: `Bearer ${token}`}, method: 'get'}) .once() .returns(mockResponse()); - return api.fetchAddon({api: {token}, slug: 'bar'}).then((results) => { - const foo = {slug: 'foo', name: 'Foo!'}; - assert.deepEqual(results.result, 'foo'); - assert.deepEqual(results.entities, {addons: {foo}}); - mockWindow.verify(); - }); + return api.fetchAddon({api: {lang: 'en-US', token}, slug: 'bar'}) + .then((results) => { + const foo = {slug: 'foo', name: 'Foo!'}; + assert.deepEqual(results.result, 'foo'); + assert.deepEqual(results.entities, {addons: {foo}}); + mockWindow.verify(); + }); }); }); @@ -138,10 +139,11 @@ describe('api', () => { }) .once() .returns(mockResponse()); - return api.login({api: {}, code: 'my-code', state: 'my-state'}).then((apiResponse) => { - assert.strictEqual(apiResponse, response); - mockWindow.verify(); - }); + return api.login({api: {lang: 'en-US'}, code: 'my-code', state: 'my-state'}) + .then((apiResponse) => { + assert.strictEqual(apiResponse, response); + mockWindow.verify(); + }); }); }); @@ -160,10 +162,11 @@ describe('api', () => { ok: true, json() { return user; }, })); - return api.fetchProfile({api: {token}}).then((apiResponse) => { - assert.deepEqual(apiResponse, {entities: {users: {foo: user}}, result: 'foo'}); - mockWindow.verify(); - }); + return api.fetchProfile({api: {lang: 'en-US', token}}) + .then((apiResponse) => { + assert.deepEqual(apiResponse, {entities: {users: {foo: user}}, result: 'foo'}); + mockWindow.verify(); + }); }); }); }); diff --git a/tests/client/core/reducers/test_api.js b/tests/client/core/reducers/test_api.js index ba7644663ad..93e251025fb 100644 --- a/tests/client/core/reducers/test_api.js +++ b/tests/client/core/reducers/test_api.js @@ -11,6 +11,11 @@ describe('api reducer', () => { assert.deepEqual(api({foo: 'bar'}, {type: 'SET_JWT', payload: {token}}), {foo: 'bar', token}); }); + it('stores the lang', () => { + const lang = 'de'; + assert.deepEqual(api({bar: 'baz'}, {type: 'SET_LANG', payload: {lang}}), {bar: 'baz', lang}); + }); + it('defaults to an empty object', () => { assert.deepEqual(api(undefined, {type: 'UNRELATED'}), {}); }); diff --git a/tests/client/disco/test_api.js b/tests/client/disco/test_api.js index e13674dc112..cbeea45f3c7 100644 --- a/tests/client/disco/test_api.js +++ b/tests/client/disco/test_api.js @@ -12,7 +12,6 @@ describe('disco api', () => { assert.ok(callApi.calledWith({ endpoint: 'discovery', schema: {results: arrayOf(discoResult)}, - params: {lang: 'en-US'}, state: api, })); });