Apr 3, 2019

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.

About the Author

Jeff Sheets profile.

Jeff Sheets

VP - Technology

Jeff has developed Java, Groovy, Grails, and Javascript web apps for industries as varied as Defense, Energy, Weather, Insurance, and Telecom. He is a co-organizer of the Omaha Java Users Group. Jeff has worked on Grails projects since the Grails 1.3.x days, and has experience with production Groovy code as well as Spock tests and Gradle builds. His latest focus has been on AngularJS and Spring Boot applications using JHipster. Jeff also enjoys volunteering at local CoderDojo events to teach programming to our next generation.

One thought on “Translate Create React App with React-Intl

  1. Eagle says:

    Thanks a lot! This saved a ton of time.

  2. Simo says:

    Usefull for FormattedMessage component..
    How can i use react-intl without a FormattedMessage? For placeholders or other messages to display without FormattedMessage?

    1. Jeff Sheets says:

      @Simo, I believe you can also use `intl.formatMessage()` method and the tags will be pulled out of there too. Sorry I missed your comment a long time ago!

Leave a Reply

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

Related Blog Posts
Android Development for iOS Developers
Android development has greatly improved since the early days. Maybe you tried it out when Android development was done in Eclipse, emulators were slow and buggy, and Java was the required language. Things have changed […]
Add a custom object to your Liquibase diff
Adding a custom object to your liquibase diff is a pretty simple two step process. Create an implementation of DatabaseObject Create an implementation of SnapshotGenerator In my case I wanted to add tracking of Stored […]
Keeping Secrets Out of Terraform State
There are many instances where you will want to create resources via Terraform with secrets that you just don’t want anyone to see. These could be IAM credentials, certificates, RDS DB credentials, etc. One problem […]
Validating Terraform Plans using Open Policy Agent
When developing infrastructure as code using terraform, it can be difficult to test and validate changes without executing the code against a real environment. The feedback loop between writing a line of code and understanding […]