Translate your React app with ease using Facebook’s own framework (FBT)
Checkout this demo app using the concepts from this blog post.
This spring (2019) I went to React Europe, a really great conference dedicated to React development. John Watson had a talk there where he introduced an internationalization (i18n) framework called FBT, which is used over at Facebook. I only had prior experience using react-intl, so when the time came to translate my companies’ website I wanted to give FBT a go. This article is going to go through my findings using FBT for the first time and will be show casing examples that you can follow in order to add FBT to your own React application.
🤔 Why should I use FBT?
FBT has a couple of features that stands out compared to other similar frameworks (for example react-intl):
- Translatable text resides inline with the rest of your JSX. This means that you can search for English text strings in your text editor and directly find the React component where it’s used.
- It supports plural variations out of the box.
- It supports enums!
- Adjust translations based on pronouns.
🍖 The meat
Note: the steps below assumes your is using Babel, Webpack and yarn. It also assumes that you are developing in an UNIX environment. If you don’t have an existing app and instead want to start a new one, you can use the template app created in my other blog post Setting up a React app from scratch.
yarn add fbt fbt-easy-setup --save
fbt-easy-setup is a package that I have created to make life easier adding fbt to a React app. It provides you with an
init function, a
LocaleProvider and a
LocaleConsumer (see React’s documentation on Context). Check out the docs for fbt-easy-setup here.
Continue by installing the following dev dependencies:
yarn add babel-plugin-fbt babel-plugin-fbt-runtime @babel/node @babel/core fbt-generate-translations --dev
babel-plugin-fbt and babel-plugin-fbt-runtime are two Babel plugins that are required in order to run FBT. See more info here regarding those two plugins. Both @babel/node @babel/core are needed in order for the plugins to run properly. fbt-generate-translations is a script that I have made in order to generate translations from the output from FBT’s collect script and consumed by FBT’s translate script. This step would otherwise be manual (ugh!). More on the different scripts and steps later in this post.
Add the babel plugins installed above to your babel config file. If you are going to use enums the babel-plugin-fbt should be fed a path to the generated enums manifest (more on what this is later in the post). You also might need to add sourceType to be
unambiguous (at least I did) in order for the mix between commonjs modules and ES modules to transpile correctly. Here is an example of what to you need to add to your Babel config:
Let’s add the locales that you want your app to support! Start by creating a file defining the locales and add it to a folder called i18n:
mkdir i18n && touch ./i18n/locales.js
locales.js in your favourite text editor and add your locales of choice using the following format:
- manifest-fbts — Creates two json files, the first one (
.src_manifest.json) keeps track of every file that imports FBT (and therefore might contain translations) and the other one (
.enum_manifest.json) keeps track of all FBT enums used in the project.
- collect-fbts — Given the output from the last step, it collects all the texts to translate and generate corresponding hashes. See this for more info.
- fbt-generate-translations / fbt-generate-translations-single-file — Given the output from collect-fbts, it generates translation files (could also just be a single file) that could be consumed by translate-fbts (see below). The file / files generated are then supposed to be manually edited with translations for each locale.
- translate-fbts / translate-fbts-single-file — Takes the output from the step above and creates the translation object that should be passed to FBT’s
initfunction at runtime.
It is also useful to add scripts to clean the generated files (see clean-fbts below) and to do all the steps of above in one go (see prepare-fbts below). If you got scripts to build and start the app it might also be useful to add pre-scripts that runs all the steps above one go (see prestart and prebuild below). My scripts ended up like this:
As a side note: FBT supports having all translations in one file (using the *-single-file script above) or dividing the translations by locale into several files.
In order for FBT to work you need to provide translations. This needs to happen before any of the React components containing translations load. You also need to pass your locales and default locale to the this function. Mine ended up like this:
Since initialization needs to be done before the translations are used in any React component, add it to your Webpack config’s entry:
Add the Provider and the Consumer
The next step is to add the
LocaleProvider and the
LocaleConsumer to your app. Start by wrapping your app with the
Now you can use the
LocaleConsumer where applicable. For example you can create a component that toggles between English and Swedish like this:
Add your text
You are now all setup to add translations to you app! Hurray!
You can now for example add a fancy translatable paragraph component:
FBT’s docs for how to add translatable text to your JSX is actually really good. Check it out here!
All git users out there should also add the following to
When you have added all the translatable text you want to your JSX you should run
yarn prepare-fbts. After that you can add the translations for each locale to the generated file(s) (either in
translations_input.json or in the files located in the
translations folder). The typical workflow for translations (using the setup from this blog post) will look like this:
- Add FBT JSX to your app.
- Run your scripts to generate translation files.
- Add and modify translations in the generated files from the above step.
- Build and run your app that now contains the new translations.
However, there are endless possibilities on how to improve this process. For example it is possible (with the help of some custom scripts) to fetch translations from a remote location (eg. a translations API) and merge them with your translation file(s). This process could for example be a part of your build pipe. This is actually how Facebook is using it:
Some notes on shared enumerations
Something that is not documented when it comes to shared enums is that you need to add the suffix
$FbtEnum.js to the filename of the module that defines it.
Another thing to note is that due to some issues regarding the babel plugin (eg. see this issue for example), you need to use
require and not ES imports when using the module that defines the shared enum:
const SharedEnum = require('Shared$FbtEnum');
You also might notice that the shared enum above is required using an absolute path. Due to a bug in the babel plugin this is required, otherwise it throws. In order for the absolute path to work you will need to add a resolve entry to your Webpack config:
😝 Still stuck?
FBT promises some really awesome features like inlining translatable JSX text (which makes the text searchable in your text editor), support for plural variations, enums and pronouns. For the most part this works really well (after the initial setup has been made). However, the problems with enums shines light on some FBT’s rough edges. It feels like FBT needs some more users and maintainers in order to get some of these issues discovered and fixed. So if you are not afraid to dig in to some open source work, I definitely think that FBT is great candidate for managing i18n in your next React app.
Let me know what you think about this post in the comments below and hit that 👏 button if you liked what you read. You can also find me on Twitter if you got any further questions or comments on what you just read.