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, P50–P99. 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:
- Open the Translations page, pick the template and the Target language.
- 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.
- 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:
- Add the locale to the
Localeunion andSUPPORTED_LOCALESlist in bothapps/ui/src/i18n/index.tsandapps/bff/src/i18n/types.ts. - Drop in the catalog files:
apps/ui/src/i18n/locales/<locale>.json(UI chrome)apps/bff/src/i18n/lexicon/<locale>.json(shared widget vocabulary)
- From
apps/bff, runpnpm 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.