---
title: "i18n"
description: "How Clover 4 speaks its reader's language, the languages it ships, and how to add one."
url: https://clover.iiif.library.northwestern.edu/docs/i18n
version: "4"
---

# i18n

Clover translates its own words: the names of its controls, its messages, and what it announces. IIIF text (a label, a summary, metadata) is the resource’s own, in the languages it has, and Clover chooses among them as [Composing layouts](https://clover.iiif.library.northwestern.edu/docs/composing#languages) describes.

Clover has its own translator, well under 1 kB gzipped, with no translation library to install. Its strings are kept the way i18next keeps them, so `initCloverI18n` calls from Clover 3.x keep working.

## Languages Clover ships

Each is complete: it has every string English has, which a test checks.

| Language          | Tag  | Notes                                                       |
| ----------------- | ---- | ----------------------------------------------------------- |
| English           | `en` | Comes with Clover. Every other language falls back to it.   |
| Spanish           | `es` |                                                             |
| Finnish           | `fi` |                                                             |
| French            | `fr` |                                                             |
| Norwegian Bokmål  | `nb` |                                                             |
| Norwegian Nynorsk | `nn` |                                                             |
| Norwegian         | `no` | The same strings as `nb`.                                   |
| Portuguese        | `pt` | European Portuguese, which Brazilian (`pt-BR`) readers get. |

Pick one to hear the Viewer in it. This page is in English, so each of Clover’s words in another language carries that language’s `lang`.

> **Example**: a live example on the page, showing sample IIIF data. What it shows is demonstration content, not about Clover; its code, where the page gives it, follows.

## How Clover chooses

Clover speaks the language of the page around it. To find it, Clover looks in this order:

1. First, it checks for a language you set yourself, with `initCloverI18n({ lng })`.
2. Then, it reads the page it is on, for the language in its `<html lang>`.
3. Then, it looks at the reader’s browser, and takes the first of their languages that Clover ships.
4. Last, for any string the language it chose does not have, it uses the `fallbackLng` you give, and then English.

A regional tag tries its base language too, so `pt-BR` falls back to `pt`.

## Setting a language

Call `initCloverI18n` near your app’s entry point, before Clover draws:

**React**

```jsx
import { initCloverI18n } from "@nulib/clover";
 
initCloverI18n({ lng: "fr" });
```

**Web components**

```html
<script type="module">
  import { initCloverI18n } from "@nulib/clover/web-components";
 
  initCloverI18n({ lng: "fr" });
</script>
```

| Option        | Type                                       | Default                                      |
| ------------- | ------------------------------------------ | -------------------------------------------- |
| `lng`         | `string`                                   | The page’s `<html lang>`, then the browser’s |
| `fallbackLng` | `string`, `string[]` or `false`            | None; English is always last                 |
| `resources`   | `{ [lng]: { clover: { [key]: string } } }` | None                                         |

It can be called again: each call merges its `resources` and applies any `lng` or `fallbackLng` it is given, and every Clover part on the page changes language at once. The web components take the same call; see [Languages](https://clover.iiif.library.northwestern.edu/docs/technical#languages) in the technical overview.

## A language Clover does not ship

Give its strings as `resources`, under the `clover` namespace:

**React**

```jsx
import { initCloverI18n } from "@nulib/clover";
 
initCloverI18n({
  lng: "de",
  resources: {
    de: {
      clover: {
        commonClose: "Schließen",
        informationPanelTabs: "Informationsbereich",
      },
    },
  },
});
```

**Web components**

```html
<script type="module">
  import { initCloverI18n } from "@nulib/clover/web-components";
 
  initCloverI18n({
    lng: "de",
    resources: {
      de: {
        clover: {
          commonClose: "Schließen",
          informationPanelTabs: "Informationsbereich",
        },
      },
    },
  });
</script>
```

Any string you leave out falls back to English, marked `lang="en"`. The full list of keys, with their English, is [`en.json`](https://github.com/nulib-ds/clover/blob/main/packages/clover/src/i18n/locales/en.json). A string with `{{name}}` in it is filled in by Clover: keep each placeholder as it is.

## Loading

English comes with Clover. Each other language is its own file, loaded the first time Clover is to speak it, so a page in English never fetches one. Until it arrives, Clover’s words are shown in English, marked `lang="en"`, and they change when it does. A page rendered on the server, or a test, that needs them in place first waits for them:

```js
await initCloverI18n({ lng: "fr" }).ready();
```

## Marking languages

Where one of Clover’s strings is not in the page’s language (a French reader on an English page, or a string that fell back to English), the element that shows it, or is named by it, carries its language as `lang`, so a screen reader pronounces it correctly (WCAG 3.1.2). The page’s language is the nearest `lang` around the `Root` (web components: `<clover-root>`), or one you give the `Root` (web components: `<clover-root>`). A part of your own does the same with `useT` and `useLangOf`, as [Languages](https://clover.iiif.library.northwestern.edu/docs/composing#languages) in Composing layouts shows.

Text in a language written right to left (Arabic, Hebrew, Persian, Urdu…) carries `dir="rtl"` beside its `lang`, so its words run and its punctuation sits as they are written on a page written either way: Clover’s own strings in such a language, and the resource’s labels, summaries, metadata and annotations alike. Text in a language written left to right takes the page’s direction. Fields are the exception: a search field’s direction is the reader’s typing, not its placeholder’s.

## Adding a language to Clover

A language added to Clover itself ships to everyone. In [nulib-ds/clover](https://github.com/nulib-ds/clover):

1. Add `packages/clover/src/i18n/locales/<tag>.json`, with every key `en.json` has, translated. Keep each `{{name}}` placeholder as it is.
2. Add its loader to `loaders` in `packages/clover/src/i18n/locales/index.ts`, so it loads only when it is needed.
3. Run `npm test`. `locales.test.ts` checks that the file has every key English has and no other, and that each placeholder is kept. A word your language spells as English does (French “Collection”) goes on that test’s `SAME_AS_ENGLISH` list for your language.
4. Add a line under `## Unreleased` in `packages/clover/CHANGELOG.md`, and open a pull request.

packages/clover/src/i18n/locales/de.json

```json
{
  "commonClose": "Schließen",
  "commonNext": "Weiter",
  "commonPrevious": "Zurück"
}
```

packages/clover/src/i18n/locales/index.ts

```ts
export const loaders: Record<string, LocaleLoader> = {
  de: () => import("./de.json"),
  es: () => import("./es.json"),
  // …
};
```

For a regional variant, name the file by its full tag (`fr-CA.json`) and give its loader the same key (`"fr-CA"`); readers of that region get it, and others the base language.

A new key in `en.json` is added to every locale, translated, in the same change, and the test fails until it is. If you speak one of the languages Clover ships, a review of its strings is welcome, as an issue or a pull request.
