fix(i18n): eager-load nl bundle, fix language selector, remove hardcoded backend messages
- Eager-load both en and nl translation bundles at i18n init to eliminate
the async gap that caused English flash when Dutch was the detected language
- LanguageSwitcher: use i18n.language (synchronous) instead of resolvedLanguage
(asynchronous) so the visual selection is always correct after switching
- AvailabilityController: remove hardcoded English messages ("System is running
normally.") from GET /availability/status; return empty string so the frontend
translations control the display text
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,13 @@ import {
|
||||
/** Language selector for the topbar; lazy-loads the chosen locale (Q4-B). */
|
||||
export function LanguageSwitcher() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const current = (i18n.resolvedLanguage ?? 'en') as SupportedLanguage;
|
||||
// Use i18n.language (set synchronously on change) rather than resolvedLanguage
|
||||
// (which can lag when bundles were lazy-loaded). Split on '-' to normalise
|
||||
// region variants like 'nl-NL' → 'nl'.
|
||||
const rawLang = i18n.language?.split('-')[0] ?? 'en';
|
||||
const current = (SUPPORTED_LANGUAGES.includes(rawLang as SupportedLanguage)
|
||||
? rawLang
|
||||
: 'en') as SupportedLanguage;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
|
||||
@@ -2,6 +2,7 @@ import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import enTranslation from './locales/en/translation.json';
|
||||
import nlTranslation from './locales/nl/translation.json';
|
||||
|
||||
export const SUPPORTED_LANGUAGES = ['en', 'nl'] as const;
|
||||
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
||||
@@ -11,25 +12,15 @@ export const LANGUAGE_LABELS: Record<SupportedLanguage, string> = {
|
||||
nl: 'Nederlands',
|
||||
};
|
||||
|
||||
// English (the fallback) is bundled eagerly so the UI never flashes raw keys.
|
||||
// Other locales are lazy-loaded on demand (Q4-B / NFR-U1-05).
|
||||
const loaded = new Set<string>(['en']);
|
||||
|
||||
async function loadLocale(lng: string): Promise<void> {
|
||||
if (loaded.has(lng) || !SUPPORTED_LANGUAGES.includes(lng as SupportedLanguage)) {
|
||||
return;
|
||||
}
|
||||
const module = await import(`./locales/${lng}/translation.json`);
|
||||
i18n.addResourceBundle(lng, 'translation', module.default, true, true);
|
||||
loaded.add(lng);
|
||||
}
|
||||
|
||||
// Both supported locales are bundled eagerly so the detected language is
|
||||
// available immediately on init — no async gap that would cause an English flash.
|
||||
void i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources: {
|
||||
en: { translation: enTranslation },
|
||||
nl: { translation: nlTranslation },
|
||||
},
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: SUPPORTED_LANGUAGES as unknown as string[],
|
||||
@@ -42,12 +33,8 @@ void i18n
|
||||
react: { useSuspense: false },
|
||||
});
|
||||
|
||||
// Ensure the detected language is loaded after init.
|
||||
void loadLocale(i18n.resolvedLanguage ?? 'en');
|
||||
|
||||
/** Lazy-load the target locale, then switch to it (persisted by the detector). */
|
||||
/** Switch to a supported locale and persist the choice via the detector cache. */
|
||||
export async function changeLanguage(lng: SupportedLanguage): Promise<void> {
|
||||
await loadLocale(lng);
|
||||
await i18n.changeLanguage(lng);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user