An open API service indexing awesome lists of open source software.

https://github.com/apsl/redux-i18n

A simple and powerful package for translate your react applications using react-redux.
https://github.com/apsl/redux-i18n

Last synced: about 1 year ago
JSON representation

A simple and powerful package for translate your react applications using react-redux.

Awesome Lists containing this project

README

          

# Description

**redux-i18n** is a simple yet powerful package to translate your _react_ applications using _react-redux_.

[![npm version](https://badge.fury.io/js/redux-i18n.svg)](https://www.npmjs.com/package/redux-i18n)
![downloads](https://img.shields.io/npm/dm/redux-i18n.svg)

## Installation

```
npm i redux-i18n --save
```

or

```
yarn add redux-i18n
```

## Features

- Translate literals.
- Pluralize literals.
- Designed for react-redux.
- Compatible with Immutable.js.
- Export translations to POT files (make your translations with [Poedit](https://poedit.net/)).
- Import translations from .PO files to _translations.js_ object (for use in your project).
- Add comments for translators.

## Requirements

- node >= 4.0.0

## Overview

**redux-i18n** offers your app the `t()` function to translate literals.

The `t()` function is available in the components of your app via React [context](https://reactjs.org/docs/context.html). To achieve this you need to wrap your app into the `` component from **redux-i18n** that provides for the context. Furthermore, for all components that want to use the `t()` function you need to define `contextTypes`, e.g.:

```javascript
// import ...
import PropTypes from 'prop-types'

class MyComponent extends React.Component {
render() {
return

{this.context.t('Hello World!')}

}
}

MyComponent.contextTypes = {
t: PropTypes.func
}
```

If `contextTypes` is not defined, then context will be an empty object.

The `t()` function takes up to three arguments `t(textKey [, params, comments])`, where `textKey` is either the string to be translated or --- for pluralization --- an object as defined below.

For setting the language in the redux store **redux-i18n** offers an action creator `setLanguage`.

To manage the translations in your React app, **redux-i18n** supports two choices:

1. load all your translations into a one big JS object
1. load your translations into a slice of your redux store

For the latter **redux-i18n** provides an action function creator `setTranslations`. As `setTranslations` is an action function creator you need to add _redux-thunk_ to your middleware for it to work.

**redux-i18n** supports your store in plain JavaScript structures, but also if it is managed by help of _immutable.js_.

Finally, **redux-i18n** offers scripts to generate a translations object from po files that can be managed in [Poedit](https://poedit.net/).

## Usage

The package provides a parent component to encapsulate your application as well as helpers functions to translate your project.

```javascript
// import ...
import I18n from 'redux-i18n'
// with Immutable.js:
import I18n from 'redux-i18n/immutable'

import { translations } from './translations'

class Root extends React.Component {
render() {
return (





)
}
}
```

Where `translations` is a dictionary similar to this:

```javascript
export const translations = {
es: {
'Translate this text': 'Traduce este texto',
'Hello {n}!': 'Hola {n}!'
}
}
```

You can also set the initial language with the _initialLang_ prop:

```javascript


My Project


{this.props.children}

```

If you have partial translations, this means that you don't have your translations at 100% and you want to show untranslated literals in an other language, you can use the `fallbackLang` prop.

```javascript


My Project


{this.props.children}

```

In this case, if you want to show this translations:

```html

{this.context.t('_hello_')}

```

And this isn't in "de" language, it will show in "en".

## Redux Reducer

The language state is managed in a slice of the store named `i18nState`. Therefore, you have to add the **i18nState** reducer in your `combineReducers`.

```javascript
import { otherreducers } from './Yourproject'

import { i18nState } from 'redux-i18n'
// with Immutable.js:
import { i18nState } from 'redux-i18n/immutable'

const appReducer = combineReducers({
otherreducers,
i18nState
})
```

The current language is contained in the `lang` key of `i18nState`.

The `i18nState` is initially defined as

```javascript
const defaultI18nState = {
lang: 'en',
translations: {},
forceRefresh: false
}

// immutablejs
const defaultI18nState = new Map({
lang: 'en',
translations: {},
forceRefresh: false
})
```

When you [map your state to props with connect](https://github.com/reduxjs/react-redux/blob/master/docs/api/connect.md) you can also access the `lang` attribute in your components:

```javascript
export default connect(state => ({
lang: state.i18nState.lang,
}))(Home)

// with Immutable.js:
export default connect(state => ({
lang: state.getIn(['i18nState', 'lang']),
}))(Home)

```

## Translate literals

You can access the functions of `` using your component's context. For example:

```javascript
Home.contextTypes = {
t: PropTypes.func.isRequired
}
```

...you will then be able to use the `t()` method in your component.

```javascript
render() {
return (


Your current language, is: {this.props.lang}

{this.context.t("Translate this text")}

{this.context.t("Hello {n}!", {n: "World"})}


Change Language

)
}
```

You can also use the `t()` function to change date formats

```javascript
export const translations = {
de: {
'YYYY-MM-DD': 'DD.MM.YYYY'
}
}
```

```javascript
render() {
let today = moment()
return (


{today.format(this.context.t("YYYY-MM-DD"))}

)
}
```

Add comments for translators.

```javascript
render() {
return (


{this.context.t("Translate this text", {},
"This is a comment for translators.")}
{this.context.t("Hello {n}!", {n: "Cesc"},
"This is another comment.")}

)
}
```

Here's how _Poedit_ will show the comments:

![Poedit screenshot](imgs/poedit1.jpg?raw=true 'Poedit screenshot')

### HTML Object as parameter

```javascript
const user = { name: 'World' }
const name = {user.name}
return


```

Result:

```html
Hello Cesc
```

Notice that for [security reasons](https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml) we can't print html code directly, which is why we need to use the "dangerouslySetInnerHTML" method for that.

## Stateless components

Example:

```javascript
const Foo = ({}, context) =>

{context.t('Hello World')}


```

## Pluralize

To use plurals in your translations.

```javascript

{this.context.t(['una noche', '{n} noches', 'n'], { n: 1 })}

```

Pass an array instead of a string as first parameter. The first element is a singular term, the second is the plural form and the last one is an object used to set the quantity.

After extracting the translations to a POT file and opening it with Poedit you will see the following:

![Poedit screenshot](imgs/poedit2.jpg?raw=true 'Poedit screenshot')

Also the `translations` object allows to set an options node. There you can set a plurals form rule and a plurals number. Also, you can suppress warnings logged in console. For example:

```javascript
export const translations = {
es: {
'Translate this text': 'Traduce este texto',
'Hello {n}!': 'Hola {n}!'
},
options: {
plural_rule: 'n > 1',
plural_number: '2',
suppress_warnings: true // defaults to false
}
}
```

When the translations are generated from po import file, this node is created automatically.

_Note_: Versions >=1.5.10 allow to use all existing pluralization rules:
[http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html](http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html)

## Change language

Use the _setLanguage_ action.

```javascript
import {setLanguage} from "redux-i18n"

componentWillMount() {
this.props.dispatch(setLanguage("es"))
}
```

If you work with combined languages like `"es-ES"`, `"en-GB"`, but your translations object doesn't include those properties...

```javascript
export const translations = {
"es": {
...
},
"en": {
...
}
}
```

..._redux-i18n_ will fallback on the closest property. In this case, `"es"` or `"en"`.

## Extract/Import scripts

**redux-i18n** includes a script to extract your translation strings to a _.pot_ template which you can use in _Poedit_, and another to import strings from _po_ files to a `translation.js`.

Add the scripts in your _package.json_ for this purpose:

```json
"scripts": {
"extract": "i18n_extract",
"import": "i18n_import"
}
```

You can then run the following commands in your terminal:

```
npm run extract
npm run import
```

### Extract texts and build template.pot

```
npm run extract
```

By default, this script will search for all literals inside your **src** folder with a regular expression and build a **locales/template.pot** file. This file can then be used in [Poedit](https://poedit.net/) to build _en.po_, _es.po_, etc. files.

If you want to set other source folder, you can use the `--source` switch.

```json
"scripts": {
"extract": "i18n_extract --source=mysourcefolder",
"import": "i18n_import"
}
```

Or if you want to export your locales to a different folder...

```json
"scripts": {
"extract": "i18n_extract --source=mysourcefolder --locales=mylocalesfolder",
"import": "i18n_import"
}
```

By default this command find in all _.js_ and _.jsx_ file extensions, but you can customize it with _fexts_ parameter. Check out this example:

```json
"scripts": {
"extract": "i18n_extract --fexts=js,jsx,coffee",
"import": "i18n_import"
}
```

The default regular expression will search all occurrences of `this.context.t` string, but you can also supply your own custom pattern, as in the following example:

```javascript
export default function App({ aProp, bProp }, { t: translate }) {
return

{translate('Hello world!')}

}
```

You will then need to set the `--pattern` flag in _package.json_:

```json
"scripts": {
"extract": "i18n_extract --pattern=translate",
"import": "i18n_import"
}
```

### Import .po files

When your translators are done translating your terms, you can import your _po_ files running the import script:

```
npm run import
```

This script read all _po_ files inside your _locales_ folder, extract all translations and build a **src/translations.js** that you can then use in your project.

Your _.po_ files must define header language, check [mininal format](https://gist.github.com/Salec/3ddcf037e9cc4c44082441f379bf0165) for more information.

You can also set another _locales_ folder:

```json
"scripts": {
"extract": "i18n_extract --source=mysource --locales=mylocales",
"import": "i18n_import --locales=mylocales"
}
```

Or, save _translation.js_ to a different location:

```json
"scripts": {
"extract": "i18n_extract --source=mysource --locales=mylocales",
"import": "i18n_import --locales=mylocales --translations=myfolder"
}
```

You can also change the encoding for your extraction from PO (default is iso-8859-1)

```json
"scripts": {
"extract": "i18n_extract --source=mysource --locales=mylocales",
"import": "i18n_import --encoding=utf-8"
}
```

## Async translations

When applications grow, translations tend to bigger as well, adding a lot to the overall size of the js bundle.

You can set an empty translations object to the `` component and set the `useReducer` prop to true to use the store as the source of strings. For example:

```javascript



```

Then you can use the `setTranslations` action.

```javascript
import { setTranslations } from 'redux-i18n'
api.get('...').then((r) => this.props.dispatch(setTranslations(r.translations)))
```

You can pass a second parameter to the action to set the language.
Depending on your response's structure, it could look like this:

```javascript
api.get('...').then((r) => this.props.dispatch(setTranslations(r.translations, 'en')))
```

Since version 1.5.1 is possible pass a dictionary as a second param with some options. This allows us set more functionalities to method.

- preserveExisting (bool): If is true, the translations received does merge with existing translations.
- language (string): Language code

Some examples:

```javascript
setTranslations(newTranslations, { preserveExisting: true })
setTranslations({ Hello: 'Hallo' }, { language: 'de' })
```

## InitialState

Sometimes language is set initially by the redux store creation, or in an isomorphic way. In this case, you can set the `initialized` prop to stop the `I18n` provider from dispatching an action.

## HOC

If you want to isolate the use of context from your components, you can import the Localize Hoc to provide the translate function as a prop to your component. For example:

```javascript
import { localize } from 'redux-i18n'

class SomeComponent extends Component {
render() {
return this.props.t('hello world')
}
}

export default localize()(SomeComponent)
```

You can also change the name of the provided prop:

```javascript
import { localize } from 'redux-i18n'

class SomeComponent extends Component {
render() {
return this.props.translate('hello world')
}
}

export default localize('translate')(SomeComponent)
```

---

**Please**, if you like my package, don't forget to rate it. Click on the "star"!