Translate Tool

September 8, 2025

Rust

At work, we use ngx-translate to hande translations for our web-apps. It’s a great tool but it uses JSON files for the translations and managing them is a bit of a pain. I wanted to fix that. I wanted a simple way to interact with the translation files, add new keys, and validate that all the keys are present in all languages. I tried searching online, but quickly gave up as the common “I’ll just build it myself” thought entered my mind.


As usual for me with these things, I built it in Rust.


I started by writing a simple CLI tool using clap that could read and parse the translation files in the specified folder. Then, when I got that working, I upgraded it so that it could write the files. I used the awesome inquire crate to prompt the user for keys and values.


Here’s an overview of the commands I implemented:

  • add - add a new key
    • prompts for values in all languages
  • update - update an existing key
    • prompts for values in all languages
  • validate - validate the translation files
    • test that all keys are present in all languages
    • --fail-on-empty flag to make it fail if any keys are missing

Configuration file

For the configuration file I used a simple JSON based file format. The configuration file looks something like this:

{
	// commands to execute after writing to the disk; defaults to an empty list
	"post_write_commands": ["bun format"],
	// defaults to "en"
	"default_locale": "en",
	// defaults to "translations"
	"translations_directory": "test/translations"
}

Usage in docker

While downloading the CLI tool is easy, I wanted to make it even easier to use for my colleages. To accomplish this, I created this command to run the CLI in a docker container with volume mounts to the disk:

docker run --rm -it -v ./tt.config.json:/tt.config.json -v ./translations:/translations ghcr.io/borisnliscool/translate-tool:latest

It is a bit long, yes, but it works great. I even put it in my package.json file as a script.

{
	"scripts": {
		"tt": "docker run --rm -it -v ./tt.config.json:/tt.config.json -v ./translations:/translations ghcr.io/borisnliscool/translate-tool:latest"
	}
}

The result is a super easy way (pnpm tt) to run the tool, without having to install it locally.


Usage in workflows

Because I already created a docker image, I also wanted to use it in my workflows. This way, I can automatically lint my translation files before deployment to ensure there are no missing locales in my app.


Here’s an example workflow:

jobs:
  lint-translations:
    name: Lint translations
    image: ghcr.io/borisnliscool/translate-tool:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Validate translations
        run: |
          mkdir /translations
          cp -r /static/i18n/* /translations/
          translate-tool validate --fail-on-empty

Summary

I spent a weekend building an awesome tool to help me work with translations. source code

Copyright © 2016-2025 boris.foo (formerly borisnl.nl), All rights reserved.