├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ ├── actions.js ├── components │ ├── IntlProvider.js │ └── Provider.js ├── immutable.js ├── integrated.js ├── lib.js └── reducer.js ├── babel.config.js ├── examples ├── initial-locale │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ └── index.js └── multiple-languages │ ├── .env │ ├── README.md │ ├── package.json │ ├── public │ └── index.html │ └── src │ ├── components │ ├── Greeting.js │ └── SwitchLocale.js │ ├── index.js │ └── store.js ├── package.json └── src ├── components ├── IntlProvider.js └── Provider.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | dist 5 | lib 6 | coverage 7 | .idea 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Next 2 | 3 | ## v2.3.0 4 | 5 | - Added support for `react-intl@^4.0.0` and `react-intl@^5.0.0` 6 | - Updated `peerDependencies` 7 | 8 | ## v2.1.1 9 | 10 | - Include `react-redux@^7.0.0"` in `peerDependencies` 11 | 12 | ## v2.1.0 13 | 14 | - Include `react-redux@^6.0.0"` in `peerDependencies` 15 | 16 | ## v2.0.2 17 | 18 | - Rebuild lib 19 | 20 | ## v2.0.1 21 | 22 | - Use .babelrc, close [#50](https://github.com/ratson/react-intl-redux/issues/50) 23 | 24 | 25 | ## v2.0.0 26 | 27 | ##### Breaking Changes 28 | - Use `locale` as `key` for `IntlProvider` by default. 29 | 30 | 31 | ## v1.0.0 32 | 33 | ##### Enhancements 34 | - Set `sideEffects` to `false` for webpack 35 | 36 | 37 | ## v0.7.0 38 | 39 | ##### Breaking Changes 40 | - Remove deprecated `update` action, `updateIntl` should always be used 41 | - Remove `key` hack, see [#7](https://github.com/ratson/react-intl-redux/issues/7) 42 | 43 | ##### Documentation 44 | - Add [multiple-languages](https://github.com/ratson/react-intl-redux/tree/master/examples/multiple-languages) example 45 | 46 | 47 | ## v0.6.0 48 | 49 | ##### Breaking Changes 50 | - Add `formats` to reducer. ([@mewdriller](https://github.com/mewdriller) in [#35](https://github.com/ratson/react-intl-redux/pull/35)) 51 | 52 | ##### Internal 53 | - Change to use `ava` for testing 54 | - Update example to use `create-react-app` 55 | 56 | 57 | ## v0.5.0 58 | 59 | - React 15.5 compatibility 60 | 61 | 62 | ## v0.4.1 63 | 64 | - Remove usage of `storeShape` 65 | 66 | 67 | ## v0.4.0 68 | 69 | ##### Breaking Changes 70 | - Change `react-intl`, `react-redux`, `redux` to be `peerDependencies` 71 | 72 | ## v0.3.0 73 | 74 | ##### Breaking Changes 75 | - Update `react-redux` from `^4.4.6` to `^5.0.1` 76 | - Remove unnecessary React `propTypes` from the production build 77 | 78 | ##### Enhancements 79 | - Export `initialState` 80 | 81 | ## v0.2.0 82 | 83 | ##### Breaking Changes 84 | - Update `redux` from `^3.5.2` to `^3.6.0` 85 | 86 | ##### Enhancements 87 | - `IntlProvider` accept optional `intlSelector` function 88 | 89 | ## v0.1.1 90 | 91 | ##### Deprecations 92 | - Warn for usage of `update` 93 | 94 | ## v0.1.0 95 | 96 | ##### Breaking Changes 97 | - Do not accept `props` for `Provider` 98 | 99 | ##### General 100 | 101 | - Export `IntlProvider` 102 | 103 | ##### Deprecations 104 | - Deprecate `update` in favor of `updateIntl` 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2018 Ratson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Intl Redux 2 | 3 | [Redux](https://github.com/reactjs/redux) binding for [React Intl](https://github.com/yahoo/react-intl). 4 | 5 | Building idiomatic React Redux Application by 6 | having translations in store and dispatching action to update it. 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm install react-intl-redux react react-intl react-redux redux --save 12 | ``` 13 | 14 | ## Usage 15 | 16 | 17 | 18 | 19 | ```js 20 | import React from 'react' 21 | import ReactDOM from 'react-dom' 22 | import { createStore, combineReducers } from 'redux' 23 | import { FormattedNumber } from 'react-intl' 24 | import { Provider, intlReducer } from 'react-intl-redux' 25 | import reducers from '/reducers' 26 | 27 | const reducer = combineReducers({ 28 | ...reducers, 29 | intl: intlReducer, 30 | }) 31 | 32 | const store = createStore(reducer) 33 | 34 | const App = () => ( 35 | 36 | 37 | 38 | ) 39 | 40 | ReactDOM.render(, document.getElementById('container')) 41 | ``` 42 | 43 | ### Provide `locale` and `messages` on load 44 | 45 | You should provide a different `locale` and `messages` if your user is not using `en` locale. 46 | 47 | 48 | 49 | ```js 50 | const initialState = { 51 | intl: { 52 | locale: 'it', 53 | messages: { 54 | 'app.greeting': 'Ciao!', 55 | }, 56 | }, 57 | // ...other initialState 58 | } 59 | const store = createStore(reducer, initialState) 60 | ``` 61 | 62 | Refer to the [`initial-locale` example](https://github.com/ratson/react-intl-redux/tree/master/examples/initial-locale) for more details. 63 | 64 | ### Switch `locale` and `messages` on request 65 | 66 | You could also switch `locale` on user's request by dispatching `updateIntl` action. 67 | 68 | 69 | 70 | ```js 71 | import { updateIntl } from 'react-intl-redux' 72 | 73 | store.dispatch(updateIntl({ 74 | locale, 75 | messages, 76 | })) 77 | ``` 78 | 79 | React Intl in browsers only contain locale data for basic English 80 | by default, see 81 | [Loading Locale Data](https://github.com/yahoo/react-intl/wiki#loading-locale-data) 82 | for loading locale data in browsers. 83 | 84 | ### `Provider` vs `IntlProvider` 85 | 86 | In most cases, `react-intl-redux` will be wrapped immediately after `Provider` from `react-redux`. For convenient, `react-intl-redux` provides `Provider` to do that for you. 87 | 88 | However, if you don't want it, you could do it manually via [`IntlProvider`](https://github.com/yahoo/react-intl/wiki/Components#intlprovider). For example, 89 | 90 | 91 | 92 | ```js 93 | import React from 'react' 94 | import { IntlProvider } from 'react-intl-redux' 95 | import { Provider } from 'react-redux' 96 | 97 | const App = () => ( 98 | 99 | 100 | 101 | 102 | 103 | ) 104 | ``` 105 | 106 | ### Formatting Data 107 | 108 | `react-intl` provides two ways to format data, see the [official docs](https://github.com/yahoo/react-intl/wiki#formatting-data). 109 | 110 | To change `formats` through [React components](https://github.com/yahoo/react-intl/wiki/Components), 111 | 112 | 113 | 114 | ```js 115 | import { updateIntl } from 'react-intl-redux' 116 | 117 | store.dispatch(updateIntl({ 118 | locale, 119 | formats, 120 | messages, 121 | })) 122 | ``` 123 | 124 | ### Use with `redux-immutable` 125 | 126 | See the usage in [test](https://github.com/ratson/react-intl-redux/blob/master/test/immutable.spec.js). 127 | 128 | ## Examples 129 | 130 | There are some examples under the [`examples`](./examples) folder for reference. 131 | 132 | ## Troubleshooting 133 | 134 | 1. Why my connected component does not update after locale change? 135 | 136 | By default, `locale` is used as `key` for `IntlProvider`, which will trigger re-render when locale changes, things should just work. 137 | 138 | If it doesn't, here are few solutions could be tried, 139 | 140 | * Do a `forceUpdate` after changing locale. 141 | * Mark the connecting compoent `{pure: false}`. 142 | * Pass `locale` in `props`. 143 | * Set `key` when dispatching `updateIntl`. 144 | * Provide custom `intlSelector` for `IntlProvider`. 145 | 146 | 2. How to use `intl` in asynchronous action? 147 | 148 | A simple solution would be retrive `intl` object using [`injectIntl`](https://github.com/yahoo/react-intl/wiki/API#injection-api) and pass it in the action payload. 149 | -------------------------------------------------------------------------------- /__tests__/actions.js: -------------------------------------------------------------------------------- 1 | import { UPDATE, updateIntl } from '../src/' 2 | 3 | test('updateIntl should create action', () => { 4 | const locale = 'it' 5 | const messages = {} 6 | 7 | expect(updateIntl({ locale, messages })).toEqual({ 8 | type: UPDATE, 9 | payload: { locale, messages, formats: undefined } 10 | }) 11 | }) 12 | 13 | test('updateIntl should include formats', () => { 14 | const locale = 'it' 15 | const formats = {} 16 | 17 | expect(updateIntl({ locale, formats })).toEqual({ 18 | type: UPDATE, 19 | payload: { locale, formats, messages: undefined } 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /__tests__/components/IntlProvider.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { shallow } from 'enzyme' 2 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17' 3 | import React from 'react' 4 | import { FormattedNumber } from 'react-intl' 5 | import { Provider } from 'react-redux' 6 | import { combineReducers, createStore } from 'redux' 7 | import { IntlProvider, intlReducer } from '../../src/' 8 | 9 | Enzyme.configure({ adapter: new Adapter() }) 10 | 11 | test('IntlProvider should render default en locale', () => { 12 | const reducer = combineReducers({ 13 | intl: intlReducer 14 | }) 15 | const store = createStore(reducer) 16 | const App = () => ( 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | const app = shallow() 24 | 25 | expect(app.html()).toBe('1,000') 26 | }) 27 | -------------------------------------------------------------------------------- /__tests__/components/Provider.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { shallow } from 'enzyme' 2 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17' 3 | import React from 'react' 4 | import { FormattedNumber } from 'react-intl' 5 | import { combineReducers, createStore } from 'redux' 6 | import { intlReducer, Provider } from '../../src/' 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | test('Provider should render default en locale', () => { 11 | const reducer = combineReducers({ 12 | intl: intlReducer 13 | }) 14 | const store = createStore(reducer) 15 | const App = () => ( 16 | 17 | 18 | 19 | ) 20 | const app = shallow() 21 | 22 | expect(app.html()).toBe('1,000') 23 | }) 24 | -------------------------------------------------------------------------------- /__tests__/immutable.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { shallow } from 'enzyme' 2 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17' 3 | import Immutable from 'immutable' 4 | import React from 'react' 5 | import { FormattedNumber } from 'react-intl' 6 | import { Provider } from 'react-redux' 7 | import { createStore } from 'redux' 8 | import { combineReducers } from 'redux-immutable' 9 | import { IntlProvider, intlReducer } from '../src/' 10 | 11 | Enzyme.configure({ adapter: new Adapter() }) 12 | 13 | test('IntlProvider should render default en locale', () => { 14 | const reducer = combineReducers({ 15 | intl: intlReducer 16 | }) 17 | const initialState = Immutable.fromJS({ 18 | intl: { 19 | locale: 'en', 20 | messages: {} 21 | } 22 | }) 23 | const store = createStore(reducer, initialState) 24 | // eslint-disable-next-line unicorn/consistent-function-scoping 25 | const intlSelector = state => state.get('intl').toJS() 26 | const App = () => ( 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | const app = shallow() 34 | 35 | expect(app.html()).toBe('1,000') 36 | }) 37 | -------------------------------------------------------------------------------- /__tests__/integrated.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { mount } from 'enzyme' 2 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17' 3 | import React from 'react' 4 | import { FormattedNumber } from 'react-intl' 5 | import { Provider } from 'react-redux' 6 | import { combineReducers, createStore } from 'redux' 7 | import { IntlProvider, intlReducer, updateIntl } from '../src/' 8 | 9 | Enzyme.configure({ adapter: new Adapter() }) 10 | 11 | test('change locale', () => { 12 | const reducer = combineReducers({ 13 | intl: intlReducer 14 | }) 15 | const store = createStore(reducer) 16 | const App = () => ( 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | const app = mount() 24 | 25 | expect(app.html()).toBe('1,000.95') 26 | expect(store.getState().intl.locale).toBe('en') 27 | 28 | store.dispatch(updateIntl({ locale: 'fr-FR' })) 29 | // expect(app.html()).toBe('1 000,95') 30 | 31 | store.dispatch(updateIntl({ locale: 'en-GB' })) 32 | expect(app.html()).toBe('1,000.95') 33 | }) 34 | -------------------------------------------------------------------------------- /__tests__/lib.js: -------------------------------------------------------------------------------- 1 | import { IntlProvider, intlReducer, updateIntl } from '../src/' 2 | 3 | test('exports', () => { 4 | expect(IntlProvider).toBeTruthy() 5 | expect(intlReducer).toBeTruthy() 6 | expect(updateIntl).toBeTruthy() 7 | }) 8 | -------------------------------------------------------------------------------- /__tests__/reducer.js: -------------------------------------------------------------------------------- 1 | import { initialState, intlReducer, UPDATE } from '../src/' 2 | 3 | test('initialState should default to en', () => { 4 | expect(initialState).toEqual({ 5 | locale: 'en', 6 | messages: {} 7 | }) 8 | }) 9 | 10 | test('intlReducer should set initial state', () => { 11 | expect(intlReducer(undefined, {})).toEqual(initialState) 12 | }) 13 | 14 | test('intlReducer can update state', () => { 15 | const payload = { 16 | locale: 'it', 17 | messages: {} 18 | } 19 | const action = { 20 | type: UPDATE, 21 | payload 22 | } 23 | expect(intlReducer(undefined, action)).toEqual(payload) 24 | }) 25 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 4 8 | } 9 | } 10 | ], 11 | '@babel/preset-react' 12 | ], 13 | plugins: [ 14 | '@babel/transform-runtime', 15 | '@babel/plugin-transform-shorthand-properties', 16 | '@babel/plugin-proposal-export-default-from' 17 | ], 18 | env: { 19 | production: { 20 | plugins: [ 21 | [ 22 | 'transform-react-remove-prop-types', 23 | { 24 | mode: 'wrap' 25 | } 26 | ] 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/initial-locale/README.md: -------------------------------------------------------------------------------- 1 | Run this example. 2 | 3 | ```bash 4 | npm install 5 | npm start 6 | open http://localhost:3000/ 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/initial-locale/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "initial-locale-example", 4 | "version": "2.0.0", 5 | "scripts": { 6 | "prepare": "del node_modules/react-intl-redux/node_modules", 7 | "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start", 8 | "build": "react-scripts build" 9 | }, 10 | "dependencies": { 11 | "react": "^16.12.0", 12 | "react-dom": "^16.12.0", 13 | "react-intl": "^3.6.0", 14 | "react-intl-redux": "file:../..", 15 | "react-redux": "^7.1.3", 16 | "react-scripts": "3.2.0", 17 | "redux": "^4.0.4" 18 | }, 19 | "devDependencies": { 20 | "del-cli": "^3.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/initial-locale/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Intl Redux Example - initial-locale 8 | 9 | 10 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/initial-locale/src/index.js: -------------------------------------------------------------------------------- 1 | /* globals document */ 2 | import { FormattedMessage } from 'react-intl' 3 | import { createStore, combineReducers } from 'redux' 4 | import { IntlProvider, intlReducer } from 'react-intl-redux' 5 | import { Provider } from 'react-redux' 6 | import React from 'react' 7 | import ReactDOM from 'react-dom' 8 | 9 | const reducer = combineReducers({ 10 | intl: intlReducer 11 | }) 12 | const initialState = { 13 | intl: { 14 | defaultLocale: 'zh', 15 | locale: 'it', 16 | messages: { 17 | 'app.greeting': 'Ciao!' 18 | } 19 | } 20 | } 21 | const store = createStore(reducer, initialState) 22 | 23 | const App = () => ( 24 | 25 | 26 |

