Lingual

Validating your react-intl applications

Introduction

When working with react-intl we would like to ensure that our JSON translations are valid and up to date. What we want is to avoid our translation files containing outdated keys and translations and broken target translations. These broken translations might not be caught until they reach production, causing a suboptimal user experience or broken interfaces at worst.

More often than not, especially if we are not using a translation management system, we don’t have a good overview or understanding of the current state of our translations. This is especially true if we are dealing with very large applications containing hundreds or thousands of keys and multiple languages. We want to avoid these type of scenarios.

How can we automate our checks as much as possible without having to actively manage the state of our internationalization efforts?

In this post we will see how i18n-check can help with our internationalization efforts by finding any unused, untranslated or invalid translation messages.

What we want to check

By default react-intl offers a code parser that can create a valid JSON file based on the translations keys in the code, depending on the setup you could use @formatjs/cli to extract the keys .

Using the CLI to extract the messages can ensure that the base language is up-to-date, but the secondary languages might not be up to date.

We are looking for a simple way to answer the following questions:

How many keys are missing in the fr language file?

or

Are all keys valid in the de file?

Missing keys are clear enough to understand, as they either exist in the target language files or not. When it comes to invalid/broken keys the situation can be more complex. Potential situations where the key could be in an invalid state can occur when dealing with time or date formats, translations including currency, pluralisation or translations containing tags.

Introducing i18n-check

To get a better understanding, check the following translations:

// en.json
"message.greeting": "Hi, <b>{name}</b>!"

// de.json
"message.greeting": "Hallo {name}!"

The default message in the en.json file contains tags, while the target language file de.json is missing the tags around the name placeholder.

// en.json
"message.greeting": "Hi {user}, it is {today, date, medium}.",

// de.json
"message.greeting": "Hallo {user}, heute ist {today, date, medium} und morgen ist {tomorrow, date, medium}.",

In the above example the target translation de.json contains two dates as compared to the source en.json file only containing, which indicates that the source and target translations are out of sync.

What we are looking for is an automated check that can inform us on untranslated, unused and broken translations.

Getting started with i18n-check

The following command will install i18n-check:

// yarn
yarn add --dev @lingual/i18n-check

// npm
npm install --save-dev @lingual/i18n-check

// pnpm
pnpm add --save-dev @lingual/i18n-check

The i18n-check command can either be accessed in the package.json file or directly in the CLI after running the installation command.

You can update the package.json file and add a new command:

"scripts": {
    // ...other commands,
    "i18n:check": "i18n-check"
}

Once you have added the i18n:check command, you can directly call it from the command-line, i.e. yarn i18n:check.

As an alternative approach you can also access the library directly via:

node_modules/.bin/i18n-check

Checking your codebase and translation files

For this post we will assume that our translations files exist in a single folder. A basic setup could include a folder called messageExamples containing a number of translation files organized as en-en.json, fr-fr.json, it-it.json etc:

- messageExamples/
  - en-us.json
  - fr-fr.json
  - it-it.json
  - de-de.json

For more advanced scenarios you can consult the README , which includes more examples on how to setup the check depending on how the translation files are organised.

You can use the -l or --locales option to define the directory that contains the target files and with the -s or --source option you can specify the source language (i.e. en-us or en) to compare the target files against.

yarn i18n:check --locales messageExamples --source en-us

In the above scenario the i18n-check will compare the fr-fr.json and it-it.json file against the en-us.json file and check for any missing or broken keys. Running the above command might return the following result:

Example 1

i18n translations checker
Source file(s): messageExamples/en-us.json

Found missing keys!
┌──────────────────────────────┬────────────┐
│ file                         │ key        │
├──────────────────────────────┼────────────┤
│  messageExamples/de-de.json  │  richText  │
│  messageExamples/de-de.json  │  yo        │
│  messageExamples/de-de.json  │  nesting1  │
│  messageExamples/de-de.json  │  nesting2  │
│  messageExamples/de-de.json  │  nesting3  │
│  messageExamples/de-de.json  │  key1      │
└──────────────────────────────┴────────────┘



Found invalid keys!
┌──────────────────────────────┬─────────────────────┐
│ file                         │ key                 │
├──────────────────────────────┼─────────────────────┤
│  messageExamples/de-de.json  │  multipleVariables  │
└──────────────────────────────┴─────────────────────┘



Done in 0.02s.

You can also use the -r or --reporter option to see a summary of the check instead of single keys, this is especially useful if you do not want to list all the keys:

Example 2

i18n translations checker
Source file(s): messageExamples/en-us.json

Found missing keys!
┌──────────────────────────────┬───────┐
│ file                         │ total │
├──────────────────────────────┼───────┤
│  messageExamples/de-de.json  │ 6└──────────────────────────────┴───────┘



Found invalid keys!
┌──────────────────────────────┬───────┐
│ file                         │ total │
├──────────────────────────────┼───────┤
│  messageExamples/de-de.json  │ 1└──────────────────────────────┴───────┘



Done in 0.02s.

If we want to also check for unused keys, we can extend the command:

yarn i18n:check --locales messageExamples --source en-us -u client/ -f react-intl

We need to provide the src entry path via the --unused or -u option, i.e. src/, additionally we also want to tell the check what the --format is via -f react-intl.

There are more options available to configure the check even further. In most cases the aforementioned options should be enough when working with react-intl.

Automate the checks

We can automate the checks by running them on the CI. This is an example of how a Github workflow can be defined:

name: i18n-check
on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

jobs:
  i18n-check:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@master

      - name: yarn install & build
        run: |
          yarn install
          yarn build          

      - name: yarn i18n-check
        run: |
          yarn i18n-check --locales translations/messageExamples -s en-US -u client/ -f react-intl          

This is the output that would be shown when running the action: ci output

For more information on how to setup a CI action check the README .

Summary

Translations files are hard to keep in sync, as they are mostly in constant change. Automating the validation of react-intl translations files can help to idenify unused, untranslated or invalid translation messages

Checkout i18n-check here

If you have further questions in regards to setting up i18n-check or more general questions in regards to running i18n validations on the CLI or CI or just general feedback on this post, you can find Lingual on Twitter .

We are working on a new translation management system with first-class branching and CLI integration.

Subscribe to our newsletter and we'll let you know when the beta becomes available.