# Interface translations

_Category: REST API reference_

Change the words your help center's interface shows, such as the search placeholder, headings, buttons and the widget's messages, in any of your languages. You read the catalogue of interface strings with your help center's values, and write your own wording for the strings and languages you choose.

These are not article translations. An article's title and body in each language live on the article itself: write them with `PATCH /v1/articles/{articleId}` and a language map per field (see [Articles](https://developers.helpcenter.io/content/articles-api)).

## Endpoints

| Method and path | What it does | Scope |
| --- | --- | --- |
| `GET /v1/translations` | List the interface strings with your values | `content.read` |
| `POST /v1/translations` | Write values for several strings | `content.write` |
| `POST /v1/translations/{translationKey}` | Write values for one string | `content.write` |

These endpoints work on every plan, with API keys and OAuth access tokens. A Read only key on a write answers `403` with `insufficient_scope`.

## How interface strings work

- **One catalogue of keys for every help center.** Each string has a key, such as `site.search_placeholder` or `widget.buttons.send`, and a built-in default. The defaults are in English. `GET /v1/translations` lists every key.
- **Your values override the default, per language.** A German value shows only on German pages. A language you have no value for shows the default, in English.
- **A language shows only when it is enabled.** You can store values for any supported language, but readers see them only in the languages your help center has (see [Add a language to your help center](https://self.helpcenter.io/content/add-a-language)).
- **Texts set in your design win.** Where your help center's design sets its own text, such as a search field with a placeholder of its own, readers see that text instead of the interface string. Change those in the template editor (see [Translate texts in your design](https://self.helpcenter.io/content/translate-design-texts)).
- **The widget uses its own English.** For English, the widget always shows its built-in texts, so an English value for a `widget.*` key is stored but never shown. The write succeeds with a warning.
- **There is no publish step.** Pages usually show a change on their next load, but a page served from a cache can show the previous wording for a while.

The same strings are on the dashboard's **Localization** page (see [Translate buttons and labels](https://self.helpcenter.io/content/interface-translations)).

## The translation object

| Field | Type | Description |
| --- | --- | --- |
| `key` | string | The string's key. |
| `label` | string or null | Its name on the **Localization** page. |
| `description` | string or null | Where the string appears. |
| `default` | string or null | The built-in English text. |
| `group` | string or null | The group on the **Localization** page. See the table below. |
| `catalogued` | boolean | `false` for a value your help center has stored for a key that is no longer in the catalogue. Nothing shows it; you can read and clear it. |
| `text` | object | Your values, by language code: `{"de": "Wie können wir helfen?"}`. `{}` when you have none. |

For a key that is not in the catalogue, `label`, `description`, `default` and `group` are `null`. The groups:

| `group` | On the Localization page | For example |
| --- | --- | --- |
| `null` | **General** | `site.header.search.placeholder`, `site.homepage.categories-heading` |
| `article` | **Article** | `site.article.feedback.submit` |
| `category` | **Category** | `site.articles_count` |
| `homepage` | **Homepage** | `site.recent_articles` |
| `search` | **Search** | `site.search_placeholder`, `site.search_empty` |
| `contacts` | **Contact** | `site.contact_title` |
| `navigation` | **Navigation & footer** | `site.browse_content` |
| `errors` | **Error pages** | `site.not_found_title` |
| `accessibility` | **Accessibility** | `site.skip_to_content` |
| `widget` | **Widget** | `widget.buttons.send` |

## List interface strings

Get every string in the catalogue, in the order of the **Localization** page, with your values.

GET`/v1/translations`

List the interface strings with this help center's overrides.

| Parameter | Type | Description |
| --- | --- | --- |
| `lang` | string | Keep only this language in each `text`. Any supported language code, enabled or not. An unsupported code answers `422` with `Unsupported language code.` |
| `group` | string | Only this group, matched exactly. There is no way to ask for the `null` group alone. |
| `only_overridden` | boolean | `1` returns only strings that have a value (in `lang`, when you send it). Send `1` or `0`: `true` answers `422` with `The only overridden field must be true or false.` |

To see what you have changed in German:

```
curl -sS "https://api.helpcenter.io/v1/translations?only_overridden=1&lang=de" \
  -H "apikey: $HELPCENTER_API_KEY" \
  -H "Accept: application/json"
```

The response lists only the strings with a German value, and `meta` tells you the help center's languages:

```
{
  "status": "success",
  "translations": [
    {
      "key": "site.search_placeholder",
      "label": "Search Field Placeholder",
      "description": "The placeholder text inside search fields that have not been given a custom placeholder.",
      "default": "How can we help?",
      "group": "search",
      "catalogued": true,
      "text": {
        "de": "Wie können wir helfen?"
      }
    },
    {
      "key": "site.skip_to_content",
      "label": "Skip to Content Link",
      "description": "The keyboard link that jumps past the header straight to the content. Visible only while focused.",
      "default": "Skip to content",
      "group": "accessibility",
      "catalogued": true,
      "text": {
        "de": "Zum Inhalt springen"
      }
    }
  ],
  "meta": {
    "items_count": 3,
    "overridden_count": 3,
    "default_language": "en",
    "site_languages": [
      "en",
      "de"
    ]
  }
}
```

Two of the three strings are shown. `meta` has `items_count` (strings returned), `overridden_count` (of those, how many have a value), `default_language` and `site_languages` (the default language first, then the others enabled on the help center). Without `group`, the list ends with any stored values for keys that are no longer in the catalogue, marked `"catalogued": false`.

## Write several strings

Send `translations`: an object whose keys are string keys and whose values are language maps. Each language you send is merged into what is stored; languages you leave out stay as they are.

POST`/v1/translations`

Write overrides for several strings in one request.

- Up to 250 keys per request. Each language map needs at least one language, and each value can be up to 2,000 characters.
- `null` or an empty string removes your value for that language, so readers see the default again. When a string's last language is removed, the string no longer counts as overridden.
- Every key must be in the catalogue, or already have a stored value in your help center. Otherwise the answer is `422` with `Unknown interface-string key "site.categories_heading". Writable keys are the ones GET /v1/translations lists.`
- One invalid entry fails the whole request with `422`, and nothing is written.
- Values are trimmed, so a value of only spaces removes the language too.

The response lists every key you sent, with its values after the write, and `meta`:

| Field | Description |
| --- | --- |
| `items_count` | How many keys you sent. |
| `keys_written` | How many keys were created or changed. A write that changes nothing does not count. |
| `keys_removed` | How many keys lost their last value. |
| `warnings` | Values that were stored but will not show. Read them. |

The two warnings you can get: an English value for a `widget.*` key, which the widget never shows, and a language your help center has not enabled. Writing `{"widget.buttons.send": {"en": "Send", "fr": "Envoyer"}}` on a help center without French answers with this `meta`:

```
{
  "items_count": 1,
  "keys_written": 1,
  "keys_removed": 0,
  "warnings": [
    "The widget does not read English overrides — it uses its built-in English copy — so the \"en\" value stored for widget.buttons.send will not render.",
    "Stored, but not rendered yet: fr is not an enabled language on this help center."
  ]
}
```

## Write one string

Send `text`, the language map for the key in the path. The rules are the same as for several strings.

POST`/v1/translations/{translationKey}`

Write overrides for one string.

A key that is not in the catalogue, and has no stored value, answers `404` with the `Unknown interface-string key` message. A body that is not a language map answers `422`, for example `text must be an object keyed by language code.` for `{"text": "Suchen"}`. To go back to the default text in one language, send `null` for it:

```
curl -sS -X POST "https://api.helpcenter.io/v1/translations/site.search_placeholder" \
  -H "apikey: $HELPCENTER_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"text": {"de": null}}'
```

## Errors

| Status | Message | When |
| --- | --- | --- |
| `422` | `Unknown interface-string key "…". Writable keys are the ones GET /v1/translations lists.` | A key that is not in the catalogue, in a request for several strings. |
| `404` | The same message | A key that is not in the catalogue, in the path. |
| `422` | `Unsupported language code "pt-BR".` | A language code that HelpCenter.io does not support. Portuguese is `pt`. |
| `422` | `Provide the text as a { "locale": "string" } object with at least one locale.` | An empty or missing language map for a key. |
| `422` | `The translation must be a string, or null to remove the override.` | A value that is a number, object or list. |
| `422` | `The translation may not be longer than 2000 characters.` | A value over 2,000 characters. |
| `422` | `Send a translations object: { "key": { "locale": "text" } }.` | No `translations` in a request for several strings. |
| `422` | `Write at most 250 interface strings per call.` | More than 250 keys. |
| `403` | `This action requires the content.write scope.` | A Read only key, or a token without `content.write`, on a write. |

Validation errors come as `{"status": "validation_error", "message": "The request could not be accepted.", "errors": {…}}`, with each message under the entry it concerns, such as `translations.site.search_placeholder.pt-BR`. For the shapes every endpoint shares, see [Errors](https://developers.helpcenter.io/content/errors).

## Related

- [Requests and responses](https://developers.helpcenter.io/content/requests-responses-and-conventions)
- [Articles](https://developers.helpcenter.io/content/articles-api)
