# Modify translations in bulk

`PUT /api/translations`

- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Modify translations in bulk](https://support.gladstonesoftware.cloud/reference/configuration/modify-translations-in-bulk)

## Overview

Sets the translations for one or more records in a single call. Each element of the array targets
one record — identified by its translation type and its `Id` — and the array of translations you
send **replaces everything currently held for that record**.

**This is a replace, not a merge, and that is the thing to get right.** For each record you name,
every translation it currently holds is compared with what you sent,
matching on culture (and language, where you use it):

- a culture you send that already exists has its text overwritten;
- a culture you send that does not exist is inserted;
- **a culture that exists but is absent from your array is deleted.**

So sending a single Welsh translation for a record that also had French removes the French. Always
read the record's current translations first — most configuration endpoints return them inline, and
[Export a file containing translations](export-a-file-containing-translations.md)
returns them in bulk — then send the full set back with your edits applied. Records you do not
mention at all are untouched: the blast radius is exactly the type and `Id` pairs in your request.

**The call is not atomic.** Each element is validated up front, but saved one at a
time. An invalid translation type is only detected while saving, so a `404` can arrive after
earlier elements have already been applied, and those are not undone. Send small batches, and on
failure re-read rather than assuming nothing happened.

A success returns `204` with no body — nothing tells you how many translations were added, changed or
removed.

Valid values for `type` come from [Get all translation types](get-all-translation-types.md), and valid
values for `culture` and `language` from [Get all available cultures](get-all-available-cultures.md).

## Endpoint

`PUT {base_url}/api/translations`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `[].Id` | string | Yes | The id of the record being translated — a product code, a tax code, a site id and so on, depending on `type`. Note the capital `I`: the field is `Id`, not `id`. Maximum 50 characters; omitting it fails with `Id must be specified`. It is not checked against existing records, so a typo does not error — it silently creates translations against a record that does not exist. |
| `[].type` | string | Yes | The kind of record `Id` refers to, such as `Product` or `TaxCode`. Take it from [Get all translation types](get-all-translation-types.md); matching is case-insensitive. Maximum 50 characters, and required — omitting it fails with `Type must be specified`. A value that is not a known type fails the whole element with `404` and `One or more invalid translation type(s) "{TYPE}"`, but only once that element is reached, so earlier elements in the same request are already saved. |
| `[].translations` | array of object | Yes | The complete set of translations this record should have afterwards. Required — omitting it fails with `One or more Translations must be specified`. Anything the record currently holds and not present here is deleted, so an empty array removes every translation the record has. |
| `[].translations[].culture` | string | Yes | The culture this text is for, using a code from [Get all available cultures](get-all-available-cultures.md). Required, maximum 20 characters. This is the key used to decide between update, insert and delete. A code that is not in the cultures list fails the request with `404` and `One or more invalid cultures specified for {type}/{Id} - "…"`. Within one element, the combination of `culture`, `language` and `type` must be unique or the request fails with `400`. |
| `[].translations[].type` | string | Yes | The property of the record being translated — for example `description`. Required, maximum 50 characters. **It is saved but not used for matching**: existing translations are paired with your entries on culture and language alone, so a record that has two properties translated into the same culture can have its texts written to the wrong property. Where a record has more than one translatable property, send one request element per property and expect to re-read afterwards. |
| `[].translations[].text` | string | Yes | The translated text itself. Required — there is no way to blank a translation by sending an empty string; omit the culture entirely to delete it. Maximum 255 characters, and the limit is enforced on the request, so long marketing copy is rejected with `400` rather than truncated. |
| `[].translations[].language` | string | No | An optional second dimension alongside `culture`, taking a value from the same cultures list. Maximum 20 characters. Omit it, or send an empty string — which is treated as `null` — for the ordinary case. When set, it becomes part of the key: an existing translation with a language only matches a request entry with the same language, so introducing a language on an existing culture deletes the language-less translation and inserts a new one. A value not in the cultures list fails the request with `404` and `One or more invalid languages specified for {type}/{Id} - "…"`. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/translations' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '[
  {
    "Id": "string",
    "type": "string",
    "translations": [
      {
        "culture": "string",
        "type": "string",
        "text": "string",
        "language": "string"
      }
    ]
  }
]'
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | A required field is missing or too long — `Id must be specified`, `Type must be specified`, `One or more Translations must be specified`, `Culture must be specified`, `Text must be specified`, or the matching `cannot be more than N characters` message — or the same `culture`, `language` and `type` appear twice within one element, which fails with `One or more duplicate culture codes specified for {type}/{Id} - "…"`. Nothing is written. |
| `404` | Not found | Three distinct causes, all with the message as a plain string body: an unknown `culture` (`One or more invalid cultures specified for {type}/{Id} - "…"`), an unknown `language` (`One or more invalid languages specified for {type}/{Id} - "…"`), or an unknown `type` (`One or more invalid translation type(s) "{TYPE}"`). The first two are caught before anything is written; the third is not, so **it can leave earlier elements of the array already applied**. |
| `500` | Internal server error | Any unexpected server-side failure, with an empty body. As with a `404` on an unknown type, elements processed before the failure are already saved. |
