Skip to content

A dead simple way to add complex translations (i18n) in a React (DOM/Native) project 🌎🌍🌏

License

Notifications You must be signed in to change notification settings

amsul/react-translated

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Feb 4, 2020
21ea6eb Β· Feb 4, 2020

History

93 Commits
Dec 12, 2017
Jan 15, 2018
Jan 23, 2019
Jan 29, 2020
Nov 22, 2018
Dec 2, 2017
Dec 1, 2017
Aug 16, 2018
Dec 2, 2017
Dec 12, 2017
Dec 12, 2017
Dec 12, 2017
Dec 12, 2017
Dec 12, 2017
Feb 16, 2019
Feb 4, 2020
Jan 15, 2018
Feb 16, 2019

Repository files navigation

react-translated

A dead simple way to add complex translations in a React project 🌎🌍🌏

Features

  • πŸ’₯ Data interpolation
  • β˜„ Component interpolation
  • β“‚ Markdown inline-manipulations
  • πŸ”€ Custom manipulations, pluralizations, and grammar rules based on input-data
  • βš› Component-level translation files (enables loading only required translations)

Example

Write this:

<Translate
  text="{difficulty} *translations* in React <ReactLogo>"
  data={{ difficulty: 'Simple' }}
  renderMap={{
    renderReactLogo: () => <ReactLogo size={14} />,
  }}
/>

To render this:

Support

React DOM and React Native πŸ”₯

Try

Play around with the library in your browser through the CodeSandbox.


Table of Contents


Installation

Whatever floats your boat:

  • Yarn: yarn add react-translated
  • npm: npm install react-translated

Setup

Step 1: Create the translations file

Create a file that will contain a mapping of keys to the string in each language you support.

To keep things simple, use the strings of your default language as the key:

// translation.js

export default {
  'Hi, World!': {
    en: 'Hi, World!',
    fr: 'Bonjour le monde!',
  },
  // ...
}

NOTE: There is no enforcement on the key used for a language. In these examples, 2-digit country codes (en, fr, etc) are used. Decide on a convention and use that for all translations.

Step 2: Connect the Provider

Wrap your top-level component with the <Provider> and set the translation and language props:

// index.js

import { Provider } from 'react-translated'
import translation from './translation'
const App = (
  <Provider language="en" translation={translation}>
    <MyApplicationRoot />
  </Provider>
)

NOTE: The value of the language prop must be one of the keys used for a language defined in Step 1.

Step 3: Start translating

That is all!

Continue reading below to see how to handle the various translation scenarios.


Usage

The library can be imported in whatever way you find suitable:

import ReactTranslated from 'react-translated'
import * as ReactTranslated from 'react-translated'

<ReactTranslated.Translate /*...*/ />

Or:

import { Provider, Translate, Translator } from 'react-translated'

<Translate /*...*/ />

Translate vs Translator

The Translate component should always be used when the translation is rendered as a child component; such as buttons, paragraphs, headings, etc.

The Translator component should only be used when the translation is needed as a string; such as placeholders, alternate text, etc.

Translation scenarios


Static text

This is pretty self-explanatory:

// translation.js
export default {
  'Hi, World!': {
    en: 'Hi, World!',
    fr: 'Bonjour le monde!',
  },
}

// any component file
<Translate text='Hi, World!' />

Renders as:

Templated text

To use dynamic text, the text can be templated:

// translation.js
export default {
  'Hi, {firstName}!': {
    en: 'Hi, {firstName}!',
    fr: 'Salut {firstName}!',
  },
}

// any component file
<Translate
  text='Hi, {firstName}!'
  data={{ firstName: 'Sergey' }}
  />

Renders as:

Dynamically templated text

Sometimes just dynamic text is not enough and the template itself needs to be dynamic (for example pluralization). That can be achieved using a function call:

// translation.js
export default {
  'There are {catsCount} cats in this room.': {
    en({ catsCount }) {
      if (catsCount === 1) {
        return 'There is {catsCount} cat in this room.'
      }
      return 'There are {catsCount} cats in this room.'
    },
    // ...
  },
}

// any component file
<Translate
  text='There are {catsCount} cats in this room.'
  data={{ catsCount: 2 }}
  />
<Translate
  text='There are {catsCount} cats in this room.'
  data={{ catsCount: 1 }}
  />

Renders as:

Since these templates are simple function calls, things more complex than pluralization can be done too:

// translation.js
export default {
  'This is a {fruit}': {
    en({ fruit }) {
      if (/^[aeiou]/.test(fruit)) {
        return 'This is an {fruit}'
      }
      return 'This is a {fruit}'
    },
    // ...
  },
}

// any component file
<Translate
  text='This is a {fruit}'
  data={{ fruit: 'banana' }}
  />
<Translate
  text='This is a {fruit}'
  data={{ fruit: 'apple' }}
  />

Renders as:

Styled text

The translated text can also have some basic styling applied:

// translation.js
export default {
  'Hi, *World*!': {
    en: 'Hi, *World*!',
    fr: 'Bonjour *le monde*!',
  },
}

// any component file
<Translate text='Hi, *World*!' />

Renders as:

And of course the same can be done with dynamic templates:

// translation.js
export default {
  'Hi, *{firstName}*!': {
    en: 'Hi, *{firstName}*!',
    fr: 'Salut *{firstName}*!',
  },
}

// any component file
<Translate
  text='Hi, *{firstName}*!'
  data={{ firstName: 'Sergey' }}
  />

Renders as:

Component within text

For more advanced uses where Markdown and Emojis don’t suffice, components can be rendered within the text:

// translation.js
export default {
  'Tap the <StarIcon> to add': {
    en: 'Tap the <StarIcon> to add',
    fr: 'Appuyez sur la <StarIcon> pour ajouter',
  },
}

// any component file
<Translate
  text='Tap the <StarIcon> to add!'
  renderMap={{
    renderStarIcon: () => <StarIcon size={14} />
  }}
  />

Renders as:

Another practical application of this is nested translations - text that requires data that also needs to be translated:

// translation.js
export default {
  'I was born in <MonthName>': {
    en: 'I was born in <MonthName>',
    fr: 'Je suis nΓ© en <MonthName>',
  },
  'August': {
    en: 'August',
    fr: 'aoΓ»t',
  },
}

// any component file
const monthName = 'August'
<Translate
  text='I was born in <MonthName>'
  renderMap={{
    renderMonthName: () => <Translate text={monthName} />
  }}
  />

Renders as:


Translated text as string

Added v2.2.0

In scenarios where the translated text is required as a string, such as with text inputs placeholders or accessibility labels, the Translator can be used:

// translation.js
export default {
  'Enter your age {firstName}': {
    en: 'Enter your age {firstName}',
    fr: 'entrez votre Γ’ge {firstName}',
  },
}

// any component file
<Translator>
  {({ translate }) => (
    <input
      placeholder={translate({
        text: 'Enter your age {firstName}',
        data: { firstName: 'Sergey' },
      })}
      />
  )}
</Translator>

Renders as:


Contributors


amsul

πŸ’» 🎨 πŸ“– πŸ’‘ πŸ”§

Johnson Su

πŸ› πŸ’»

πŸ‘‹ Interested becoming a contributor too?

Awesome! This project follows the all-contributors specification. Contributions of any kind are welcome!

You may also want to take a look at our TODOs below and make sure to give our Contributing guide a read.


TODOs

  • Add tests using Jest

License

Licensed under MIT.

Β© 2019 Amsul