How to auto-translate JSON files for app localization (2026 guide)

Kinga Pomykała
Kinga Pomykała
Last updated: March 23, 202615 min read
How to auto-translate JSON files for app localization (2026 guide)

JSON is the de facto standard for storing translation strings in modern web and mobile apps. React with i18next, Next.js, Vue i18n, Angular, they all use JSON translation files by default. When your app supports one language, managing a few JSON files is easy. When it supports ten, it becomes a serious maintenance problem.

This guide covers how to auto-translate JSON files effectively: manually for small projects, with CLI tools for teams that want automation, and with a full CI/CD pipeline for products where translation is part of every release. It also covers the mistakes that cause JSON translation workflows to break at scale, which is why many teams end up re-solving this problem every year.

If you are earlier in the process and haven't yet set up i18n in your codebase, start with our complete technical guide to internationalization and software localization first.

Check also our quick video guide about auto-translating JSON files:

YouTube video about auto-translating JSON files

Why JSON translation files become hard to manage

The appeal of JSON for localization is obvious: every developer already knows it, no special tooling is required to get started, and it integrates directly with every major i18n library. For a project with one or two languages, a folder of JSON files is a perfectly reasonable approach.

The problems start around the time you add a third or fourth language:

  • Key drift: a developer adds a new key to en.json, but de.json, fr.json, and es.json never get updated. The app silently falls back to English for those strings, and nobody notices until a user in Germany files a support ticket.
  • Structure mismatches: nested JSON files can diverge between locales, causing parse errors or missing translations in unexpected places.
  • Context loss: a key named title or button_ok gives a human translator nothing to work with. Without context, translations are guesses.
  • Re-translation waste: many teams periodically dump their entire JSON file into ChatGPT or DeepL and translate everything, including strings that haven't changed. This is slow, expensive, and introduces "translation drift": previously correct translations silently changing between runs.
  • No review process: auto-translated strings go straight to production with no human check, and errors only surface when real users encounter them.

Understanding these failure modes is more useful than any specific tool recommendation, because they determine what kind of workflow you actually need.

JSON translation file formats: What you are working with

Before automating anything, it helps to know which JSON format your project uses, since different i18n libraries expect different structures.

Flat JSON

The simplest format: a single object with string keys and string values. Used by many lightweight i18n setups.

{
  "welcome_message": "Welcome to our app!",
  "login_button": "Log in",
  "logout_button": "Log out"
}

Check format details: Single-language JSON, SimpleLocalize JSON

Nested JSON (most common)

Keys are organized into namespaces using nested objects. Used by i18next, next-i18next, and most React localization libraries.

{
  "auth": {
    "login": "Log in",
    "logout": "Log out",
    "signup": "Sign up"
  },
  "dashboard": {
    "title": "Dashboard",
    "welcome": "Welcome back, {{name}}!"
  }
}

ICU message format in JSON

Used by FormatJS / react-intl. Values contain ICU syntax for plurals, gender, and interpolation. The structure is flat or nested JSON, but the values are more complex.

{
  "items_in_cart": "{count, plural, one {You have # item in your cart} other {You have # items in your cart}}",
  "greeting": "Hello, {name}!"
}

When auto-translating ICU messages, preserving the ICU syntax inside the values is critical. A translation engine that treats {count, plural, one {...} other {...}} as literal text to translate will break your pluralization entirely. See our guide on ICU message format for details on how to handle this correctly.

Multi-language JSON

All locales in a single file, keyed by locale code. Less common in large projects but used in some setups.

{
  "en": {
    "welcome": "Welcome"
  },
  "de": {
    "welcome": "Willkommen"
  }
}

Knowing your format matters because different tools handle these differently, and some auto-translation approaches only work with specific structures.

Check format details: Multi-language JSON

How to auto-translate JSON files: Four approaches

Approach 1: Script-based translation (small projects, full control)

For solo developers or very small projects, a script that calls a translation API directly gives you complete control with minimal dependencies.

A basic Node.js script using the DeepL API:

