Unleash the hidden superpowers of react-intl (Part 1)

Jan Mühlemann
3 min readAug 21, 2018

--

Part 1: How to beautify the react.js markup for your translations using babel macros

Let images speak to see what you will get:

We do not like this clutter:

const name = 'John Doe';<FormattedMessage
id="welcome"
defaultMessage={`Hello {name}!`}
values={{ name: <b>{name}</b> }}
/>

While react-intl works awesome having those FormattedMessage everywhere does not really help keeping your code nice and readable. Wouldn’t it be nicer you just could write:

<FormattedMessage id=”welcome”>Hello <b>{name}</b>!</FormattedMessage>

babel macros to the rescue

You could easily write a babel plugin to transform the above simplified markup to the needed react-intl FormattedMessage. But there is a better way using Kent C. Dodds babel macros (https://github.com/kentcdodds/babel-plugin-macros). There comes a big advantage with using macros (beside they are super simple to create) you can use those with applications created with create-react-app without ejecting (on time of writing needs an alpha build v2.x.x).

A babel macro is basically just a function that gets a reference of the nodes using the macro:

const { createMacro } = require('babel-plugin-macros')

module.exports = createMacro(myMacro)

function myMacro({references, state, babel}) {
// the FormattedMessage imported in below code snippet
const { FormattedMessage = [] } = references;
// transform each occurance
FormattedMessage.forEach(referencePath => { /* transform */ });
}

We won’t go through all the details of the macro used to transform the simpler markup — but you can checkout the code here:

https://github.com/locize/locize-react-intl-example/blob/master/src/intl.macro.js

You can use the macro like:

// import the macro component
import { FormattedMessage } from "./intl.macro";
// and use it
<FormattedMessage id=”welcome”>Hello <b>{name}</b>!</FormattedMessage>

The macro will transform it to the react-intl FormattedMessage:

<FormattedMessage
id="welcome"
defaultMessage={`Hello {name}!`}
values={{ name: <b>{name}</b> }}
/>

Plurals and Select

We could add some more macro magic to also simpler plurals and selects:

import { Select, Plural } from "./intl.macro";// select
<FormattedMessage
id="avoid_bugs"
defaultMessage="{gender,select,he{He avoids bugs.}she{She avoids bugs.}other{They avoid bugs.}}"
values={{ count: 10 }}
/>
// will become
<Select
id="avoid_bugs"
switch={gender}
male="He avoids bugs."
female="She avoids bugs."
other="They avoid bugs."
/>
// plurals
<FormattedMessage
id="items_count"
defaultMessage="{count,plural,=0{There is no item.}one{There is # item.}other{There are # items.}}"
values={{ count: 10 }}
/>
// will become:
<Plural
id="items_count"
count={itemsCount1}
$0="There is no item."
one="There is # item."
other="There are # items."
/>

For more options (like nesting components) have a look at the sample usages:

https://github.com/locize/locize-react-intl-example/blob/master/src/ComponentUsingMacro.js

So in this first part you learnt how you can use the babel-plugin-macros module to get your markup to be more readable as compared to just use the bare FormattedMessage provided by react-intl.

Don’t forget you can build upon the sample here:

https://github.com/locize/locize-react-intl-example

In the second part coming soon you will learn how to extend your react.js project using react-intl to provide internationalization to enable a simple continuous localization lifecycle that is also suitable for large projects needing to split translations into multiple files.

Hope you liked the first part — stay tuned for the second.

--

--

Jan Mühlemann

Always in search for the holy grail ;)...loving javascript, node.js, html5, interaction design, usability