27 | 28 |

29 |
30 |
31 | ) 32 | 33 | ReactDOM.render(, document.getElementById('root')) 34 | -------------------------------------------------------------------------------- /examples/multiple-languages/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /examples/multiple-languages/README.md: -------------------------------------------------------------------------------- 1 | Run this example. 2 | 3 | ```bash 4 | npm install 5 | npm start 6 | open http://localhost:3000/ 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/multiple-languages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "multiple-languages-example", 4 | "version": "2.0.0", 5 | "scripts": { 6 | "prepare": "del node_modules/react-intl-redux/node_modules", 7 | "start": "react-scripts start", 8 | "build": "react-scripts build" 9 | }, 10 | "dependencies": { 11 | "intl": "^1.2.5", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.12.0", 14 | "react-dom": "^16.12.0", 15 | "react-intl": "^3.6.0", 16 | "react-intl-redux": "file:../..", 17 | "react-redux": "^7.1.3", 18 | "react-scripts": "3.2.0", 19 | "redux": "^4.0.4" 20 | }, 21 | "devDependencies": { 22 | "del-cli": "^3.0.0", 23 | "redux-devtools": "^3.5.0", 24 | "redux-devtools-dock-monitor": "^1.1.3", 25 | "redux-devtools-log-monitor": "^1.4.0" 26 | }, 27 | "browserslist": { 28 | "development": [ 29 | "last 2 chrome versions", 30 | "last 2 firefox versions", 31 | "last 2 edge versions" 32 | ], 33 | "production": [ 34 | ">1%", 35 | "last 4 versions", 36 | "Firefox ESR", 37 | "not ie < 11" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/multiple-languages/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Intl Redux Example - multiple-languages 8 | 9 | 10 | 11 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/multiple-languages/src/components/Greeting.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { FormattedMessage } from 'react-intl' 4 | 5 | const Greeting = () => ( 6 |

7 | 8 |

9 | ) 10 | 11 | export default connect()(Greeting) 12 | -------------------------------------------------------------------------------- /examples/multiple-languages/src/components/SwitchLocale.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { updateIntl } from 'react-intl-redux' 4 | 5 | import store from '../store' 6 | 7 | const SwitchLocale = connect(state => ({ 8 | currentLocale: state.intl.locale, 9 | locales: state.locales, 10 | }))(({ currentLocale, locales }) => ( 11 | 26 | )) 27 | 28 | export default SwitchLocale 29 | -------------------------------------------------------------------------------- /examples/multiple-languages/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | import { IntlProvider } from 'react-intl-redux' 3 | import { Provider } from 'react-redux' 4 | import React from 'react' 5 | import ReactDOM from 'react-dom' 6 | 7 | import Greeting from './components/Greeting' 8 | import SwitchLocale from './components/SwitchLocale' 9 | import store, { DevTools } from './store' 10 | 11 | const UPDATE_LOCALES = 'UPDATE_LOCALES' 12 | 13 | class App extends React.Component { 14 | handleLoadlLocales = () => { 15 | store.dispatch({ 16 | type: UPDATE_LOCALES, 17 | payload: { 18 | en: { 19 | 'app.greeting': 'Hello!', 20 | }, 21 | it: { 22 | 'app.greeting': 'Ciao!', 23 | }, 24 | zh: { 25 | 'app.greeting': '你好!', 26 | }, 27 | }, 28 | }) 29 | } 30 | 31 | render() { 32 | return ( 33 | 34 | 35 |
36 | 37 |

38 | {' '} 41 | 42 |

43 | 44 |
45 |
46 |
47 | ) 48 | } 49 | } 50 | 51 | ReactDOM.render(, document.getElementById('root')) 52 | -------------------------------------------------------------------------------- /examples/multiple-languages/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers } from 'redux' 2 | import { intlReducer } from 'react-intl-redux' 3 | import React from 'react' 4 | 5 | import { createDevTools } from 'redux-devtools' 6 | import DockMonitor from 'redux-devtools-dock-monitor' 7 | import LogMonitor from 'redux-devtools-log-monitor' 8 | 9 | const UPDATE_LOCALES = 'UPDATE_LOCALES' 10 | 11 | function localesReducer(state = {}, action) { 12 | switch (action.type) { 13 | case UPDATE_LOCALES: 14 | return { 15 | ...state, 16 | ...action.payload, 17 | } 18 | default: 19 | return state 20 | } 21 | } 22 | 23 | const reducer = combineReducers({ 24 | intl: intlReducer, 25 | locales: localesReducer, 26 | }) 27 | 28 | export const DevTools = createDevTools( 29 | 34 | 35 | 36 | ) 37 | 38 | const store = createStore(reducer, {}, DevTools.instrument()) 39 | 40 | export default store 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-intl-redux", 3 | "version": "2.4.1", 4 | "description": "Redux binding for React Intl", 5 | "main": "lib", 6 | "side-effects": false, 7 | "scripts": { 8 | "build": "cross-env NODE_ENV=production babel src --out-dir lib", 9 | "clean": "del lib coverage", 10 | "lint": "eslint --ignore-path .gitignore --ext js,md .", 11 | "prepublish": "run-s clean build", 12 | "test": "jest" 13 | }, 14 | "peerDependencies": { 15 | "@babel/runtime": "^7.17.9", 16 | "prop-types": "^15.8.1", 17 | "react": "^16.12.0 || ^17.0.2 || ^18.0.0", 18 | "react-intl": "^2.2.2 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", 19 | "react-redux": "^5.0.1 || ^6.0.0 || ^7.0.0 || ^8.0.0" 20 | }, 21 | "dependencies": { 22 | "@babel/runtime": "^7.17.9", 23 | "prop-types": "^15.8.1" 24 | }, 25 | "devDependencies": { 26 | "@babel/cli": "^7.17.10", 27 | "@babel/core": "^7.17.10", 28 | "@babel/plugin-proposal-export-default-from": "7.16.7", 29 | "@babel/plugin-syntax-dynamic-import": "7.8.3", 30 | "@babel/plugin-syntax-import-meta": "7.10.4", 31 | "@babel/plugin-transform-runtime": "^7.17.10", 32 | "@babel/plugin-transform-shorthand-properties": "^7.16.7", 33 | "@babel/preset-env": "^7.17.10", 34 | "@babel/preset-react": "^7.16.7", 35 | "@babel/register": "^7.17.7", 36 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.7", 37 | "babel-jest": "^28.1.0", 38 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 39 | "cross-env": "^7.0.3", 40 | "del-cli": "^4.0.1", 41 | "enzyme": "^3.11.0", 42 | "eslint": "^8.15.0", 43 | "eslint-config-concise": "^0.40.1", 44 | "eslint-config-concise-esnext": "^0.40.1", 45 | "eslint-config-concise-jest": "^0.40.1", 46 | "eslint-config-concise-react": "^0.40.1", 47 | "eslint-plugin-react": "^7.29.4", 48 | "husky": "^3.1.0", 49 | "immutable": "^4.0.0", 50 | "intl": "^1.2.5", 51 | "jest": "^28.1.0", 52 | "jest-environment-jsdom": "^28.1.0", 53 | "jsdom": "^19.0.0", 54 | "lint-staged": "^9.5.0", 55 | "npm-run-all": "^4.1.5", 56 | "prettier-eslint-cli": "^5.0.1", 57 | "react": "^17.0.2", 58 | "react-dom": "^17.0.2", 59 | "react-intl": "^6.4.4", 60 | "react-redux": "^8.0.1", 61 | "react-test-renderer": "^17.0.2", 62 | "redux": "^4.2.0", 63 | "redux-immutable": "^4.0.0" 64 | }, 65 | "repository": "ratson/react-intl-redux", 66 | "license": "MIT", 67 | "files": [ 68 | "*.md", 69 | "LICENSE", 70 | "lib", 71 | "src" 72 | ], 73 | "keywords": [ 74 | "react", 75 | "reactjs", 76 | "react-intl", 77 | "react-redux", 78 | "intl", 79 | "i18n", 80 | "redux" 81 | ], 82 | "browserslist": [ 83 | "last 2 versions", 84 | "Android > 2", 85 | "last 2 ChromeAndroid versions" 86 | ], 87 | "jest": { 88 | "testEnvironment": "jsdom" 89 | }, 90 | "eslintConfig": { 91 | "extends": [ 92 | "concise", 93 | "concise-esnext", 94 | "concise-jest", 95 | "concise-react" 96 | ], 97 | "rules": { 98 | "comma-dangle": "off", 99 | "default-param-last": "off", 100 | "react/jsx-curly-newline": "off" 101 | }, 102 | "settings": { 103 | "react": { 104 | "version": "detect" 105 | } 106 | } 107 | }, 108 | "husky": { 109 | "hooks": { 110 | "pre-commit": "lint-staged" 111 | } 112 | }, 113 | "lint-staged": { 114 | "*.js": [ 115 | "eslint --fix", 116 | "git add" 117 | ] 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/components/IntlProvider.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { IntlProvider } from 'react-intl' 3 | 4 | function defaultSelector(state) { 5 | const { intl } = state 6 | return { 7 | key: intl.locale, 8 | ...intl 9 | } 10 | } 11 | 12 | const mapStateToProps = (state, { intlSelector = defaultSelector }) => 13 | intlSelector(state) 14 | 15 | export default connect(mapStateToProps)(IntlProvider) 16 | -------------------------------------------------------------------------------- /src/components/Provider.js: -------------------------------------------------------------------------------- 1 | import { Provider as ReduxProvider } from 'react-redux' 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | import IntlProvider from './IntlProvider' 6 | 7 | const Provider = ({ store, children }) => 8 | 9 | 10 | {children} 11 | 12 | 13 | 14 | Provider.propTypes = { 15 | children: PropTypes.element.isRequired, 16 | } 17 | 18 | export default Provider 19 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as IntlProvider } from './components/IntlProvider'; 2 | export { default as Provider } from './components/Provider'; 3 | 4 | export const UPDATE = '@@intl/UPDATE' 5 | 6 | export const updateIntl = ({ locale, formats, messages }) => ({ 7 | type: UPDATE, 8 | payload: { locale, formats, messages }, 9 | }) 10 | 11 | export const initialState = { 12 | locale: 'en', 13 | messages: {}, 14 | } 15 | 16 | export function intlReducer(state = initialState, action) { 17 | if (action.type !== UPDATE) { 18 | return state 19 | } 20 | 21 | return { ...state, ...action.payload } 22 | } 23 | --------------------------------------------------------------------------------