import fs from "fs";
import Anthropic from "@anthropic-ai/sdk"; // or DeepL, OpenAI, etc.

const source = JSON.parse(fs.readFileSync("en.json", "utf8"));

async function translateValues(obj, targetLocale) {
  const result = {};
  for (const [key, value] of Object.entries(obj)) {
    if (typeof value === "object") {
      result[key] = await translateValues(value, targetLocale);
    } else {
      // Call your translation API here
      result[key] = await translateString(value, targetLocale);
    }
  }
  return result;
}

const translated = await translateValues(source, "de");
fs.writeFileSync("de.json", JSON.stringify(translated, null, 2));

What this handles well: straightforward flat or nested JSON with simple string values.

What it does not handle well: ICU message format (the API will translate the syntax, not just the text), placeholders like {{name}} (may get translated), and detecting which keys already exist versus which are new (requires additional logic).

When to use it: prototypes, internal tools, single-developer projects where you can review every output before it ships.

Approach 2: CLI-based translation with a TMS

For teams that want automation without building infrastructure from scratch, a translation management system (TMS) with a CLI tool handles the file format complexity and key syncing for you.

With SimpleLocalize CLI, the workflow looks like this:

# Install the CLI
npm install @simplelocalize/cli

# Push your source JSON to SimpleLocalize
simplelocalize upload \
  --apiKey YOUR_API_KEY \
  --uploadPath "locales/en.json" \
  --uploadFormat single-language-json

# Auto-translate missing keys for all languages
simplelocalize auto-translate --apiKey YOUR_API_KEY

# Pull translated files back
simplelocalize download \
  --apiKey YOUR_API_KEY \
  --downloadPath "locales/{lang}.json" \
  --downloadFormat single-language-json

The key difference from a script: the CLI only sends keys that are new or changed to the translation API. Keys that already have translations are not re-translated, which prevents drift and keeps costs predictable.

It also handles placeholder protection out of the box: {{name}}, {count}, and ICU message syntax are preserved rather than passed to the translation engine as text to translate.

For products where translation is part of every release, the translation workflow should run automatically on every push. New keys get detected, translated, and synced back without any manual steps.

A GitHub Actions workflow that does this:

name: 'Localization sync'

on:
  push:
    branches: [main]

jobs:
  sync-translations:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install SimpleLocalize CLI
        run: npm install -g simplelocalize-cli

      - name: Upload source strings
        run: |
          simplelocalize upload \
            --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }} \
            --uploadPath "locales/en.json" \
            --uploadFormat single-language-json

      - name: Auto-translate missing keys
        run: simplelocalize auto-translate --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }}

      - name: Download translated files
        run: |
          simplelocalize download \
            --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }} \
            --downloadPath "locales/{lang}.json" \
            --downloadFormat single-language-json

      - name: Commit updated translations
        run: |
          git config --global user.email "ci@yourapp.com"
          git config --global user.name "Translation Bot"
          git add locales/
          git diff --staged --quiet || git commit -m "chore: update translations"
          git push

This workflow triggers only when en.json changes, uploads new strings, auto-translates them, and commits the updated locale files back. Your developers add a key to en.json; by the time the PR is merged, translated versions are already ready.

For a deeper look at CI/CD-based localization pipelines, see our guide on continuous localization and the step-by-step localization workflow for developers.

Approach 4: Translation editor with manual review

Fully automated translation is appropriate for some content (UI labels, error messages, navigation items) but not others (marketing copy, onboarding flows, anything with tone requirements). For content where quality matters more than speed, the right workflow is: auto-translate as a first draft, then human review.

SimpleLocalize's translation editor provides this workflow out of the box. Auto-translated strings get a "needs review" status, and translators or product writers can review and approve them before they ship. You can also add descriptions and screenshots to each key so translators have context for what they are translating.

Auto-translating new languages with SimpleLocalize

Working with specific JSON formats

How to auto-translate flat JSON

Flat JSON is the easiest case. Upload your source file, run auto-translation, download the result. If you're using SimpleLocalize:

  • Import your en.json (choose single-language JSON as the format).
  • Go to Languages and add your target languages.
  • Click Auto-translate all: missing translations are filled in.
  • Export each language as its own flat JSON file.
