Formats

JSON

A flat key-to-string document per locale, in the shape i18next and react-intl load, for any runtime that reads its own catalog.

What you get

Everything the editor does, through the files you already ship.

  • AI drafts, a person verifies

    Every language is drafted against your glossary and each key's length limit. A draft never ships until someone has verified it.

  • Five checks on every save

    Placeholders, length, terminology, consistency and missing translations are flagged on the row they concern, not in production.

  • Everything is an HTTP call

    Project tokens carry read, push and manage scopes, so a CI token that pushes files cannot delete keys.

    The HTTP API
  • Runs in CI

    Push the source language on merge and pull every language before the release build, on any CI that can run a shell.

    CI pipelines

Set it up

From nothing to every language.

  1. Step 1: The file

    Flat keys with dots in the name, one object per locale. Plurals use i18next's _one and _other suffixes, placeholders its {{name}} form, so the file loads without a transform.

    locales/de.json
    {
      "cart.items_one": "1 Artikel",
      "cart.items_other": "{{count}} Artikel",
      "onboarding.location_permission": "Standort & Wetter für {{app}} erlauben",
      "paywall.subscribe_cta": "Jetzt abonnieren"
    }
  2. Step 2: Load it with i18next

    The keys are flat, so turn the key separator off; otherwise i18next reads cart.items as a path into nested objects. The plural suffixes and {{count}} are i18next's own conventions and need no configuration.

    i18n.ts
    import i18next from "i18next";
    import de from "./locales/de.json";
    import en from "./locales/en.json";
    
    await i18next.init({
      lng: "de",
      keySeparator: false,
      resources: { de: { translation: de }, en: { translation: en } },
    });
    
    i18next.t("cart.items", { count: 3 }); // 3 Artikel
  3. Step 3: Install the CLI and log in

    Homebrew or the one-line installer, then a token issued per project under Settings, Integrations. The read scope pulls; push writes source files and values; manage creates and deletes keys.

    shell
    brew install anylocale/tap/anylocale
    anylocale login --project my-app
  4. Step 4: Push the source file

    The push is additive: keys missing from the file stay in place, so a truncated file cannot empty a project. Descriptions in the file become translator notes.

    shell
    curl -fsS -X POST "https://anylocale.com/api/loc/push?project=my-app&format=json" \
        -H "Authorization: Bearer $ANYLOCALE_TOKEN" \
        --data-binary @locales/en.json
  5. Step 5: Pull one locale

    One file in the json format, written to the path you name. Repeat per locale, or script it over the project's locale list.

    shell
    anylocale pull --project my-app --locale de \
        --format json --out locales/de.json
  6. Step 6: Or every locale the project has

    The locales endpoint lists them, so no build script carries a hardcoded list.

    shell
    for locale in $(curl -fsS "https://anylocale.com/api/loc/locales?project=my-app&plain=1" \
        -H "Authorization: Bearer $ANYLOCALE_TOKEN" | awk '$1 == "locale" { print $2 }'); do
      anylocale pull --project my-app --locale $locale --format json --out locales/$locale.json
    done

Good to know

  • A key is read as a plural only when its _other form and at least one other category are both present, so a key that merely ends in _one stays a plain string.
  • JSON has no comment channel: descriptions live in the project and are shown in the editor, not in the file.
  • Every pull carries the current text of every key, verified or not. Only over-the-air delivery is limited to verified strings.

Questions

Nested or flat?
Flat. A nested object is refused with the key that nests, because a nested {"a":{"b":..}} and a flat "a.b" cannot both round-trip. Keep the dots in the key names and turn the key separator off in the loader.
Which runtimes is this for?
Any that loads its own catalog per locale: i18next in a web app, react-intl, a backend, a game, a desktop app. For a next-intl app use the next-intl format instead, which keeps ICU plurals intact.next-intl
How do plurals and placeholders look in the file?
A plural is a set of keys with the CLDR category as suffix, cart.items_one and cart.items_other, and a placeholder is {{name}}; i18next reads both as they are. A key that merely ends in _one stays a plain string unless its _other sibling exists too.

Start with the file your app already ships.

After checkout, import one strings file and read the first drafts within minutes.

Nothing to connect and nothing to install. The CLI is optional.