> ## Documentation Index
> Fetch the complete documentation index at: https://developer.dittowords.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are Ditto's developer docs. Ditto is a single source of truth for product copy across design, review, localization, and code.
> The first step on any codebase is a scan: https://developer.dittowords.com/get-started/scan-your-repo.md. That page also covers reviewing the style guide Ditto drafts from the scan. After it, the follow-on guides are Connect Figma files and Set up localization (under /guides/) and Connect the Ditto MCP (the Setup page of the MCP reference).
> Two different MCP servers: the Ditto MCP server at https://api.dittowords.com/v2/mcp gives an agent a workspace's text and style guide rules; the documentation MCP server at https://developer.dittowords.com/mcp searches these docs.
> Prefer logging in with a Ditto account (npx -y @dittowords/cli@latest login; OAuth in MCP clients) over API keys. API keys are for CI and headless clients only.
> Commands are complete as written and use npx -y @dittowords/cli@latest; nothing needs a global install.

# JSON

> JSON string formats Ditto exports via the API and CLI: flat, structured, nested, ICU, and ARB, with plurals, variables, and variant metadata.

String formats are how Ditto hands text to code. The CLI writes these files on `pull` according to the `outputs` in your [config](/cli-reference/configuration#outputs), and the API returns the same formats from the export endpoints ([`GET /v2/textItems/export`](/api-reference/text-items/export-text-items) and [`GET /v2/components/export`](/api-reference/components/export-components)). Every format can be fetched for the base text or for a single variant; [Set up localization](/guides/set-up-localization) covers how locales map to files.

[JSON (Javascript Object Notation)](https://www.json.org/json-en.html) is a standard file format that allows you to map data in key-value pairs.

You can fetch text from Ditto in several JSON string file formats: **flat, structured, nested, ICU, and ARB**. As this format is framework-agnostic, JSON files are frequently used to store strings in web development.

If you're fetching this format via the API, all JSON formats have a HTTP `Content-Type` header of `application/json`.

## Flat JSON

This format provides a direct mapping from string IDs to strings, without additional metadata.

**Example:**

```json theme={null}
{
  "[TEXT ID]": "[TEXT OF TEXT ITEM]",
  "[TEXT ID]": "[TEXT OF TEXT ITEM]",
  // Text items with plural values will have those plural values
  // included in the output as individual key/value pairs.
  // Plural forms: zero, one, two, few, many, other
  "[TEXT ID]_[PLURAL FORM]": "[TEXT OF PLURAL FORM]",
  ...
}
```

**Instructions:**

To fetch this via the **API**, provide the query parameter `?format=flat`.

To fetch this via the **CLI**, specify `format: flat` in the `config.yml` file.

## Structured JSON

This format maps string IDs to an object of each text item's metadata.
This format is recommended if metadata fields are used directly in development.
The `text` field will always be included, with additional fields based on those included on the text item:

* `status`
* `notes`
* `variables`
* `plurals`
* `variants`
* `rich_text`

**Example:**

```json theme={null}
{
  "[TEXT ID]": {
    "text": "[TEXT OF TEXT ITEM]",
    "status": "[STATUS OF TEXT ITEM]", // optional
    "notes": "[NOTES OF TEXT ITEM]", // optional
    "tags": [ ... ], // optional: array of strings
    "variables": { // optional
      "[VARIABLE NAME]": {
          ... // variable properties: example, fallback, etc
      }
    },
    "plurals": { // optional: only if plurals are enabled for text
      // Plural forms specified are listed in object
      // Plural forms: zero, one, two, few, many, other
      "[PLURAL FORM]": "[TEXT FOR PLURAL FORM]",
      ...
    },
    "variants": { // optional: only present if text has variant values and not a single-variant export
      "[VARIANT ID]": {
        "text": "[TEXT OF VARIANT]",
        "variables": { ... }, // see above `variables` property
        "plurals": { ... } // see above `plurals` property
      },
    },
    // Only included when `?includeRichText=true` is passed (API) or
    // `richText: html` is set (CLI). Rich text is the text item's
    // formatting (bold, italic, links, lists) rendered as HTML.
    "rich_text": "[HTML CONTENT OF TEXT ITEM]"
  },
  "[TEXT ID]": { ... },
  ...
}
```

**Instructions:**

To fetch this via the **API**, provide the query parameter `?format=structured`.

To fetch this via the **CLI**, specify `format: structured` in the `config.yml` file.

## Nested JSON

<Info>**Note:** This format is only available when fetching components (not projects).</Info>

This format nests the JSON output based on nesting in the component library.

In Ditto, library component names can use forward slashes to express hierarchy (for example `onboarding/header/title`), mirroring the folders and groups they are organized into in the component library.

When a developer ID is generated from such a name, the workspace's [Developer ID configuration](/concepts#configuration) replaces the `/` with a delimiter you choose (the **character replacement** rule for `/`). In the nested JSON format, component IDs are split on that delimiter, and each part becomes a level of depth in the output.

**Example:**

```json theme={null}
{
  "onboarding": {
    "header": {
      "title": "Welcome!",
      "subtitle": "We're happy to have you here"
    },
    "body": {
      "CTA": "Let's get started"
    }
  },
  "authentication": {
    "new-user-cta": "Create an account",
    "existing-user-cta": "Sign in"
  },
  "toast": "Thanks for signing up!"
}
```

Assuming a `/` replacement character of `.` in the component ID settings, the JSON above would be generated from the following component IDs:

```text theme={null}
onboarding.header.title
onboarding.header.subtitle
onboarding.body.CTA
authentication.new-user-cta
authentication.existing-user-cta
toast
```

**Instructions:**

To fetch this via the **API**, provide the query parameter `?format=nested`.

To fetch this via the **CLI**, specify `format: nested` in the `config.yml` file.

## ICU JSON

This format provides a mapping from string IDs to strings in accordance with the [ICU message format](https://icu.unicode.org/home).

**Example:**

```json theme={null}
{
  "onboarding.header.title": "Welcome, {username}!",
  "onboarding.header.subtitle": "We''re happy to have you here",
  "authentication.message": "You are {role, select, admin {an administrator} viewer {a viewer} other {currently logged in}}.",
  "cart.count": "{count, plural, =0 {No items in the cart} zero {No items in the cart} =1 {One item in the cart} one {One item in the cart} other {{cartCount} items in the cart}}"
}
```

* A Map or List variable in Ditto translates to an ICU SelectFormat argument, with the variable's first value used as the "other" key.
* Single quotes (`'`) will be escaped with another single quote (i.e "You're logged in" -> "You''re logged in")
* Pluralization in Ditto translates to an ICU PluralFormat argument:
  * The PluralFormat, if present, will always encompass the rest of the string, within which variables can be nested.
  * The name of the argument will always be `count` (i.e. `{count, plural, ...}`)
  * `zero`, `one`, and `two` plural forms are each output twice: once as an exact-value selector (`=0`, `=1`, `=2`) and once as the matching CLDR category keyword (`zero`, `one`, `two`), both mapped to the same text. This is intentional, not a duplicate to clean up — some locales (e.g. English) have no `zero` category at all, so without the `=0` selector that text would never display for a literal 0. Other locales' categories cover more than the literal number (e.g. Russian's `one` category also applies to 21, 31, 101, ...), so the keyword selector is needed too.

**Instructions:**

To fetch this via the **API**, provide the query parameter `?format=json_icu`.

To fetch this via the **CLI**, specify `framework: icu` alongside `format: json` in the `config.yml` file. See [Frameworks](/cli-reference/files#framework-specific-files) for more details.

## ARB JSON

This format provides a mapping from string IDs to strings in the [ARB (Application Resource Bundle)](https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification) format, commonly used by Flutter's `intl` package for localization.

Message text uses the same [ICU message format](https://icu.unicode.org/home) syntax as ICU JSON above (variable interpolation, pluralization, and Map/List SelectFormat arguments all work identically). In addition:

* If a text item has notes, they're included as a `description` field in that item's `@`-prefixed metadata entry, to give translators context.
* If a text item has variables, they're included as a `placeholders` field in that item's `@`-prefixed metadata entry, mapping each variable name to its ARB type (`num` for number variables, `String` for all other variable types).
* If the export has a known base or variant locale, it's included as a top-level `@@locale` key.

**Example:**

```json theme={null}
{
  "@@locale": "en",
  "onboarding.header.title": "Welcome, {username}!",
  "@onboarding.header.title": {
    "placeholders": {
      "username": { "type": "String" }
    }
  },
  "cart.count": "{count, plural, =0 {No items in the cart} =1 {One item in the cart} other {{count} items in the cart}}",
  "@cart.count": {
    "description": "Shown at the top of the shopping cart page",
    "placeholders": {
      "count": { "type": "num" }
    }
  }
}
```

**Instructions:**

To fetch this via the **API**, provide the query parameter `?format=arb`.

To fetch this via the **CLI**, specify `framework: arb` alongside `format: json` in the `config.yml` file. See [Frameworks](/cli-reference/files#framework-specific-files) for more details.

<Warning>
  `@@locale` is the **only** thing identifying an ARB file's language. Ditto file names follow the [standard `{project_id}___{variant_id}` convention](/cli-reference/files#string-file-naming-conventions) (e.g. `my-project___base.arb`), not Flutter's own `app_<locale>.arb` convention — so a file without `@@locale` carries no language information at all, and tools like `intl_utils`/`gen-l10n` won't be able to place it correctly.

  For a base (non-variant) export, `@@locale` comes from the project's base locale — set under **Variant & locale settings** in the app. This is unset (`null`) by default, so `@@locale` won't appear until it's configured there.

  For a variant export, `@@locale` comes from that variant's own locale code, set the same way. Configure these before relying on the `arb` format or framework.
</Warning>

## Variant-specific exports

For each of these JSON formats, you can choose to fetch the text of a single variant.

For flat, structured, nested, and ICU JSON, variant-specific exports also include two metadata keys for the variant:

```json theme={null}
{
  "__variant-name": "French",
  "__variant-description": "Copy localized for native French speakers",
  ...
}
```

<Info>ARB exports don't include `__variant-name`/`__variant-description`. Unlike the other formats, ARB is consumed directly by third-party tooling (e.g. Flutter's `intl_utils`/`gen-l10n`), which treats any non-`@`-prefixed top-level key as a translatable message — so these keys would leak into a generated app as fake strings. Use the variant's locale, included as `@@locale`, to identify an ARB export's variant instead.</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.