(frontend) the LanguagePicker now uses config as options

- config endpoint languages are used as available options for LanguagePicker
- updating the language from it, triggers an update on the user via API
This commit is contained in:
rvveber
2025-03-04 16:08:39 +01:00
committed by Samuel Paccoud
parent 2bf47b7705
commit 7fc83a4fcd
5 changed files with 52 additions and 31 deletions

View File

@@ -12,8 +12,8 @@ const config = {
MEDIA_BASE_URL: 'http://localhost:8083',
LANGUAGES: [
['en-us', 'English'],
['fr-fr', 'French'],
['de-de', 'German'],
['fr-fr', 'Français'],
['de-de', 'Deutsch'],
],
LANGUAGE_CODE: 'en-us',
POSTHOG_KEY: {},

View File

@@ -4,25 +4,45 @@ import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { DropdownMenu, Text } from '@/components/';
import { LANGUAGES_ALLOWED } from '@/i18n/conf';
import { useConfig } from '@/core';
import { useLanguageSynchronizer } from './hooks/useLanguageSynchronizer';
import { getMatchingLocales } from './utils/locale';
export const LanguagePicker = () => {
const { t, i18n } = useTranslation();
const { preload: languages } = i18n.options;
const language = i18n.language;
const { data: conf } = useConfig();
const { synchronizeLanguage } = useLanguageSynchronizer();
const language = i18n.languages[0];
Settings.defaultLocale = language;
// Compute options for dropdown
const optionsPicker = useMemo(() => {
return (languages || []).map((lang) => ({
label: LANGUAGES_ALLOWED[lang],
isSelected: language === lang,
callback: () => {
i18n.changeLanguage(lang).catch((err) => {
console.error('Error changing language', err);
});
},
}));
}, [i18n, language, languages]);
const backendOptions = conf?.LANGUAGES ?? [[language, language]];
return backendOptions.map(([backendLocale, label]) => {
// Determine if the option is selected
const isSelected =
getMatchingLocales([backendLocale], [language]).length > 0;
// Define callback for updating both frontend and backend languages
const callback = () => {
i18n
.changeLanguage(backendLocale)
.then(() => {
void synchronizeLanguage('toBackend');
})
.catch((err) => {
console.error('Error changing language', err);
});
};
return { label, isSelected, callback };
});
}, [conf, i18n, language, synchronizeLanguage]);
// Extract current language label for display
const currentLanguageLabel =
conf?.LANGUAGES.find(
([code]) => getMatchingLocales([code], [language]).length > 0,
)?.[1] || language;
return (
<DropdownMenu
@@ -54,7 +74,7 @@ export const LanguagePicker = () => {
<Text $isMaterialIcon $color="inherit" $size="xl">
translate
</Text>
{LANGUAGES_ALLOWED[language]}
{currentLanguageLabel}
</Text>
</DropdownMenu>
);

View File

@@ -1,7 +0,0 @@
export const LANGUAGES_ALLOWED: { [key: string]: string } = {
en: 'English',
fr: 'Français',
de: 'Deutsch',
};
export const LANGUAGE_COOKIE_NAME = 'docs_language';
export const BASE_LANGUAGE = 'en';

View File

@@ -1,27 +1,34 @@
import i18n from 'i18next';
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import { BASE_LANGUAGE, LANGUAGES_ALLOWED, LANGUAGE_COOKIE_NAME } from './conf';
import resources from './translations.json';
i18n
export const availableFrontendLanguages: readonly string[] =
Object.keys(resources);
i18next
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: BASE_LANGUAGE,
supportedLngs: Object.keys(LANGUAGES_ALLOWED),
fallbackLng: 'en',
debug: false,
detection: {
order: ['cookie', 'navigator'], // detection order
caches: ['cookie'], // Use cookies to store the language preference
lookupCookie: LANGUAGE_COOKIE_NAME,
lookupCookie: 'docs_language',
cookieMinutes: 525600, // Expires after one year
cookieOptions: {
path: '/',
sameSite: 'lax',
},
},
interpolation: {
escapeValue: false,
},
preload: Object.keys(LANGUAGES_ALLOWED),
preload: availableFrontendLanguages,
lowerCaseLng: true,
nsSeparator: false,
keySeparator: false,
})
@@ -29,4 +36,4 @@ i18n
throw new Error('i18n initialization failed');
});
export default i18n;
export default i18next;

View File

@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import { AppProvider } from '@/core/';
import { useSWRegister } from '@/features/service-worker/';
import '@/i18n/initI18n';
import { NextPageWithLayout } from '@/types/next';
import './globals.css';