Automating Internationalization Workflow in Emacs
A lot of projects we work on at MojoTech require the UI to be accessible in many different locales. Recently we have been working with a global retailer to build an internationalized React client for their B2B ordering platform. One of the core requirements was for this to be accessible throughout Europe and the United States. This includes translating strings to different languages, and formatting numbers, dates, and currencies to the user's preferred locale.

We decided to use react-intl to handle internationalization in our React app. The formatting of dates, numbers, and currencies was relatively straightforward. With a user-specified locale preference, the react-intl APIs can easily format these. Handling the translation of strings to many different languages was a bit more involved.

Using react-intl to Render Strings

To render a string with react-intl, you specify a key in your UI component and map that key to a string in a Javascript object supplied to the react-intl IntlProvider. The IntlProvider wraps the app and uses React context for the components to hook into the translations provided. In our case we had separate JSON files per locale to hold the key-value pairs. Depending on the user’s locale preference, we would provide the corresponding JSON file to the IntlProvider. Here is an example of the work involved to render a string:

// dashboard.js
  <FormattedMessage id='dashboard.carts.quantity-label' />

// en-US.json
{ ..., "dashboard.carts.quantity-label": "Quantity", ... }

// nl-NL-google.json
{ ..., "dashboard.carts.quantity-label": "Aantal Stuks", ... }

For our global retailer client, we adopted the following process to handle new strings in the app:

1) Render the FormattedMessage tag with a unique key
2) Add the key and English string to the en-US.json file
3) Use Google Translate to add the key and translated Dutch value to the nl-NL-google.json file.
4) Down the line, a human translation service would translate these and move the final translations to nl-NL.json and the rest of the locales.

At all times the app would be presentable in English, and in Dutch via Google Translate. Periodically the strings would be sent to a translation service to be translated to the other languages.

Let’s explore how our workflow for managing translations evolved.

Adding Translations with Google Translate Web

As we started to build the UI, I got into the habit of always having Google Translate open, ready to take strings to translate to Dutch. It quickly became apparent though that it was somewhat time-consuming to switch between the browser and text editor, and to a few different files, to add each string in the app. It became a chore for something we do dozens of times a day.

Adding Translations with Google Translate in Emacs

A few of us on the project used Emacs and I was glad to stumble across google-translate, an "Emacs Interface to Google Translate". With this I could highlight a string in visual mode, run the google-translate-at-point command (bound to SPC x g t in Spacemacs), and have the translation displayed in a new buffer. From there I could copy the translation and add it to the Dutch file, add the key and English string to the English file, and add the FormattedMessage tag to the UI component. This was a good improvement, I could stay within Emacs and have the translation part be done in a few keystrokes.

This became muscle memory after a while and adding a new string to the app would now take maybe 30 seconds. Not bad, but for something we do many times a day, it is still a chore. Three separate files need to be updated - the UI component and the two translation files. And I need to remember the key to use across all three files, and make sure the entries are sorted by key. In order to iterate quickly on the UI I would often leave the translating for the end as cleanup, mundane work. When work becomes frequent and predictable, chances are it can be simplified with some automation. I had already been using the google-translate-at-point command to do the translation, so I thought to handle the rest with Emacs as well.

Adding Translations with a Custom Emacs Command

The process for adding a string to the UI could be expressed as a series of Emacs commands. Before, I could highlight a string in visual mode and have the translation open in a buffer. Now I wanted to be able to highlight the string in visual mode, run a command, and have the following actions performed: