Rebrands the product from MobilityOps to Fleet Ops across the UI, backend defaults and knowledge base, and makes nl-BE/en-GB/fr-BE full first-class languages: i18next with eager-bundled per-namespace resources, a persisted accessible language switcher (topbar and mobile drawer), locale-aware date/number formatting, and a coverage test that fails the build on any missing or empty translation key. Backend dynamic content (demo scenarios, blocked-reason text, integration status) moves from fixed English/Dutch prose to stable message codes + params so the frontend can localize it; the demo knowledge base gains a fully translated NL/EN/FR procedure corpus (11 documents each) with per-language retrieval and localized evidence-state messages. The Demo Guide becomes breakpoint-adaptive: a docked rail on extra-wide desktop, a floating panel that auto-collapses to a persistent, closable progress chip on standard desktop/tablet, and a collapsed/half/full bottom sheet on mobile -- with scroll+focus+ highlight on "go to this step", Escape handling, and reduced-motion support. The Data Quality Workbench gets accessible choice-card decisions with a clear primary/ secondary/tertiary action hierarchy; the Automation ledger groups repeated successes and uses meaningful short refs; the Audit trail groups events by correlation id with human action labels and readable before/after diffs. Attention Queue, Today's movements, Vehicles, Bookings and Data Quality rows are fully clickable (stretched-link pattern) with independent secondary links, keyboard support and mobile touch targets. Fixes a topbar overflow on mobile caused by the new language switcher (moved into the mobile drawer at <=960px) and two dangling aria-labelledby references introduced this session. Updates all affected Playwright specs for the new nl-BE default and the new Audit/DemoGuide DOM structure, and adds new i18n-coverage, demo-guide-adaptive and clickable-rows specs. 131 backend tests, Ruff and mypy, and 71 Playwright tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
34 lines
998 B
TypeScript
34 lines
998 B
TypeScript
import { useTranslation } from "react-i18next";
|
|
import {
|
|
persistLanguage,
|
|
SUPPORTED_LANGUAGES,
|
|
type SupportedLanguage,
|
|
} from "../i18n/config";
|
|
|
|
export function LanguageSwitcher({ compact = false }: { compact?: boolean }) {
|
|
const { t, i18n } = useTranslation("common");
|
|
const current = (i18n.language as SupportedLanguage) || "nl-BE";
|
|
|
|
function handleChange(next: SupportedLanguage) {
|
|
void i18n.changeLanguage(next);
|
|
persistLanguage(next);
|
|
}
|
|
|
|
return (
|
|
<label className={`language-switcher ${compact ? "language-switcher-compact" : ""}`}>
|
|
<span className="visually-hidden">{t("language.label")}</span>
|
|
<select
|
|
value={current}
|
|
onChange={(event) => handleChange(event.target.value as SupportedLanguage)}
|
|
aria-label={t("language.label")}
|
|
>
|
|
{SUPPORTED_LANGUAGES.map((lang) => (
|
|
<option key={lang} value={lang}>
|
|
{t(`language.${lang}`)}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
);
|
|
}
|