JavaScripting

The definitive source of the best
JavaScript libraries, frameworks, and plugins.


  • ×

    React Translated

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

    • 🔾20%Overall
    • 178
    • 25.2 days
    • 🕩12
    • 👥4

    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

    Show All