Languages and Translations

Horizon UI ships with eight locales:

Locale Native name
en English (source)
de Deutsch
es Español
fr Français
ja 日本語
ko 한국어
pt Português
zh-CN 中文(简体)

In the picker English sits on its own line at the top; the other seven are listed alphabetically by code so additions slot in predictably.

English is the immovable default. Every translatable string is authored in English first; the other locales are catalog overlays. Missing keys fall back to English at the leaf, never at the file — a half-translated catalog renders strictly better than English-only.

Coverage by scope

All eight languages are first-class across the surfaces Horizon controls. The scope decides where the translation resolves:

Scope Resolves Languages
UI chrome — buttons, labels, modals, login, topbar, sidebar In the browser (vue-i18n) All 8
Bundled layer-dashboard templates On the BFF, from sibling overlay catalogs All 8
Bundled overview templates On the BFF All 8
User-maintained dashboards saved to OAP On the BFF, from per-language translation overlays stored on OAP English + whatever the author provides
OAP-supplied data — service / instance / endpoint / alarm names, tags, log lines, trace ops not translated rendered verbatim

A missing key in any translated scope falls back to English at the leaf, so a partially-translated catalog renders better than English-only.

Picking a language

The locale picker lives in the topbar (next to the theme chip) and on the login page. Picks are stored in your browser’s local storage; the choice follows you across logout and back. A fresh device — one that has never set the locale picker — starts in English. The browser’s Accept-Language is not consulted; the project policy is “English by default, opt in to other locales via the picker” so a user on a non-English browser sees a known starting state and can switch explicitly.

Switching language re-renders the chrome immediately and triggers a silent re-fetch of any server-side content that ships translatable text (sidebar entries, layer dashboards, overview dashboards) so the new locale takes effect without a page reload.

What is and isn’t translated

Translated Not translated
UI chrome (buttons, labels, modals, time picker, login page) OAP-supplied data: service names, instance names, endpoint names, alarm rule names, tag values, log messages, trace span names
Bundled layer-dashboard templates (widget titles, KPI labels, group titles, tooltips) Layer keys (GENERAL, MESH, K8S_SERVICE, …)
Bundled overview templates Metric ids, MQE expressions
User-maintained templates (via per-language overlays on the Translations page) Units (ms, rpm, %) — abbreviated technical conventions
Layer alias and aliases.* (the display labels we author) OAP scope enums (Service, ServiceInstance, Endpoint, Process)

Product and project names — SkyWalking, Kubernetes, Envoy, Istio, OAP, MQE, eBPF, Zipkin, OpenTelemetry, gRPC, Apache — are kept in their original form across every locale. Same for technical abbreviations like RPM, SLA, Apdex, P50P99. Operators read these terms across docs, source, and other SkyWalking surfaces; they need to stay recognizable.

AI-assisted seeds

Initial translations for zh-CN, es, pt, ja, ko were seeded with AI assistance. Modern models handle SkyWalking’s technical vocabulary competently, but native speakers may spot phrasings that could read more naturally. Pull requests with corrections are welcome — target the matching catalog file:

  • UI chrome: apps/ui/src/i18n/locales/<locale>.json
  • Shared widget vocabulary (the “lexicon”): apps/bff/src/i18n/lexicon/<locale>.json
  • Bundled layer dashboards: apps/bff/src/bundled_templates/layers/<key>.i18n.<locale>.json
  • Bundled overview dashboards: apps/bff/src/bundled_templates/overviews/<id>.i18n.<locale>.json

Translating a custom dashboard

Translations are not embedded in the dashboard you author. The English source dashboard (layer or overview) stays single-language; each locale is a separate translation overlay stored on OAP and edited on the Translations page, one language at a time.

To translate a dashboard you’ve published:

  1. Open the Translations page, pick the template and the Target language.
  2. Fill the translated fields — the overlay mirrors the English source’s structure, so you fill only the fields you want translated. Anything you leave blank falls back to English at the leaf.
  3. Check diff & push shows a side-by-side diff of your draft against the version live on OAP and publishes the overlay. Keys with no matching field in the source are dropped — the source is the schema.

The overlay never travels inside the dashboard JSON, so a dashboard exported to another OAP arrives with its English source only; move its translations across on the Translations page if you need them there.

Import / Export

On the Translations page, Export downloads the in-use translation for the picked template and Target language — the version live on OAP, or the shipped seed — as a JSON file. Import reads such a file and loads it as a local draft for that language; review it in the preview, then Check diff & push to publish. Import never writes OAP directly.

Each language is its own file and its own row, so you export/import one language at a time. Source templates and their translations are edited on separate pages, so their import/export are separate too: a template’s export carries the English source only — move its translations across here, per language.

Adding a new locale

Adding de, fr, or any other locale is three steps:

  1. Add the locale to the Locale union and SUPPORTED_LOCALES list in both apps/ui/src/i18n/index.ts and apps/bff/src/i18n/types.ts.
  2. Drop in the catalog files:
    • apps/ui/src/i18n/locales/<locale>.json (UI chrome)
    • apps/bff/src/i18n/lexicon/<locale>.json (shared widget vocabulary)
  3. From apps/bff, run pnpm i18n:seed -- --locale <locale> to generate sibling overlays for every bundled layer / overview template. Fill the gaps that the lexicon couldn’t cover.

The validate CLI catches drift:

pnpm --filter @skywalking-horizon-ui/bff i18n:validate

It rejects catalog keys that no longer match the source template, non-string values at translatable paths, and lexicon entries that aren’t present in the source en.json lexicon registry.

When you add a new layer, generating its template’s translations is part of that workflow — see Adding a New Layer.