Garage 44

Open Source Playground

Expressio - machine translation

Background

How to keep multi-lingual software translations - once and for all - up-to-date with limited resources. This is what I've been trying to figure out on and off for the last year. Long story short; manual translation just doesn't cut it; source strings keep on changing, translators are scarse and coordination to sync translations is a pain. So, once I reached this conclusion, I decided to just get rid of manual translations, and replace it with an automatic process. It turned out to be feasible to translate i18n texts to several target languages automatically using the Deepl API.

This fixed the coordination issues I had with the translation process; there is nothing left to coordinate. As a free bonus, it also allowed me to scale the number of supported languages. Syncing translations has become a reliable on-the-fly process. The quality of the translations is quite good. One of the requirements is that i18n placeholders are kept intact. One of Deepl's features is that you can add xml tags around parts of the text that should be ignored for translation. This feature works great for this purpose.

Also, I learned that Deepl can almost always guess the right context of a text, as long the source text provides enough hints about its context. This is also an indicator for the quality of the source texts. If one language translates well, all other languages mostly seem to follow without issues. Are these translations 100% accurate for each language, being able to translate layered meanings (e.g. "I scream, we scream, we all scream for ice cream") to other languages? No, probably not (yet), but then again...manual translations are not perfect either. Translators can have a bad day, be a non-native speaker, or translations can be outdated.

In the end, what matters is that this enables non-native speakers to navigate and learn about foreign websites without being puzzled by simple incorrectly translated contexts. A dutch sentence like "Wanneer wordt de levering verwacht?" should not be translated to a context in which conception and birth is involved. Older approaches (Google translate a few years back) suffered from these kind of issues, but Deepl's translations get this part amazingly right. Good enough for me.

So, initially, I added this flow as part of a custom frontend build tool and i18next. Frontend developers adapt a source file, a watcher checks for changes, and the target languages are getting synced either by calling the Deepl API to translate a new source text, or by removing a translation if the source text is not being used anymore. This setup is a bit cumbersome to maintain, but it works.

Meet Expressio

Lately I've been molding these lessons-learned into an open-source tool. This is a weekend project, so please expect rough edges. Expressio is a minimalistic tool that manages source texts and machine translations. It keeps all of a project's source texts and translations together in an .expressio.json workspace file, which can easily be checked into version control. The UI is focussed around managing source texts in (a nested) i18next format. It does this using a JSON-style structure editor, but comes with extra features that gives it an edge compared to your typical IDE. Each source text can easily be changed and (re)translated with <Ctrl+Enter> or by using UI buttons.

Grouping translate

Here you can see a grouping of tags and its translationsm, while translating Expressio with Expressio. The grouping of tags and its translations makes it easy to navigate through translatable content. Syncing enabled target languages and translations is being done automatically. You can setup which language to translate from, and which language to translate to. Not all people in the world prefer to use English as their source language, and you don't have to! Besides syncing i18n tags that are already present, The sync workspace option lets you work on your project's translation tags, while it shows redundant and new translation tags as you modify your project files. This enables you to focus on translations that are actually used, and get rid of the ones that have become redundant.

A i18next-formatted translation JSON file can either be retrieved from a HTTP endpoint or by including it as part of your application bundle. For existing i18next source files, there is an import option that lets you import those existing translation tags into an Expressio workspace.

By focussing on a minimalistic UI, an easy to use syncing process and flexible options to import or export translations, Expressio aims to make multi-lingual software development a breeze. To try it yourself, you need to install the tool using Bun. Bun is a fast alternative for Node.js; bunx is the equivalent of npx.

cd /path/to/your/project
bunx @garage44/expressio@1.0.8 --help

Help

The options are simple. You can import from an i18next file, export to a bundle i18n file or start the Expressio service & UI, which is served as a local web application. Lets do the latter:

bunx @garage44/expressio@1.0.8 start

Start CLI

Expressio starts by asking for your Deepl key once. This setting will be persisted to ~/.expressiorc next time. You can get a free Deepl API key for 500K characters per month here, but you need a creditcard to subscribe. Consider paying for a Deepl subscription if you plan to use it professionaly. Its definitely worth the few extra bucks! Once you entered the API key, Expressio will load some example data in your workspace.

Settings

First stop is the Expressio settings page. Here you can setup your language preferences. The UI language, your source text language and the target languages to translate to. API usage is calculated based on the amount of characters that are being translated. Don't start by enabling too many languages, if you want to keep your API usage low.

Usage

Checkout the API usage meter at the bottom of the screen, to keep an eye on your usage.

Sync Workspace

The sync workspace option is a rudementary check for $t('') tags in your frontend code. It shows which tags are not yet translated, or which tags are not being found in the scanned code. This is still a work in progress, but should be usable enough to get you started.

The UI

Now you can start editing your project translations. Changes will be automatically saved to the workspace file (/path/to/your/project/.expressio.json) and to a i18next bundle file in src/i18next.json. Either import this file in your application code or load the i18next translations from /api/translations if you want to use the translations in your frontend.

Want more information? Checkout the code on Codeberg or read about updates on Mastodon.