diff --git a/frontend/src/core/i18n.ts b/frontend/src/core/i18n.ts index 65b3f461f3..badc729497 100644 --- a/frontend/src/core/i18n.ts +++ b/frontend/src/core/i18n.ts @@ -105,7 +105,7 @@ i18n.on('languageChanged', (lng) => { document.documentElement.lang = lng; }); -function normalizeLanguageCode(languageCode: string): string { +export function normalizeLanguageCode(languageCode: string): string { // Replace underscores with hyphens to align with i18next/translation file naming const hyphenated = languageCode.replace(/_/g, '-'); const [base, ...rest] = hyphenated.split('-'); @@ -118,6 +118,21 @@ function normalizeLanguageCode(languageCode: string): string { return [base.toLowerCase(), ...normalizedParts].join('-'); } +/** + * Convert language codes to underscore format (e.g., en-GB → en_GB) + * Used for backend API communication which expects underscore format + */ +export function toUnderscoreFormat(languageCode: string): string { + return languageCode.replace(/-/g, '_'); +} + +/** + * Convert array of language codes to underscore format + */ +export function toUnderscoreLanguages(languages: string[]): string[] { + return languages.map(toUnderscoreFormat); +} + /** * Updates the supported languages list dynamically based on config * If configLanguages is null/empty, all languages remain available diff --git a/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx b/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx index e366ada89c..75f8516e9e 100644 --- a/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx +++ b/frontend/src/proprietary/components/shared/config/configSections/AdminGeneralSection.tsx @@ -11,7 +11,8 @@ import { useLoginRequired } from '@app/hooks/useLoginRequired'; import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBanner'; import { usePreferences } from '@app/contexts/PreferencesContext'; import { useUnsavedChanges } from '@app/contexts/UnsavedChangesContext'; -import { supportedLanguages } from '@app/i18n'; +import { supportedLanguages, toUnderscoreFormat, toUnderscoreLanguages } from '@app/i18n'; +import { Z_INDEX_CONFIG_MODAL } from '@app/styles/zIndex'; interface GeneralSettingsData { ui: { @@ -44,9 +45,6 @@ interface GeneralSettingsData { }; } -const toUnderscoreLanguages = (languages?: string[]) => - (languages || []).map(language => language.replace(/-/g, '_')); - export default function AdminGeneralSection() { const { t } = useTranslation(); const { loginEnabled, validateLoginEnabled } = useLoginRequired(); @@ -55,7 +53,7 @@ export default function AdminGeneralSection() { const { setIsDirty, markClean } = useUnsavedChanges(); const languageOptions = useMemo( () => Object.entries(supportedLanguages) - .map(([code, label]) => ({ value: code.replace(/-/g, '_'), label: `${label} (${code})` })) + .map(([code, label]) => ({ value: toUnderscoreFormat(code), label: `${label} (${code})` })) .sort((a, b) => a.label.localeCompare(b.label)), [] ); @@ -165,7 +163,7 @@ export default function AdminGeneralSection() { }); const selectedLanguages = useMemo( - () => toUnderscoreLanguages(settings.ui?.languages), + () => toUnderscoreLanguages(settings.ui?.languages || []), [settings.ui?.languages] ); @@ -402,7 +400,7 @@ export default function AdminGeneralSection() { searchable clearable placeholder={t('admin.settings.general.languages.placeholder', 'Select languages')} - comboboxProps={{ zIndex: 1400 }} + comboboxProps={{ zIndex: Z_INDEX_CONFIG_MODAL }} disabled={!loginEnabled} /> @@ -422,7 +420,7 @@ export default function AdminGeneralSection() { searchable clearable placeholder="en_GB" - comboboxProps={{ zIndex: 1400 }} + comboboxProps={{ zIndex: Z_INDEX_CONFIG_MODAL }} disabled={!loginEnabled} />