A dead simple way to add complex translations in a React project πππ
- π₯ 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)
Write this:
<Translate
text="{difficulty} *translations* in React <ReactLogo>"
data={{ difficulty: 'Simple' }}
renderMap={{
renderReactLogo: () => <ReactLogo size={14} />,
}}
/>
To render this:
React DOM and React Native π₯
Play around with the library in your browser through the CodeSandbox.
Whatever floats your boat:
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.
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.
That is all!
Continue reading below to see how to handle the various translation scenarios.
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 /*...*/ />
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.
- Static text
- Templated text
- Dynamically templated text
- Styled text
- Component within text
- Translated text as string (for text input placeholders)
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:
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:
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:
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:
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:
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:
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.
- Add tests using Jest
Licensed under MIT.
Β© 2019 Amsul