Skip to content

Internationalisation

openwcs-docs-agent edited this page Jun 13, 2026 · 9 revisions

Internationalisation (i18n)

The operator/management SPA supports English, German, French, Spanish, and Chinese. English is the source language and the inline fallback; backend output, server logs, and the login screen stay English regardless of the user's choice.

A user's preferred language is stored on their IAM account (AppUser.language, default en) so it follows them across devices. The browser caches it in localStorage (owcs.lang) for an instant first paint before the server round-trip completes.

Switching languages

A language picker lives in the top bar next to the warehouse switcher. Each option is labelled in its own script (Deutsch, Français, Español, 中文) so a user who cannot read the current language can still find theirs. Selecting a language:

  1. Updates the <html lang> attribute immediately.
  2. Writes the choice to localStorage (cache for next load).
  3. Persists it to the IAM account via PUT /api/iam/me/language (best-effort; a network failure still changes the language for the current session).

IAM language API

Both endpoints are resolved from the X-Auth-User header forwarded by the gateway.

Method Path Description
GET /api/iam/me/language Returns { "language": "en" } for the signed-in user. Falls back to en when the user has no row.
PUT /api/iam/me/language Body { "language": "de" }. Unknown codes are coerced to en. Auto-provisions the AppUser row if the user has never been managed in IAM, so the preference still sticks.

Supported codes: en · de · fr · es · zh.

Framework internals (src/i18n/)

The i18n layer is dependency-free (no i18n library):

File Role
config.ts Lang union type, LANGS array, DEFAULT_LANG = 'en', LANG_STORAGE_KEY.
LanguageContext.tsx React context + LanguageProvider. Reads the server preference once on mount; exposes lang + setLang.
useT.ts useT(ns) hook — returns t(key, english). When lang === 'en' or the key is absent, the inline English is returned verbatim (no raw key ever leaks).
dictionaries.ts Auto-discovers every locales/<lang>/<namespace>.ts via import.meta.glob. No central registry — parallel namespace work is conflict-free.
LanguageSwitcher.tsx Top-bar <Select> wired to LanguageContext.
locales/<lang>/<namespace>.ts One default-export { key: 'translated text' } object per language × namespace. English needs no files.

Adding translations to a screen

  1. Import and call useT with a namespace (one per screen / feature area):

    import { useT } from '../i18n/useT'
    
    const t = useT('inbound')
    return <h1>{t('title', 'Inbound orders')}</h1>

    The second argument is the English source text and the fallback — wrapping a string with t(...) never changes the English UI. Missing translations degrade to English, not a raw key.

  2. Add locale files for each of de, fr, es, zh:

    // src/i18n/locales/de/inbound.ts
    export default {
      title: 'Wareneingangsaufträge',
    }

    dictionaries.ts picks up the file automatically — no registry edit needed.

Do not wrap: data values, codes (SKU/HU/location codes), log text, or anything the backend owns. Keep numbers, units, and identifiers out of translated strings; interpolate them in the component.

What stays English

  • Backend output and API responses.
  • Server logs (all Java / Go services).
  • The Keycloak login screen (rendered outside the LanguageProvider).
  • Error codes and identifiers shown in the UI (HU codes, location codes, SKU codes, etc.).

Clone this wiki locally