Auto-translating flat JSON files with SimpleLocalize

How to auto-translate nested JSON

Nested JSON works the same way as flat JSON in SimpleLocalize: the nesting is preserved on export. The platform stores keys as paths (e.g., auth.login, dashboard.title) and reconstructs the nested structure when you download.

Auto-translating nested JSON files with SimpleLocalize

If you are rolling your own script, you will need to flatten the JSON before sending to the translation API, then reconstruct the nesting afterward.

Libraries like flat make this straightforward:

import flat from "flat";
import { unflatten } from "flat";

const nested = { auth: { login: "Log in" } };
const flattened = flat(nested); // { "auth.login": "Log in" }
// ... translate flattened values ...
const reconstructed = unflatten(translated);

How to auto-translate JSON with ICU messages

ICU message format requires careful handling. The syntax inside values ({count, plural, ...}, {name}, {0}) must survive the translation unchanged. Only the text portions should be translated.

Most general-purpose translation APIs do not handle ICU natively. If you pass an ICU string directly to DeepL or Google Translate, the variable names and plural keywords may be translated.

The safest approach: use a TMS that understands ICU format and strips the syntax before sending to the translation engine, then re-inserts it in the translated output. SimpleLocalize handles this automatically for you. See our pluralization guide for more detail on how plural forms work across languages.

Auto-translating ICU message format JSON files with SimpleLocalize

Common problems and how to fix them

Placeholders get translated

Problem: your {{name}} becomes {{nom}} in French, {{名前}} in Japanese.

Fix: use a TMS that protects placeholder syntax before sending strings to the translation engine. If using a script, extract and mask placeholders before translating, then restore them after:

function maskPlaceholders(str) {
  const placeholders = [];
  const masked = str.replace(/\{\{[^}]+\}\}/g, (match) => {
    placeholders.push(match);
    return `PLACEHOLDER_${placeholders.length - 1}`;
  });
  return { masked, placeholders };
}

function restorePlaceholders(str, placeholders) {
  return str.replace(/PLACEHOLDER_(\d+)/g, (_, i) => placeholders[i]);
}

Keys go missing between locales

Problem: en.json has 200 keys. de.json has 180. The 20 missing keys silently fall back to English and nobody notices.

Fix: use a TMS or CLI tool that only sends new keys to the translation engine, and flags missing keys in the target locale. This way, you can catch missing translations before they go to production.

Alternatively, a simple script that compares key sets between locales catches this before deployment.

Translations drift between runs

Problem: you re-translate everything from scratch each time. A string that was reviewed and approved last month now has a different (possibly worse) translation.

Fix: treat translated strings as persistent state, not generated output. Only send new or changed keys to the translation engine. A TMS with translation memory does this automatically. If rolling your own, store a hash of the source string alongside each translation and only re-translate when the hash changes.

JSON structure is invalid after editing

Problem: a translator edits JSON files directly and introduces a syntax error, like a missing comma, an unclosed quote. The app fails to parse the file at runtime.

Fix: never have translators edit JSON files directly. Use a translation editor that stores strings in a database and exports valid JSON. If you must use raw files, add a JSON lint step to your CI pipeline that fails the build on invalid syntax.

Choosing a translation engine for JSON files

The translation engine you connect to your workflow affects both quality and cost. A few practical notes:

  • DeepL produces the best output for European languages (German, French, Spanish, Dutch, Polish, etc.) and is fast. Less strong for Asian languages. Has issues with ICU message format and placeholders if not used through a TMS that handles those.
  • Google Translate has the broadest language coverage and is reliable for Asian and less common languages.
  • OpenAI / Claude / Gemini produce better results when context is provided (project description, tone instructions, key-level descriptions). Slower and more expensive per string than MT engines, but the quality ceiling is higher.

For most SaaS teams, a practical approach is: use DeepL or Google Translate as the default engine for UI strings and use an LLM for high-stakes content (onboarding, marketing, any user-facing copy where tone matters). See our full auto-translation comparison with examples.

