How to Add react-intl to an Nx React App
react-intl is a library that helps to set up internationalization for a react app. It has been migrated to formatjs monorepo. There are instructions on formatjs website about the setup for react apps: https://formatjs.io/docs/getting-started/installation. Nx is a monorepo tool. This blog shows how to set up for Nx react app.
The message declaration, we just need to use defineMessage
, formatMessage
or FormattedMessage
: https://formatjs.io/docs/getting-started/message-declaration.
Message Extraction
However, for message extraction, we need to install @formatjs/cli
and the command would be like:
# npm
npm run formatjs extract '{apps/<app-name>,libs/<library-name>}/src/**/*.{ts,tsx}' --out-file apps/<app-name>/src/translations/extracted-messages.json"# yarn
yarn formatjs extract '{apps/<app-name>,libs/<library-name>}/src/**/*.{ts,tsx}' --out-file apps/<app-name>/src/translations/extracted-messages.json"
In the above command, we extract the message from files that use defineMessage
or FormattedMessage
in apps and/or libs and put the message into a file called extracted-messages.json
.
The extracted-messages will have the below format:
{
"<id>": {
"defaultMessage": "...",
"description": "..."
}
}
Message Distribution
For the message distribution, we need to transform the extracted message from above to a compile translated file:
# npm
npm run formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/en.json# yarn
yarn formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/en.json
In the above command, it will take content from extracted-messages.json
to and use the default message in en.json
like
{
"<id>": "<default message>"
}
Custom Formatter
However, if we want to translate something not use the default message, then we need to create a custom formatter and passed in using --format
option: https://formatjs.io/docs/getting-started/message-distribution#translation-management-system-tms-integration.
Below is an example of a custom formatter that reads the output file and not override the value if the id already exists. If id does not exist, it will prefix the default message with the locale name.
const argv = require('yargs').argv;
const originTranslations = require('../' + argv['out-file']);
const locale = argv['pseudo-locale'];const isEnglish = !locale || locale === 'en';module.exports = {
compile: function (msgs) {
const results = {};
for (const k in msgs) {
const defaultMessage = msgs[k].defaultMessage;
if (originTranslations[k]) {
results[k] = originTranslations[k];
} else if (defaultMessage) {
results[k] = isEnglish ? defaultMessage : locale + ' ' + defaultMessage;
} else {
results[k] = 'MISSING TRANSLATION';
}
}
return results;
},
};
The below command will compile the extracted message to fr.json
:
# npm
npm run formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/fr.json --pseudo-locale fr# yarn
yarn formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/fr.json --pseudo-locale fr
The compiled fr.json
will look like this:
{
"<existing id>": "<existing value>",
"<non-existing id>": "fr <default message>"
}
Custom Commands
Put it all together, for Nx, we could add 1 custom command to the app that needs extract and distribute messages in workspace.json
: https://github.com/nrwl/nx/blob/master/packages/workspace/docs/run-commands-examples.md. We need to add the below translate command under targets
:
"translate": {
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": [
npm run formatjs extract '{apps/<app-name>,libs/<library-name>}/src/**/*.{ts,tsx}' --out-file apps/<app-name>/src/translations/extracted-messages.json",
"npm run formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/en.json",
"npm run formatjs compile apps/<app-name>/src/translations/extracted-messages.json --out-file apps/<app-name>/src/translations/fr.json --pseudo-locale fr"
],
"parallel": false
}
}
This way if we run nx run <app-name>:translate
, it will run the translation for that app.
We could also add a command in the package.json
to run translate command for all apps and libs:
"translate": "nx run-many --all --target=translate --parallel"
If now we run npm run translate
or yarn translate
, it will extract and distribute the messages across all apps and libs.
Babel
We need to install babel-plugin-formatjs
and add it to the .babelrc
for the apps that consume react-intl:
{
"presets": ["@nrwl/react/babel"],
"plugins": [
[
"formatjs",
{
"idInterpolationPattern": "[sha512:contenthash:base64:6]",
"ast": false
}
]
]
}