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
Using react-intl to Render Strings
To render a string with
// dashboard.js<div>...<FormattedMessage id='dashboard.carts.quantity-label' />...</div>// 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:
- Render the tag with a unique keyFormattedMessage
- Add the key and English string to the en-US.json file
- Use Google Translate to add the key and translated Dutch value to the nl-NL-google.json file.
- 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. While the Google translations aren’t necessarily accurate, it’s a good first step to test the UI in multiple languages. Periodically the strings would be sent to a translation service to be translated to Dutch and 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
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
Adding Translations with 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:
- Prompt the user for a key
- Add the key and highlighted region to the English file
- Translate the highlighted region and add it to the Dutch file
- Replace the highlighted region with a tag including the keyFormattedMessage
The root of the command implementation is shown below, written in Emacs Lisp (Elisp). The full source can be viewed here.
(defun ri-translate ()(interactive)(let ((key (read-string "Specify a key: ")))(message "Adding translation entries...")(ri-translate--add-english-entry key)(ri-translate--add-dutch-entry key)(ri-translate--insert-formatted-message key)(message "Added translation entries.")))
This roughly matches the process I described above.
To add the entry to the English file, we need to push the new key-value pair to the existing JSON in the file. To do this in Elisp we can read the JSON file into a hash, convert the hash to a list of key-value pairs, push the new key-value pair, sort the list by key, convert back to a hash, and write that back to the JSON file. This may seem like a lot of steps but it's essentially what we did by hand before: take a key and value, open the translation file, insert the entry sorted by key, and save/prettify the file. The same commands used to do this by hand are the same used in the Elisp program:
Adding to the Dutch file we follow the same process, only first we need to translate the highlighted region. We can use the same
Now when adding a new string to the UI, what used to take 30 seconds can now be done automatically in a few keystrokes. The only thing to think about now is what to name the key.
Let Your Text Editor Work for You
I find getting to know your text editor to be one of the biggest productivity boosts. Start with using key combinations for actions you perform often. If you notice things in your workflow that are frequent and have a predictable process, see if there are any editor plugins you can use to make that easier. While this post specifically referred to Emacs, most text editors have a plugin system with open source packages. And if an existing package can't quite fit to your workflow, hopefully this post inspires you to try writing your own.
Having written this command to manage translations, I'm now more observant of my workflow, looking for common patterns that could benefit from automation. For example, I noticed that I'm always typing out
Have any custom editor plugins or ideas where one would be useful? Send us a tweet at @MojoTech!
PS: Do you have a React project in the pipeline? Check out our React development services to learn more about our development engagements.