ratson / react-intl-redux

Redux binding for React Intl.
MIT License
298 stars 48 forks source link

how to dispatch updateIntl correctly? #38

Closed benjazehr closed 7 years ago

benjazehr commented 7 years ago

I am building my first redux application and use react-intl-redux for translations. I want to use the language of the browser as a default locale and offer buttons to switch the language.

So i have two questions:

1. Where do I best read the browser language and set the locale accordingly?

My Application has the following structure src/actions/index.js, src/reducers/index.js, src/stores/index.js So what works for me now is in stores/index.js:

/* localisation */
import de from '../assets/locales/de.json'
import en from '../assets/locales/en.json'

const possibleLanguage = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage
const stripRegionCode = possibleLanguage.toLowerCase().split(/[_-]+/)[0]
const locale = stripRegionCode || possibleLanguage || 'en'
const messages = { 'en': en, 'de': de }

const initialIntlState = {
  intl: {
    defaultLocale: locale,
    locale: locale,
    messages: messages[locale]
  }
}

Is this a good idea?

and 2. how do i dispatch updateIntl when a button is clicked?**

In my src/modules/App.js I would like to offer a button to switch languages:

import en from '../assets/locales/en.json'
...
handleClick () {
  const locale = 'en'
  const messages = en
  store.dispatch(updateIntl({
    locale,
    messages
  }))
}

But now I get the following error: 'store' is not defined

So am I dispatching the action at the wrong place / in the wrong manner?

Maybe this is of importance: In my src/modules/AppContainer i use mapDispatchToProps like this:

const mapDispatchToProps = dispatch => (
  bindActionCreators(actions, dispatch)
)

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AppContainer)
benjazehr commented 7 years ago

I found the answer for question 2:

AppContainer.js

import { updateIntl } from 'react-intl-redux'
...
const mapDispatchToProps = dispatch => (
  bindActionCreators({...actions, updateIntl}, dispatch)
)

App.js

import de from '../../assets/locales/de.json'
import en from '../../assets/locales/en.json'
...
<button
  className='Navigation-langSwitch-button'
  onClick={() => this.props.updateIntl({locale: 'de', messages: de})}
>
  DE
</button>
<button
  className='Navigation-langSwitch-button'
  onClick={() => this.props.updateIntl({locale: 'en', messages: en})}
>
  EN
</button>
jkomusin commented 7 years ago

Hi Angelo, I think you are doing things properly at this point. It is a fine idea to initialize your store the way you have done, passing your initialIntlState to Redux's createStore().

There are some caveats to using the navigator.language API, as outlined: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages Namely, it is fairly new and will not be supported in all browsers. Depending on your target audience, you may or may not be okay with that.

The most widely-supported method for retrieving a visitor's preferred locales is via their HTTP Accept-Language header. Here is a great article by W3C on detecting a user's preferred locale(s) via Accept-Language: https://www.w3.org/International/questions/qa-accept-lang-locales There are a fair number of npm modules out there for parsing and using the contents of Accept-Language.

benjazehr commented 7 years ago

Thank you very much for your help!