---
title: Translations
description: Choose the language of the c15t banner and preference dialog on a
  plain HTML site, change its wording, and add languages in hosted and offline
  mode with the script tag.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Where the copy comes from

The script tag bundles English only. Other languages come from outside the
bundle, and which source wins depends on the mode:

|Mode|Where the copy comes from|
|--|--|
|Hosted, with `data-backend-url`|Your Inth project. The backend picks the language from the visitor's browser, or from `data-language`, and sends its copy with the policy.|
|Manifest, including a tag loaded from your backend's `/c15t.js`|The manifest's translations for the visitor's language.|
|Offline|English, or the language and messages you set in `i18n`.|

In every mode, your `i18n.messages` for the language in use override that copy
key by key. Text you set in `ui.banner`, such as `title` or
`acceptButtonText`, replaces the translated text for all languages.

## Pick the language

In hosted and manifest mode, c15t follows the visitor's browser language. To
force one, add `data-language` to the tag:

```html
<script
  src="https://your-project.inth.app/c15t.js"
  data-language="de"
  defer
></script>
```

A multilingual site can print each page's language into the tag, for example
from the `lang` attribute your CMS already writes on `<html>`.

To switch after load, call `c15t.setLanguage('fr')`. With a backend, c15t asks
the backend for French copy and re-renders the banner and dialog. In offline
mode, `data-language` and `c15t.setLanguage()` switch to a language that has
copy in the bundle or in `i18n.messages`. A language with no copy shows the
default copy. See [add a language in offline mode](#add-a-language-in-offline-mode).

## Change the wording

Set `i18n.messages` in a queued `config` call. You only list the keys you
change; the rest keep their default text:

```html
<script>
  window.c15t = window.c15t || [];
  c15t.push(['config', {
    i18n: {
      locale: 'en',
      messages: {
        en: {
          common: { acceptAll: 'Accept', rejectAll: 'Reject optional' },
          cookieBanner: { title: 'Cookies on this site' },
        },
      },
    },
  }]);
</script>
```

With a backend, the `/init` response or the manifest carries your Inth
project's copy for the visitor's language. Your `i18n.messages` for that
language override it key by key, and keys you leave out keep the project's
copy. c15t looks for the exact language first, then the primary language, so a
visitor on `de-AT` gets your `de` messages. Messages for other languages are
not applied.

A key only overrides the backend when your text differs from c15t's built-in
wording for that language. So passing the stock bundles from
`@c15t/translations/all` to enable languages keeps edits made in your Inth project visible,
while a key you actually reworded stays pinned in code. Core bundles only
English; for other languages, import `@c15t/translations/all` so c15t can
recognize its stock wording.

## Add a language in offline mode

Load `c15t.offline.js` without `data-backend-url`. Queue your `i18n`
configuration before that tag. Offline mode shows the language named in
`i18n.locale`, built from your messages over the English defaults:

```html
<script>
  window.c15t = window.c15t || [];
  c15t.push(['config', {
    i18n: {
      locale: 'de',
      messages: {
        de: {
          common: {
            acceptAll: 'Alle akzeptieren',
            rejectAll: 'Alle ablehnen',
            customize: 'Anpassen',
            save: 'Einstellungen speichern',
            acknowledge: 'OK',
          },
          cookieBanner: {
            title: 'Wir respektieren Ihre Privatsphäre',
            description: 'Diese Website verwendet Cookies, um Ihr Erlebnis zu verbessern.',
          },
        },
      },
    },
  }]);
</script>
```

Keys you leave out stay in English, so translate every key your banner and
dialog show. The messages follow the same structure every c15t package uses.

The script tag bundles English only, so in offline mode every other language
needs an entry in `i18n.messages`. With one entry per language,
`data-language` and `c15t.setLanguage()` switch between them. A regional
code such as `de-AT` uses the `de` entry when there is no `de-AT` entry. A
language with no entry shows the default copy.

## Message keys the stock UI reads

|Key|Where it shows|
|--|--|
|`cookieBanner.title`, `cookieBanner.description`|Banner heading and text under a choice policy.|
|`cookieBanner.noticeTitle`, `cookieBanner.noticeDescription`|Banner heading and text under a notice policy.|
|`common.acceptAll`, `common.rejectAll`, `common.customize`|Banner and dialog buttons.|
|`common.acknowledge`|The notice's dismiss button.|
|`common.save`|The dialog's save button.|
|`common.securedBy`|The "Secured by" tag.|
|`consentManagerDialog.title`, `consentManagerDialog.description`|Dialog heading and text.|
|`consentTypes.measurement.title`, `consentTypes.measurement.description` and the other categories|Each category row in the dialog.|
|`legalLinks.privacyPolicy`, `legalLinks.cookiePolicy`, `legalLinks.termsOfService`|Legal link labels, when a link has no `label` of its own.|
|`rights.optOut`, `rights.preferences`|The extra button an opt-out policy adds to the banner.|

The trigger's accessible name is not a message. Set `ui.trigger.ariaLabel`
per page instead.

## Right-to-left languages

For a right-to-left language such as Hebrew, the banner and dialog set
`dir="rtl"` and a banner at a default left or right position moves to the
other side. Check wrapping and button order after you switch.

[Copy and translations](/docs/customization/translations) covers labelling
actions honestly and testing long strings.

## Check it works

1. Set your browser's preferred language to German and open the page in a
   private window. With a backend, the banner shows your project's German
   copy.
2. Add `data-language="fr"` to the tag and reload. The banner switches to
   French.
3. Open the preference dialog. Its title, category names and buttons use the
   same language as the banner.
