diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 701723713..875486870 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,7 @@ forked repo, check that it meets these guidelines: * [Squash](http://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git) your commits into one for each PR. * Run `npm test` to make sure that your code style is OK and there are no any regression bugs. +* When contributing to an opt-in feature, apply the `[feature/...]` tag as a prefix to your PR title #### Style Guide diff --git a/README.md b/README.md index 9f0be03ec..40037b8ed 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,23 @@ visit our sponsors:

* Follow the [getting started guide](./docs/getting-started.md) to download and run the project * Check the [code recipes](./docs/recipes) used in this boilerplate, or share yours +### Customization + +The `master` branch of React Starter Kit doesn't include a Flux implementation or any other +advanced integrations. Nevertheless, we have some integrations available to you in *feature* +branches that you can use either as a reference or merge into your project: + + * [feature/redux](https://github.com/kriasoft/react-starter-kit/tree/feature/redux) — isomorphic + Redux by [Pavel Lang](https://github.com/langpavel) (see [how to integrate + Redux](./docs/recipes/how-to-integrate-redux.md)) + * [feature/react-intl](https://github.com/kriasoft/react-starter-kit/tree/feature/react-intl) — + isomorphic Redux and React Intl by [Pavel Lang](https://github.com/langpavel) (see [how + to integrate React Intl](./docs/recipes/how-to-integrate-react-intl.md)) + +If you think that any of these features should be on `master`, or vise versa, some features should +removed from the `master` branch, please [let us know](https://gitter.im/kriasoft/react-starter-kit). +We love your feedback! + ### Backers ♥ React Starter Kit? Help us keep it alive by donating funds to cover project diff --git a/docs/README.md b/docs/README.md index a3c529cf7..b314fe324 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,4 +15,6 @@ ### Recipes * [How to Implement Routing and Navigation](./recipes/how-to-implement-routing.md) +* [How to Integrate Redux](./recipes/how-to-integrate-redux.md) +* [How to Integrate React Intl](./recipes/how-to-integrate-react-intl.md) * [How to Integrate Disqus](./recipes/how-to-integrate-disqus.md) diff --git a/docs/recipes/how-to-integrate-react-intl.md b/docs/recipes/how-to-integrate-react-intl.md new file mode 100644 index 000000000..2c9594404 --- /dev/null +++ b/docs/recipes/how-to-integrate-react-intl.md @@ -0,0 +1,122 @@ +## How to Integrate [React Intl](https://github.com/yahoo/react-intl#react-intl) + + 1. Merge `feature/react-intl` branch with git. + Because react-intl integration is built on top of `feature/redux`, you'll also get all the features. + + 2. Adjust `INTL_REQUIRE_DESCRIPTIONS` constant in `tools/webpack.config.js` around line 17: + ```js + const INTL_REQUIRE_DESCRIPTIONS = true; + ``` + When this boolean is set to true, the build will only succeed if a `description` is set for every message descriptor. + + 3. Adjust `locales` settings in `src/config.js`: + ```js + // default locale is the first one + export const locales = ['en-GB', 'cs-CZ']; + ``` + Note that you should follow + [BCP 47](https://tools.ietf.org/html/bcp47) + ([RFC 5646](https://tools.ietf.org/html/rfc5646)). + + 4. Add locale support in `src/client.js`: + ```js + import en from 'react-intl/locale-data/en'; + import cs from 'react-intl/locale-data/cs'; + ... + + [en, cs].forEach(addLocaleData); + ``` + + 5. Execute `npm run extractMessages` or `npm start` to strip out messages. + Message files are created in `src/messages` directory. + + 6. Edit `src/messages/*.json` files, change only `message` property. + + 7. Execute `npm run build`, + your translations should be copied to `build/messages/` directory. + + +### How to write localizable components + +Just import the appropriate [component](https://github.com/yahoo/react-intl/wiki#the-react-intl-module) from `react-intl` + +- For localizable text use +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage). +- You can also use it with +the [`defineMessages()`](https://github.com/yahoo/react-intl/wiki/API#definemessages) helper. + +- For date and time: +[``](https://github.com/yahoo/react-intl/wiki/Components#formatteddate) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedtime) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedrelative) + +- For numbers and currencies: +[``](https://github.com/yahoo/react-intl/wiki/Components#formattednumber) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedplural) + +- If possible, do not use ``, see how to use *Rich Text Formatting* with +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) + +- When you need an imperative formatting API, use the [`injectIntl`](https://github.com/yahoo/react-intl/wiki/API#injectintl) High-Order Component. + +#### Example + +```jsx +import { defineMessages, FormattedMessage, injectIntl, intlShape } from 'react-intl'; + +const messages = defineMessages({ + text: { + id: 'example.text', + defaultMessage: 'Example text', + description: 'Hi Pavel', + }, + textTemplate: { + id: 'example.text.template', + defaultMessage: 'Example text template', + description: 'Hi {name}', + }, +}); + +function Example(props) { + const text = props.intl.formatMessage(messages.textTemplate, { name: 'Pavel'}); + return ( +
+ + + Pavel + }} + /> +
+ ); +} + +Example.propTypes = { + intl: intlShape, +} + +export default injectIntl(Example); +``` + +### Updating translations + +When running the development server, every source file is watched and parsed for changed messages. + +Messages files are updated on the fly. +If a new definition is found, this definition is added to the end of every used `src/messages/xx-XX.json` file so when commiting, new translations will be at the tail of file. + +When an untranslated message is removed and its `message` field is empty as well, the message will be deleted from all translation files. This is why the `files` array is present. + +When editiong a translation file, it should be copied to `build/messages/` directory. + +### Other References + + * [`Intl documentation on MDN`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl) + * [express-request-language](https://github.com/tinganho/express-request-language#readme) + – for more details how initial language negotiation works. diff --git a/docs/recipes/how-to-integrate-redux.md b/docs/recipes/how-to-integrate-redux.md new file mode 100644 index 000000000..ae838e2c0 --- /dev/null +++ b/docs/recipes/how-to-integrate-redux.md @@ -0,0 +1,56 @@ +## How to Integrate [Redux](http://redux.js.org/index.html) + +Merge `feature/redux` branch with Git. If you are interested in `feature/react-intl`, merge that +branch instead as it also includes Redux. + +**If you don't know Redux well, you should [read about it first](http://redux.js.org/docs/basics/index.html).** + + +### Creating Actions + + 1. Go to `src/constants/index.js` and define action name there. + + 2. Go to `src/actions/` and create file with appropriate name. You can copy + `src/actions/runtime.js` as a template. + + 3. If you need async actions, use [`redux-thunk`](https://github.com/gaearon/redux-thunk#readme). + For inspiration on how to create async actions you can look at + [`setLocale`](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/actions/intl.js) + action from `feature/react-intl`. + See [Async Flow](http://redux.js.org/docs/advanced/AsyncFlow.html) for more information on this + topic. + + +### Creating Reducer (aka Store) + + 1. Go to [`src/reducers/`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers) and create new file there. + + You can copy [`src/reducers/runtime.js`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers/runtime.js) as a template. + + - Do not forget to always return `state`. + - Never mutate provided `state`. + If you mutate state, rendering of connected component will not be triggered because of `===` equality. + Always return new value if you perform state update. + You can use this construct: `{ ...state, updatedKey: action.payload.value, }` + - Keep in mind that store state *must* be repeatable by replaying actions on it. + For example, when you store timestamp, pass it into *action payload*. + If you call REST API, do it in action. *Never do this in reducer!* + + 2. Edit [`src/reducers/index.js`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers/index.js), import your reducer and add it to root reducer created by + [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) + + +### Connecting Components + +You can use [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) High-Order Component from [`react-redux`](https://github.com/reactjs/react-redux#readme) package. + +See [Usage With React](http://redux.js.org/docs/basics/UsageWithReact.html) on redux.js.org. + +For an example you can look at +[``](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/components/LanguageSwitcher/LanguageSwitcher.js) +component from `feature/react-intl` branch. It demonstrates both subscribing to store and dispatching actions. + + +### Dispatching Actions On Server + +See source of `src/server.js`