Translate Create React App with React-Intl

The React ecosystem is good at many things, including being able to quickly spin-up a solid dev stack with Create React App. CRA provides a great core set of features (webpack, build scripts, jest tests, etc…) and solid documentation to easily create a production-ready application. The ecosystem is also understandably un-opinionated when it comes to choosing various libraries: like routing, css-in-js, state management, and in our case internationalization. This can leave each project team with an overwhelming array of choices that must be evaluated when setting up i18n.

Internationalization (i18n) is basically language translations — a framework to extract text to easily have different language-packs for the same code base (e.g. English, Spanish, German, etc…). There are three main i18n react libraries to choose from: react-intl (most popular), react-i18next (alternative), and LinguiJS (newest). This post will skip the trade-study and assume you’ve come to the same conclusion as the popular and opinionated React Boilerplate project, to use react-intl.

Great, you have a framework, but now how to configure it without ejecting from Create React App? There are numerous blog posts and stackoverflows and github issues that try to explain how this can work. Some require ejecting from CRA, some require custom scripts per project, and some require complicated non-developer friendly coding patterns. The best post I found comes close but uses a deprecated react-intl-cra framework.

So here are my 4 requirements for i18n to make it simple:

  1. 10 minute setup
  2. Use Create React App without ejecting
  3. Default language inline in the source
  4. Easy i18n key extraction into translation files

For the TL;DR – you can jump straight to the full code gist on github.

⚙️ Install

Install react-intl, react-intl.macro, and react-intl-translations-manager with yarn.

yarn install react-intl
yarn install react-intl.macro
yarn install --dev react-intl-translations-manager

Add a couple of commands to your package.json scripts block:

{
  "scripts": {
    "i18n:extract": "MESSAGE_DIR='./.messages' react-scripts build",
    "i18n:manageTranslations": "node ./translationRunner.js"
  }
}

And add a small ./translationRunner.js file in the project root folder

🌎 Setup react-intl

In your top-level index.js, setup React-Intl to use a default language of en for English. The trick to not having a separate en.json translations file is in the defaultLocale=”en” line. Since that matches the localeProp it will tell react-intl to just use the defaultMessages of each FormattedMessage key.

The beauty is that now when coding you don’t have to put a key in your src and go update a resource bundle to update the text of each component in separate files. The English text is in the component src making the dev workflow fairly simple. Note the magic of importing FormattedMessage from the react-intl.macro library (not from react-intl directly) this is to aid in extracting message id’s later.

Lastly set-up a small src/i18n/locales.js file. Ours has settings for Spanish, but you would add any additional languages here too.

This is all you need to start using i18n while coding! The next steps are only needed down the road you when you are finally ready to extract your keys for translation and deploy to production.

🔍 Extract message id’s

React-intl.macro behind the scenes uses babel-plugin-macros and babel-plugin-react-intl to scan the static source for usages of FormattedMessage. It will then extract all the id’s and defaultMessages into separate .message/*.json files. (Note: add the .message dir to your .gitignore file, as these are just transitional generated throw-away files)

yarn i18n:extract

✍️ Manage Translations

Now you have one simple “yarn i18n:manageTranslations” command to run that will bundle all of those ./message/*.json files into individual language files. For this example we are just translating to Spanish so a single es.json file will be created. The beauty of react-intl-translations-manager is that it will also merge any new keys it finds into your existing es.json, AND tell you about any “Untranslated keys” that are still set to the English default so you know EXACTLY what keys need to be translated!

yarn i18n:manageTranslations

The Spanish src/i18n/translations/es.json output file will look similar to this (notice how it has the default English values still before manually translating). You’ll want to commit this file to your git repo after translating the values:

 {
  "app.title": "Sample App",
  "app.hello.user": "Hello {username}! Welcome!"
 }

Sample translated file might be like:

👋 Wrap Up

There are so many areas of i18n that we didn’t discuss, like localization (l10n), globalization (g11n), pluralization, date formats, etc… There are also performance improvement opportunities like dynamically loading language files on demand, or code-splitting the language files, that could especially be worthwhile if your app supports many languages. Oh and don’t forget to internationalize our Bootstrap, Material-UI, or AntD components too! However hopefully this post can get you up and running quickly, within 10-15 minutes, so you can spend more time writing code and less time worrying about how to extract translation messages in the future.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*