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). */
|
/** Language selector for the topbar; lazy-loads the chosen locale (Q4-B). */
|
||||||
export function LanguageSwitcher() {
|
export function LanguageSwitcher() {
|
||||||
const { t, i18n } = useTranslation();
|
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 (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import i18n from 'i18next';
|
|||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import enTranslation from './locales/en/translation.json';
|
import enTranslation from './locales/en/translation.json';
|
||||||
|
import nlTranslation from './locales/nl/translation.json';
|
||||||
|
|
||||||
export const SUPPORTED_LANGUAGES = ['en', 'nl'] as const;
|
export const SUPPORTED_LANGUAGES = ['en', 'nl'] as const;
|
||||||
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
||||||
@@ -11,25 +12,15 @@ export const LANGUAGE_LABELS: Record<SupportedLanguage, string> = {
|
|||||||
nl: 'Nederlands',
|
nl: 'Nederlands',
|
||||||
};
|
};
|
||||||
|
|
||||||
// English (the fallback) is bundled eagerly so the UI never flashes raw keys.
|
// Both supported locales are bundled eagerly so the detected language is
|
||||||
// Other locales are lazy-loaded on demand (Q4-B / NFR-U1-05).
|
// available immediately on init — no async gap that would cause an English flash.
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void i18n
|
void i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources: {
|
resources: {
|
||||||
en: { translation: enTranslation },
|
en: { translation: enTranslation },
|
||||||
|
nl: { translation: nlTranslation },
|
||||||
},
|
},
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
supportedLngs: SUPPORTED_LANGUAGES as unknown as string[],
|
supportedLngs: SUPPORTED_LANGUAGES as unknown as string[],
|
||||||
@@ -42,12 +33,8 @@ void i18n
|
|||||||
react: { useSuspense: false },
|
react: { useSuspense: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure the detected language is loaded after init.
|
/** Switch to a supported locale and persist the choice via the detector cache. */
|
||||||
void loadLocale(i18n.resolvedLanguage ?? 'en');
|
|
||||||
|
|
||||||
/** Lazy-load the target locale, then switch to it (persisted by the detector). */
|
|
||||||
export async function changeLanguage(lng: SupportedLanguage): Promise<void> {
|
export async function changeLanguage(lng: SupportedLanguage): Promise<void> {
|
||||||
await loadLocale(lng);
|
|
||||||
await i18n.changeLanguage(lng);
|
await i18n.changeLanguage(lng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ public class AvailabilityController : ControllerBase
|
|||||||
{
|
{
|
||||||
Status = status.ToString(),
|
Status = status.ToString(),
|
||||||
CheckedAt = DateTimeOffset.UtcNow,
|
CheckedAt = DateTimeOffset.UtcNow,
|
||||||
Message = status == AvailabilityStatus.Available
|
Message = string.Empty,
|
||||||
? "System is running normally."
|
|
||||||
: "System is in maintenance or unavailable."
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user