Check also out our list of MT and AI translation engines.

Step-by-step: Auto-translating JSON files with SimpleLocalize

If you want to get started quickly without building a pipeline from scratch:

Step 1. Import your JSON file

Start by creating a project in SimpleLocalize and importing your existing translation files, en.json and any other locale files you have. They will serve as a base for further translations.

Importing translations from JSON files

Step 2. Add target languages

Go to the Languages tab and add the languages you want to translate into. You can add as many as you need at once.

Adding languages to the project
Adding languages to the project

Step 3. Auto-translate

Click Auto-translate all to fill in missing translations for all languages using your chosen translation engine (DeepL, Google Translate, OpenAI, or others). Translation only runs on keys that don't already have a value, so existing translations are not touched.

Auto-translating new languages

Step 4. Review in the editor

Use the translation editor to review auto-generated translations, add context, and approve strings before they ship. For any key where the auto-translation is uncertain, you can flag it for human review.

SimpleLocalize translation editor
SimpleLocalize translation editor

Step 5. Export

Go to the Data tab and export your translations as JSON. Choose single-language JSON (one file per locale) or multi-language JSON depending on your project structure.

Exporting translated JSON files

Once this is working, replace the manual upload and download steps with CLI commands in your CI pipeline to make the workflow continuous.

Summary

ApproachBest forKey limitation
Script + APISolo devs, full controlYou handle everything manually
CLI + TMSTeams, automationRequires TMS setup
CI/CD pipelineRegular releases, scaleInitial pipeline work
Editor + human reviewQuality-critical contentSlower, needs reviewers

The biggest improvement most teams can make to their JSON translation workflow is not switching tools, but adding delta detection so only changed keys are translated, and adding a missing-key check so gaps never reach production silently. Everything else builds on those two things.

For broader context on how JSON translation fits into a full i18n architecture, including key naming, namespace design, fallback strategies, and deployment, see our complete technical guide to internationalization.

FAQ

What is the best way to auto-translate JSON files?
For small projects, a script calling DeepL or OpenAI directly works fine. For teams shipping regularly, a TMS with CLI integration and CI/CD automation is more reliable because it handles delta detection, placeholder protection, and key sync automatically.
Does auto-translation preserve placeholders like {{name}}?
It depends on the tool. General-purpose translation APIs may translate placeholder text unless you mask it first. A localization platform like SimpleLocalize protects common placeholder formats automatically.
Can I auto-translate JSON files with ICU message format?
Yes, but the ICU syntax inside strings must be preserved. Use a TMS that understands ICU format, or strip and restore the syntax manually around your API calls.
How do I handle missing translations between locales?
Use a TMS or CLI tool that only sends new keys to the translation engine and flags missing keys in the target locale. This way, you can catch missing translations before they go to production.
How much does auto-translating JSON files cost?
It depends on the translation engine and your volume. DeepL and Google Translate charge per character. OpenAI charges per token. The biggest cost optimization is delta detection — only translating keys that have changed rather than re-translating everything each run.
Is auto-translation good enough for production?
For UI strings, navigation labels, and error messages, modern MT engines produce output that is production-ready for most language pairs. For onboarding flows, marketing copy, or any content with tone requirements, auto-translation as a first draft with human review is the recommended approach.
Kinga Pomykała
Kinga Pomykała
Content creator of SimpleLocalize

Get started with SimpleLocalize

  • All-in-one localization platform
  • Web-based translation editor for your team
  • Auto-translation, QA-checks, AI and more
  • See how easily you can start localizing your product.
  • Powerful API, hosting, integrations and developer tools
  • Unmatched customer support
Start for free
No credit card required5-minute setup
"The product
and support
are fantastic."
Laars Buur|CTO
"The support is
blazing fast,
thank you Jakub!"
Stefan|Developer
"Interface that
makes any dev
feel at home!"
Dario De Cianni|CTO
"Excellent app,
saves my time
and money"
Dmitry Melnik|Developer