@everydayhero/translated-components
v7.1.2
Published
Pass i18n translated strings as props to components
Downloads
41
Readme
Translated-Components
Pass translated strings as props to components through higher-order components. Compatible with redux and allows language changes to propogate through shouldComponentUpdate
barriers via the excellent react-broadcast
.
How to use
Five simple steps:
First, install via npm i -S translated-components
or yarn add translated-components
.
Next, string template parsing is handled via the intl-messageformat library, which depends on the browser's global I18n API. It's likely you'll need to polyfill this, and the simplest way is to include this script in the head of your rendered HTML:
// index.html <head>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js" type="text/javascript" />
Then wrap everything that you'd like translated in a TranslationProvider
component, which you pass the currently selected language, and optionally define your default language to use as a fallback for missing translations (if not provided, the default language used is 'en_US'). Example:
// App.js
render(
<TranslationProvider language='en_GB' defaultLanguage='en_US'>
<App />
</TranslationProvider
, document.findElementById('app'))
Next, create a translations object with your supported languages as the primary keys. Two things worth noting: the keys you use for your string templates should map to the props expected by your component, and; your default language should contain the full suite of strings. Example:
// component/translations.js
export default {
en_US: {
title: 'Howdy partner',
subtitle: '(this is how we say hello)'
},
en_GB: {
title: '\'Ello gov\'na'
}
}
Finally, export your component wrapped in translations!
// component/index.js
import translated from 'translated-component'
import translations from './translations'
const Title = ({title, subtitle}) => (
<div>
<h1>{title}</h1>
<h4>{subtitle}</h4>
</div>
)
export default translated({translations})(Title)
...js
<Title />
/* -> if current language is set to 'en_GB':
<div>
<h1>'Ello gov'na</h1>
<h4>(this is how we say hello)</h4>
</div>
*/
Advanced usage
Using props as params in template strings
Any props passed to your component are accessible to be referenced as template params in your translation strings.
const translations = {
en_US: {
title: 'Welcome {name}, have some {food}!'
}
}
const Title = ({title}) => <h1>{title}</h1>
export default translated({translations})(Title)
...
<Title name='Mary' food='cake' />
// -> <h1>Welcome Mary, have some cake!</h1>
Custom params for template strings
You can specify arbitrary template params by passing in a params object to translated
. Each param must be a function which will be passed the component's props, and returns a string:
const translations = {
en_US: {
offer: 'It sure is {temperature}, would you like some {drink}?'
}
}
const params = {
drink: ({temperature}) => temperature === 'hot' ? 'beer' : 'cocoa'
}
const Offer = ({offer}) => <p>{offer}</p>
export default translated({translations, params})(Offer)
...
<Offer temperature='cold' />
// -> <p>It sure is cold, would you like some cocoa?</p>
Controlling how translations are passed to components
Sometimes, you may have translations that should only conditionally be passed as props, or which need to be formatted differently. You can control how the translations are passed to your component with a mapTranslationsToProps
function, which is passed all translated strings, and the component's props, and must return an object.
const translations = {
en_US: {
step_1: 'Steal underpants',
step_2: '...',
step_3: 'Profit!'
}
}
const mapTranslationsToProps = (translations, {steps}) => ({
steps: steps.map((step) => translations[step])
})
const Plan = ({steps}) => (
<ul>{steps.map((step) => <li>{step}</li>)}</ul>
)
export const translated({translations, mapTranslationsToProps})(Plan)
...
<Plan steps={['step_1', 'step_2']} />
/* ->
<ul>
<li>Steal underpants</li>
<li>...</li>
</ul>
*/
Overriding the language set by the Provider
In rare cases, you may want to render a specific component with a different set of translations to the rest of your application. For example, you may want to change the localised labels of an Address fieldset when you change the selected country for that address.
The translated
HOC will use a locally set language
prop in preference to the one set by your
TranslationProvider
.
For example:
const translations = {
en_US: {
fieldOne: 'One',
fieldTwo: 'Two'
},
en_NZ: {
fieldOne: 'NZOne',
fieldTwo: 'NZTwo'
}
}
const MyWidget = (props) => (
<div>
{fieldOne}
{fieldTwo}
</div>
)
const TranslatedWidget = translated({translations})(MyWidget)
<TranslatedWidget /> // <div>OneTwo</div>
<TranslatedWidget language='en_NZ' /> // <div>NZOneNZTwo</div>
Displaying money
This lib comes with a built-in money
number format, which you can use to display currency correctly for the language provided:
const translations = {
en_US: {
label: 'Buy now for {price, number, money}'
}
}
const BuyButton = ({label}) => <button>{label}</button>
export default translated({translations})(BuyButton)
...
<BuyButton price={14.9536} />
// -> <button>Buy now for $14.95</button>
User-defined number/date/time formats
Finally, you can pass in custom configuration for intl-messageformat, to define how params are parsed. For example, if this lib didn't provide a money format (which does this for you), you could create a new way to define how money is displayed like so:
const translations = {
en_US: {
total: 'Your purchase comes to {price, number, USD}'
}
}
const format = {
number: {
USD: {
style: 'currency',
currency: 'USD'
}
}
}
const Total = ({total}) => <p>{total}</p>
export default translated({translations, format})(Total)
...
<Total price={9543.235} />
// -> <p>Your purchase comes to $9,543.23</p>