---
title: "How to translate a JSON file into another language"
description: "Translate a JSON language file with a working example, instructions for your app strings, and a Node.js check for nested keys, values and placeholders."
date: 2026-10-20
language: en
canonical: https://ai-glot.com/blog/how-to-translate-json-files
source: AI Glot blog
---
**To translate a JSON language file, upload the source, name the target language and translate the text values.** Start with a small file in the [free JSON translator](/file-formats/json-translation), then check the returned file in your app. You can [download the example](/examples/localization/en.json) used here.

This guide is for app strings, such as `locales/en.json`. If your file contains a product catalogue or API records, decide which fields are human-readable text before applying the same workflow. An identifier stored as a string is still an identifier.

## Try these six strings first

The sample contains a checkout screen and two settings labels:

```json
{
  "checkout": {
    "title": "Your order",
    "greeting": "Hello, {{name}}",
    "pay": "Pay now",
    "support": "Contact Pinecart support"
  },
  "settings": {
    "language": "Language",
    "notifications": "Email notifications"
  }
}
```

Upload it to the JSON tool and enter:

```text
Translate every text value into French.
Keep Pinecart in English and preserve {{name}} exactly.
```

The free tool accepts files up to **2 MB**, five per day, and translates up to **5,000 words per file**. Larger files return complete, with the remaining text unchanged. That makes the download usable as a sample, but it is not a fully translated locale. Use [the app](https://app.ai-glot.com/signup) for a complete large file, a workspace glossary and repeat releases.

## Know which parts belong to your app

The application calls `checkout.title`; your reader sees its value. The name variable is filled at runtime. Both have to survive a language change.

*Illustration: Translate the values, then verify the keys and placeholders before committing a locale file.*

AI Glot reads JSON as a structure and writes it back with two-space indentation. A diff may therefore include whitespace changes. Review the values separately from formatting.

**Name your placeholder convention.** The example uses i18next-style `{{name}}`. Other projects use `{name}`, `%s` or ICU messages. Copy a real string from your app when writing the instruction. Don't convert one convention into another during translation.

## Check the plan before a large job

*Illustration: A JSON translation starts with a source file and ends with a reviewed locale in the application.*

For recurring releases, use a [workspace glossary](/blog/build-translation-glossary-brand) for product terms and feature names. Scope instructions decide **which values** are translated; wording rules decide **how each value** is written.

A source-only `en.json` does not say which French labels were translated last month. Asking it to "translate only the missing French strings" provides no French file to compare against. Exclude known key prefixes, or prepare source and target fields together and check the plan's interpretation. The [app language file guide](/blog/how-to-translate-app-language-files-with-ai) covers the broader workflow and other formats.

## Run a structural check before committing

Save the download as `fr.json` alongside `en.json`. Copy this into `check-locale.mjs` and run `node check-locale.mjs` with Node.js. It checks nested object keys, array lengths, value types, unchanged non-text values and the sample's double-brace placeholders.

```js

const source = JSON.parse(readFileSync('en.json', 'utf8'));
const target = JSON.parse(readFileSync('fr.json', 'utf8'));
const tokens = text => [...text.matchAll(/\{\{[^{}]+\}\}/g)]
  .map(match => match[0]).sort();

function check(a, b, path = '$') {
  assert.equal(typeof b, typeof a, `${path}: value type changed`);
  if (a === null || typeof a !== 'object') {
    if (typeof a === 'string') {
      assert.ok(b.trim().length || !a.trim().length,
        `${path}: translation is empty`);
      assert.deepEqual(tokens(b), tokens(a),
        `${path}: placeholders changed`);
    } else {
      assert.deepEqual(b, a, `${path}: non-text value changed`);
    }
    return;
  }
  assert.equal(Array.isArray(b), Array.isArray(a),
    `${path}: container type changed`);
  assert.ok(b !== null, `${path}: container is null`);
  if (Array.isArray(a)) {
    assert.equal(b.length, a.length, `${path}: array length changed`);
  }
  assert.deepEqual(Object.keys(b).sort(), Object.keys(a).sort(),
    `${path}: keys changed`);
  for (const key of Object.keys(a)) check(a[key], b[key], `${path}.${key}`);
}

check(source, target);
console.log('Keys, types and {{placeholders}} match. Review the wording next.');
```

**This validates a contract, not translation quality.** A value left in English still passes. So can a translation that is grammatically wrong or too long for its button. The token rule covers `{{name}}`, not every placeholder format; adapt it to your application's syntax.

For [i18next JSON v4](https://www.i18next.com/misc/json-format), plural variants use language-specific suffixes and your calls supply a count. Prepare and test the keys your target locale needs. This key-equality check deliberately rejects a changed key set; it does not generate or validate a new plural inventory.

## Finish inside the application

Point your app's existing French locale loader at `fr.json`. Open the checkout, interpolate a real name and check the longest button at the smallest supported screen width. If you use plurals, test counts that exercise each target-language form.

For a small file, a careful manual pass may be enough. For thousands of strings every release, keep the source file, the glossary, this structural check and the UI review in the same workflow. The [app localization solution](/solutions/app-localization-file-translation) explains where AI Glot fits when several people maintain that process.