-
-
Notifications
You must be signed in to change notification settings - Fork 0
Internationalisation
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.
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:
- Updates the
<html lang>attribute immediately. - Writes the choice to
localStorage(cache for next load). - 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).
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.
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. |
The table below tracks every namespace currently covered in de, fr, es, and zh. Screens not
listed still render in English only; follow Adding translations to a screen to add one.
| Namespace | Screen / area | Introduced |
|---|---|---|
nav |
Sidebar labels and section headers (AppShell.tsx) |
#289 |
users |
Users admin screen | #290 |
access |
Access Control admin screen | #290 |
warehouseaccess |
Warehouse Access admin screen | #290 |
admindb |
Database Console admin screen | #290 |
systeminfo |
System Info admin screen | #290 |
dashboard |
Dashboard home screen (Dashboard.tsx) |
#291 |
help |
In-app help drawer chrome + per-screen content (HelpButton.tsx) |
#291 |
process |
BPMN process designer (ProcessDesigner.tsx) |
#291 |
reporting |
Reporting screens — Material flow, ASRS, Stock, Inbound, Outbound + shared charts | #291 |
inbound |
Inbound orders screen (InboundScreen.tsx) |
#292 |
outbound |
Outbound orders screen (OutboundScreen.tsx) |
#292 |
counting |
Stock counting / cycle-count screen (CountingScreen.tsx) |
#292 |
stocktxn |
Stock transactions screen (StockTxnScreen.tsx) |
#292 |
inventory |
Stock overview + Handling units screens (StockOverviewScreen.tsx, HandlingUnitsScreen.tsx) |
#292 |
gtpops |
GTP operator console — launcher, queue/exceptions drawers, count/pick/put panels (GtpOpsScreen.tsx) |
#292 |
gtpconfig |
GTP configuration — workplace/node CRUD dialogs, location picker (GtpConfigScreen.tsx, LocationPicker.tsx) |
#292 |
transport |
Transport overview — tiles, filters, task table, trace dialog (TransportScreen.tsx) |
#292 |
twin |
Hardware twin — page chrome, stat chips, legend, level select, detail panels (HardwareTwinScreen.tsx) |
#292 |
masterdata |
Master data catalogs — warehouses, SKUs, storage blocks, locations, equipment, HU types, label templates + guided block builder (MasterDataScreen.tsx); 297 keys |
#293 |
settings |
Settings — slotting policy, cubing, counting, stock rules, integrations, system status, demo mode, hardware emulator (SettingsScreen.tsx); 196 keys |
#293 |
slotting |
Slotting — pick faces + block slotting screen (SlottingScreen.tsx); 40 keys |
#293 |
AppShell.tsx uses useT('nav') to translate sidebar items. The keys are the screen's stable
key property (e.g. inbound, gtp-ops). Section header keys use the prefix section: followed
by the English section name (e.g. section:Master data).
All 32 screen labels and 6 section headers are covered in locales/<lang>/nav.ts for de, fr,
es, and zh. When you add a new screen to SCREENS, also add its key to each nav.ts file;
until you do, the English label is shown as a fallback.
// src/i18n/locales/de/nav.ts (excerpt)
export default {
inbound: 'Wareneingangsaufträge',
'section:Master data': 'Stammdaten',
// ...
}The help namespace is shared across all screens and uses compound keys rather than the single-level keys used by other namespaces.
Drawer chrome keys (always translated):
| Key | English default |
|---|---|
help |
Help |
helpFor |
Help for {screen} |
closeHelp |
Close help |
tips |
Tips |
Per-screen content keys (the English source stays in help/content.ts; HelpButton.tsx calls t(key, english) so untranslated screens degrade to English automatically):
| Key pattern | Example |
|---|---|
{screenKey}.summary |
dashboard.summary |
{screenKey}.s{i}.heading |
dashboard.s0.heading |
{screenKey}.s{i}.body |
dashboard.s0.body |
{screenKey}.tip{i} |
dashboard.tip0 |
Section headings (s{i}.heading) are translated for all 33 screens. Full body copy is translated for the priority screens (dashboard, processes, and all five reporting entries). The remaining screens' summaries, bodies, and tips fall back to English automatically.
// src/i18n/locales/de/help.ts (excerpt)
export default {
"help": "Hilfe",
"closeHelp": "Hilfe schließen",
"dashboard.s0.heading": "Was Sie hier tun",
"dashboard.summary": "...", // priority screen — body copy translated
"transport.s0.heading": "Was Sie hier tun",
// transport summary/body omitted → falls back to English
}-
Import and call
useTwith 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. -
Add locale files for each of
de,fr,es,zh:// src/i18n/locales/de/inbound.ts export default { title: 'Wareneingangsaufträge', }
dictionaries.tspicks 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.
- 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.).
- Service health status codes (
UP/DOWN/UNKNOWN) — these are API data values, not UI copy. - The screen-permission catalog labels in the Access Control matrix — they mirror the nav catalog and are intentionally kept in sync with the English screen keys rather than translated separately.
openWCS — open-source Warehouse Control System · summarized from build.md & docs/AS-BUILT.md (the repo docs are authoritative).
Design
Flows
- Areas
- Inbound and Inventory
- Slotting and Replenishment
- Goods-to-Person Stations
- Outbound Flow
- Equipment Integration
- Transport Overview
- Process Designer
- Mobile Process Designer
- Hardware Visualisation
- Host Integration
Reporting & Dashboards
Operations