Let images speak to see what you will get:
We do not like this clutter:
1 | const name = 'John Doe'; |
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:
1 | <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:
1 | const { createMacro } = require('babel-plugin-macros') |
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:
1 | // import the macro component |
The macro will transform it to the react-intl FormattedMessage:
1 | <FormattedMessage |
Plurals and Select
We could add some more macro magic to also simpler plurals and selects:
1 | import { Select, Plural } from "./intl.macro"; |
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
Now after adopting all the features of the demo project you will be able to:
- split translations into multiple files
- using an in-context editor
- loading translations from CDN
- detecting the user language
- automatically add new missing strings to your translation project
- updating changed strings in reference language
- submitting the description to translation context
- setting last used information so you can safely remove keys not used any longer
From internationalization to localization
While the first part focused on helping developers writing nicer jsx markup we now will focus on helping your translators by making their work easier. One of the most important things in our fast-paced times is implementing a fast feedback loop, which is also recommended by all the common “Agile” methodologies.
So let’s go a step further:
From internationalization to continuous localization
We like to innovate the localization process by breaking the old slow loop of exporting extracted translations, passing them to translators and importing them back again. This old process does not fit into a modern development environment using continuous development and agile methods.
What we want: New content in your application should be immediately available in your translation management tool for your translators and newly finished translations should be passed down to the application without a developer needing to add a file to the repository or accepting a PR from the translation management.
Connecting your application to your translation management
In this sample we will use https://locize.com as our translation management tool — as it exposes all the needed functionality to bring localization to the level of continuous localization.
There is the locizer script available to connect your application with the translation project: https://github.com/locize/locizer
With it you can load translation files, save or update translation segments.
Step 1: We need a way to extend react-intl components during development
This should be rather easy:
1 | import { FormattedMessage as FM } from 'react-intl'; |
Now let’s learn what the supportLocize() is:
Step 2: Let the component send new content to the translation service
In our code we have FormattedMessages like in regular react-intl projects but we consume our overridden component during development:
1 | // see ./locize/index.js in development mode the |
Our extended component will take those props and create new translation segments on locize using the id and defaultMessage
1 | // a hoc to extend components with locize features |
Based on the defaultMessage already exists in the translations catalog or not, or the defaultMessage has changed we use the locizer functions to create or update the translation segment in the source language.
Awesome, now every time we add a new FormattedMessage
or change a defaultMessage
those values get added or changed in your translation project immediately.
Step 3: Directly load translations
By overriding the IntlProvider
we could use the locizer script to directly load the translations that get published to the localization CDN provided by locize.
What the component basically does is taking a prop namespace to load the translation file and the needed intl locale-data:
1 | // load the given file form locize |
The full code is very simple you can find the demo repository here: https://github.com/locize/locize-react-intl-example/blob/master/src/locize/index.js#L32
So this not only enables us to load one translation catalog, but by using that IntlProvider we could load multiple files and making translation a lot easier by splitting them into multiple smaller files.
Step 4: Enable translations inside the context of your application
The integration of the locize editor is very simple:
1 | locizeEditor.init({ |
Now you can open your website appending the querystring param ?locize=true
and you will see the in-context editor.
Summary
You see internationalization is done rather easily and localization hasn’t to be harder. With the right tools you can not only keep your development — translation cycle short but you can also improve the quality, save time and money.
You can find the full sample here: https://github.com/locize/locize-react-intl-example
Take the chance and try it yourself locize.com comes with a 14 day free trial.