Skip to content

Translating the Interface

The web interface ships in English and French. Every text shown by the interface lives in JSON files, one folder per language; the code never contains user-facing strings. Adding a language needs no code change: add a folder, and the language appears in the language picker.

Where the texts are

packages/frontend/src/locales/
  en/                    English (reference language, and fallback)
    meta.json            name of the language in the picker
    common.json          shell, navigation, sign-in, API errors (errors.*)
    steward.json       one file per page / feature (audit, cluster, nodes, windows...)
    issues-*.json        validation messages (issues.*)
  fr/                    French: same files, same keys

At start-up the interface loads every locales/<code>/*.json file and merges the files of a folder into one translation table. File names only organize the work: what matters is the key path inside the files (audit.title, issues.cluster.acmeEmailRequired...). A key must be defined in one file only.

Rules for the values:

  • {{name}} is a placeholder replaced at run time: keep it as is (you may move it in the sentence).
  • Keys ending in _one / _other are plural forms; add the forms your language needs (for example _few, _many), following the i18next plural rules.
  • Lists (JSON arrays, e.g. post-install checklists) are translated item by item.
  • Product and protocol names (Teleport, Active Directory, RDP, LDAP, ACME, tctl) are not translated.

Adding a language

  1. Copy packages/frontend/src/locales/en to packages/frontend/src/locales/<code>, where <code> is the base language tag in lower case (de, es, it, nl...).
  2. Edit <code>/meta.json: the native name of the language and its writing direction.
{ "meta": { "name": "Deutsch", "dir": "ltr" } }

Use "dir": "rtl" for right-to-left languages (Arabic, Hebrew). 3. Translate the values of every file. Do not rename, add or remove keys. 4. Run the parity test:

cd packages/frontend
npx vitest run src/i18n/locales.test.ts

It fails, with the list of keys, when your language has a key that English does not have or misses one, when meta.name is missing, or when a validation code has no English or French translation. 5. Build (npm run build) and start the application: the language is listed in the picker (top right) under its native name.

Which language is shown: the choice saved in the browser if any, otherwise the first language of the browser preferences that the application provides, otherwise English. A key missing from a language is shown in English, so a partial translation never breaks a page (the parity test still reports it).

Validation messages and API error codes

The back end and the front end share the same validators (packages/shared). Each validation issue carries a stable code, optional params and an English message:

{ "path": "cluster.acmeEmail", "severity": "error", "code": "cluster.acmeEmailRequired",
  "message": "An ACME contact e-mail address is required." }

The interface shows the translation of issues.<code> (from the issues-*.json files), with the params interpolated ({{filter}}, {{code}}...). When a code has no translation, the English message is shown. When you add a validation rule in packages/shared, give it a code with the err(path, 'area.name', 'English message', params) / warn(...) helpers and add issues.area.name to every language; the test src/i18n/locales.test.ts reads the shared sources and fails on a code that has no English or French entry.

API errors use the same idea: the server answers with a stable error code (see Error codes & Debugging) and the interface shows errors.<code> (in common.json), or a generic message when the code is unknown. Teleport connection problems (teleport_* codes) are translated under tp.* (shell.json).

What stays in English

  • Generated files: Bash and PowerShell scripts, teleport.yaml, Docker Compose files and the README files inside the downloaded bundles. They are read by administrators on servers, copied into tickets and searched for on the internet; one language keeps them consistent. Their error lines start with a stable code (see the Error codes page).
  • Logs and the audit log: back-end logs, the audit log details and script output shown in the interface are recorded as produced.
  • Free text produced by the server that has no code yet (for example the details of some verification checks) is shown as sent.
  • This documentation is written in English.